@ServerEndpoint("/fxc")
public class FXCheckersWebSocketServerEndpoint {

	private final Logger logger = Logger.getLogger(this.getClass().getName());
	 
	private static final List<Session> activeSessions = Collections.synchronizedList(new ArrayList<>());
	
	private static final Queue<Session> unmatchedSessions = new ConcurrentLinkedQueue<>();
	
	@OnOpen
    public void onConnectionOpen(Session session) {
        logger.info("Connection opened ... " + session.getId());
        activeSessions.add( session );
    }
 
    @OnMessage
    public void onMessage(String message, Session session) {
    	
   		logger.info( "[ON MESSAGE] unmatchedSessions hash=" + unmatchedSessions.hashCode());
    	
    	if( !session.getUserProperties().containsKey("screenName") ) {
    		
       		Message m = new Gson().fromJson( message, Message.class );
       		
       		if( m.getType().equals( Message.TYPE_REQUEST_GAME ) ) {
       			RequestGameMessage rgm = new Gson().fromJson( message, RequestGameMessage.class );
				logger.info( "[ON MESSAGE] assigning screenName to session; " + rgm.getScreenName() );			
       			session.getUserProperties().put( "screenName", rgm.getScreenName() );
       		}
    	}
    	
    	if( unmatchedSessions.isEmpty() ) {
    		
       		logger.info( "[ON MESSAGE] no one available for a game; adding a waiter");

        	boolean changed = unmatchedSessions.add( session );
    	
       		logger.info( "[ON MESSAGE] collection changed=" + changed);

       		NotifyHostingMessage msg = new NotifyHostingMessage();
       		String json = new Gson().toJson( msg );
			try {
				session.getBasicRemote().sendObject( json );
			} catch (Exception e) {
				e.printStackTrace();
			}

    	} else {
    		
       		logger.info( "[ON MESSAGE] there is someone in the queue");

       		//
    		// There is at least one session waiting for a game
    		//
    		// Dequeue
    		//
    		Session hostSession = unmatchedSessions.remove();
    		
    		//
    		// Form message for host
    		//
    		NotifyFoundOtherPlayerMessage forHost = 
    				new NotifyFoundOtherPlayerMessage( 
    						(String)session.getUserProperties().get("screenName") 
    						);
    		
    		String forHostJson = new Gson().toJson( forHost );
    		
    		NotifyFoundHostMessage toOtherPlayer = 
    				new NotifyFoundHostMessage( 
    						(String)hostSession.getUserProperties().get("screenName"),
    						"localhost",
    						8080
    						);
    		
    		String toOtherJson = new Gson().toJson( toOtherPlayer );

    		try {
    			
				hostSession.getBasicRemote().sendObject( forHostJson );			
				hostSession.getUserProperties().put("otherSession", session);
				
				session.getBasicRemote().sendObject( toOtherJson );
				session.getUserProperties().put("otherSession", hostSession);
				
    		} catch (Exception exc) {
				
    			exc.printStackTrace();
			}
    		
    	}    	
    }
 
    @OnClose
    public void onConnectionClose(Session session) {
    	
    	String screenName = "";
    	
    	if( session.getUserProperties().get("screenName") != null ) {
    		screenName = (String)session.getUserProperties().get("screenName");
    	}
        
   		logger.info( "[CLOSE] " + session.getId() + " from " + screenName);
        
   		Session otherSession = (Session)session.getUserProperties().get("otherSession");
   		if( otherSession != null ) {  // leaving in a game; clean up other session
   			
   			synchronized(otherSession) {
   				NotifyGameEndMessage gameEnd = new NotifyGameEndMessage();
   				String toGameEndJson = new Gson().toJson( gameEnd );
   				try {
   					otherSession.getBasicRemote().sendObject( toGameEndJson );
   					otherSession.getUserProperties().remove("otherSession");
   				} catch(Exception exc) {
   					exc.printStackTrace();
   				}
   			}
   			
   			activeSessions.remove(otherSession);
   		}
   		
    	if( unmatchedSessions.contains(session) ) {
        	unmatchedSessions.remove(session);
        }
        
    	activeSessions.remove( session );
    }
}
