package it.polimi.ingsw.gc14.Network.TCP.Server;

import it.polimi.ingsw.gc14.Controller.GameController;
import it.polimi.ingsw.gc14.LimitedList;
import it.polimi.ingsw.gc14.Model.Game;
import it.polimi.ingsw.gc14.Network.NetworkEvent;
import it.polimi.ingsw.gc14.Network.NetworkEvents.AddPlayer;

import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;


public class TCPServer {
    int port = -1;
    int ConnectedPlayers = 0;
    ServerSocket serverTCP = null;
    GameController gameController;
    BlockingQueue<NetworkEvent> actionQueue;
    private LimitedList<String> playerList;
    private List<ClientHandler> clientHandlers;



    private int getConnectedPlayers(){
        return ConnectedPlayers;
    }

    public void start(){
        clientHandlers = new ArrayList<>();

        try{
            serverTCP = new ServerSocket(port);
        }
        catch (IOException e){
            System.out.println("Could not listen on port: " + port);
            e.printStackTrace();
            return;
        }
        System.out.println("Listening on port: " +  port);

        while(true){
            Socket clientSocket = null;

            try{
                clientSocket = serverTCP.accept();
                ObjectInputStream clientSocketObj = new ObjectInputStream(clientSocket.getInputStream());
                NetworkEvent event = (NetworkEvent) clientSocketObj.readObject();

                synchronized (gameController){
                    if(!(event instanceof AddPlayer) || (gameController.getModel() != null && gameController.getModel().getCurrentPlayerNumber() >= gameController.getModel().getNPlayers())){
                        clientSocket.getOutputStream().write((int)(-1));
                        clientSocket.close();
                        System.out.println("Invalid parameters. Connection terminated.\n");
                    }
                    else{
                        synchronized (gameController) {
                            AddPlayer addPlayer = (AddPlayer) event;
                            if(playerList.isEmpty() && (addPlayer.getProposedNPlayer() < 2 || addPlayer.getProposedNPlayer() > 5)){
                                clientSocket.getOutputStream().write((int)(-1));
                                clientSocket.close();
                                System.out.println("Invalid parameters. Connection terminated.\n");
                            }
                            else if(gameController.addPlayer(addPlayer.getUsername())){
                                if(playerList.isEmpty()){
                                    Game game = new Game(addPlayer.getProposedNPlayer());
                                    gameController.setModel(game);
                                    playerList.setLimit(addPlayer.getProposedNPlayer());
                                }
                                playerList.add(addPlayer.getUsername());
                                clientSocket.getOutputStream().write((int)(1));
                            }
                        }

                    }
                }
                // gestione di ADD_PLAYER
            }
            catch(IOException e){
                e.printStackTrace();
            }
            catch(ClassNotFoundException e){
                throw new RuntimeException(e);
            }

            System.out.println("Accepted player: " + gameController.getModel().getPlayerByUsername(clientSocket.getInetAddress().toString()));

            ConnectedPlayers++;
            ClientHandler clientHandler = new ClientHandler(clientSocket, clientHandlers, actionQueue);
            clientHandlers.add(clientHandler);

            //Sending model to clients
            if(ConnectedPlayers == gameController.getModel().getNPlayers()){
                for (ClientHandler handler : clientHandlers) {
                    try {
                        synchronized(handler.out){
                            ObjectOutputStream socketTx = new ObjectOutputStream(handler.getClientSocket().getOutputStream());
                            socketTx.writeObject(gameController.getModel());
                        }
                    }
                    catch(IOException e){
                        e.printStackTrace();
                    }
                }
            }

            Thread t = new Thread(clientHandler);
            t.start();
        }
    }

    public TCPServer(GameController gameController, int port, BlockingQueue<NetworkEvent> actionQueue, LimitedList<String> players){
        this.port = port;
        this.gameController = gameController;
        this.actionQueue = actionQueue;
        this.playerList = players;
    }

    public void notifyAll(NetworkEvent event){
        clientHandlers.forEach((x) -> x.notifyEvent(event));
    }

    public void notifyAll(Game model){
        clientHandlers.forEach((x) -> x.notifyModel(model));
    }
}
