package jdungeonquest.network;

import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.Server;
import com.esotericsoftware.minlog.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jdungeonquest.Game;
import static jdungeonquest.enums.NetworkMessageType.ChatMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NetworkServer implements Runnable {

    private Game game = new Game();
    private Server server = null;
    Logger logger = LoggerFactory.getLogger(NetworkServer.class);
    private int serverPort = 3334;
    public static final int DEFAULT_PORT = 4446;

    private Map<Integer, List<String>> clientPlayersMap = new HashMap<>();
    
    public NetworkServer() {
        this(DEFAULT_PORT);
        Log.set(Log.LEVEL_DEBUG);
    }

    public NetworkServer(int port) {
        server = new Server();
        Network.registerClasses(server);
        this.serverPort = port;
    }

    @Override
    public void run() {
        server.addListener(new Listener() {
            @Override
            public void connected(Connection connection) {
                logger.debug("Client '" + connection + "' " + connection.getID() + " connected");
            }

            @Override
            public void disconnected(Connection connection) {
                logger.debug("Client '" + connection + "' " + connection.getID() + " disconnected");
                broadcast("Client " + connection.toString() + " has quit.");
                for(String player : clientPlayersMap.get(connection.getID())){
                    //game.removePlayer(player);
                }
                broadcast(new PlayerList(game.getPlayerList()));
            }

            @Override
            public void received(Connection connection, Object object) {
                logger.debug(connection.getID() + " " + connection.toString() + " Recieved package: " + object);
                if (object instanceof String) {
                    String test = (String) object;
                    if (test.equals("he")) {
                        clientPlayersMap.put(connection.getID(), new ArrayList<String>());
                        connection.sendTCP(new String("lo"));
                    }
                }else if (object instanceof Message) {
                    switch (((Message) object).msgType) {
                        case RegistrationRequest:
                            registerPlayer(connection, ((RegistrationRequest) object).playerName);
                            break;

                        case ChatMessage:
                            ChatMessage msg = (ChatMessage)object;
                            logger.debug(msg.author + ":" + msg.message);
                            server.sendToAllExceptTCP(connection.getID(), msg);
                            break;
                            
                        case PlayerList:
                            //in case player has disconnected - need to correct the entry in the clientPlayerMap
                            //connection.sendTCP("lo");
                            break;
                            
                        default:
                            break;
                    }
                } else if (object instanceof com.esotericsoftware.kryonet.FrameworkMessage.KeepAlive){
                } else {
                    logger.info("Recieved unkown package: " + object);
                }
            }

        });
        try {
            server.bind(this.serverPort);
        } catch (IOException ex) {
            logger.error("Exception" + ex);
        }
        server.start();
        logger.debug("Server started on port " + this.serverPort);
    }

    private void registerPlayer(Connection conn, String playerName) {
        logger.debug("Registering player " + playerName);
        boolean result = game.registerPlayer(playerName);
        if (result) {
            conn.sendTCP(new RegistrationRequest(playerName));
            attachPlayerToClient(conn.getID(), playerName);
            broadcast("Player " + playerName + " joined.");
            broadcast(new PlayerList(game.getPlayerList()));
        } else {
            conn.sendTCP(new RegistrationRequest(""));
        }
    }

    private void attachPlayerToClient(int id, String name) {
        if(clientPlayersMap.containsKey(id)){
            List<String> players = clientPlayersMap.get(id);
            players.add(name);
        }else{
            clientPlayersMap.put(id, new ArrayList<>(Arrays.asList(new String[]{name})));
        }
        logger.debug("Added " + name);
        logger.debug("List of players for Client " + id + " : " + clientPlayersMap.get(id));
    }

    private void detachPlayerFromClient(int id, String name) {
        if (clientPlayersMap.containsKey(id)) {
            List<String> players = clientPlayersMap.get(id);
            players.remove(name);
            clientPlayersMap.put(id, players);
        }
        logger.debug("Removed " + name);
        logger.debug("List of players for Client " + id + " : " + clientPlayersMap.get(id));
    }
    
    public void stop() {
        server.stop();
        logger.debug("Server stopped");
    }

    public Game getGame() {
        return game;
    }

    private void broadcast(String text) {
        server.sendToAllTCP(new ChatMessage(text, "Server"));
    }

    private void broadcast(PlayerList playerList) {
        server.sendToAllTCP(playerList);
    }    
}

@@ -2432,24 +2432,107 @@
 ng(%22lo%22));%0D%0A
+                        connection.sendTCP(new PlayerList(game.getPlayerList()));%0D%0A
             
@@ -3207,138 +3207,358 @@
     
-case PlayerList:%0D%0A                            //in case player has disconnected - need to correct the entry in the clientPlayerMap
+//We recieve this only when client has disconnected%0D%0A                        //here we need to correct the entry in the clientPlayerMap%0D%0A                        //according to the players that were registered on that client%0D%0A                        case PlayerList:%0D%0A                            //confirm connection with the server if client is valid
 %0D%0A  
@@ -5909,24 +5909,76 @@
 ng text) %7B%0D%0A
+        logger.debug(%22Broadcasting text:%22 + text);%0D%0A
         serv
@@ -6086,24 +6086,88 @@
 yerList) %7B%0D%0A
+        logger.debug(%22Broadcasting playerList:%22 + playerList);%0D%0A
         serv

Send list of players to new clients