using System;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;

public class StrategoClient
{
    private bool _inAGame = false;
    private GameLogic _gameLogic;
    private LoadingScreen _loadingScreen;
    private ServerClientProtocol _protocol = new ServerClientProtocol();
    private StreamReader _input;
    private StreamWriter _output;
    private TcpClient _clientSocket = new TcpClient();

    public StrategoClient(LoadingScreen loadingScreen)
    {
        _loadingScreen = loadingScreen;
    }

    //connects to the server
    public void connectToHost()
    {
        try
        {
            //_clientSocket.Connect("67.252.187.177", 4567);//my router
            _clientSocket.Connect("192.168.0.20", 4567);//my house
            //_clientSocket.Connect("128.205.15.105", 4567);//school
            //_clientSocket.Connect("128.205.93.65", 4567);//work
            _input = new StreamReader(_clientSocket.GetStream());
            _output = new StreamWriter(_clientSocket.GetStream());
        }
        catch
        {
            if (_loadingScreen.isFailureScreenShowing() == true)
            {
                _loadingScreen.setFailureScreenMessage("Server is not online");
            }
            else
            {
                _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.createFailureScreenDelegate(_loadingScreen.createFailureScreen), "Server is not online");
            }
        }
    }

    public void setGameLogic(GameLogic gameLogic)
    {
        _gameLogic = gameLogic;
    }

    //returns whether or not the socket is connected
    public bool isSocketConnected()
    {
        //if the socket is null
        if (_clientSocket == null)
        {
            return false;
        }
        //if it might be connected
        else
        {
            return _clientSocket.Connected;
        }
    }

    //checks to see if this name is already being used on the server
    public void checkNameValidation(String name)
    {
        String serverClient = "%$!*&#01";
        serverClient += name;
        _output.WriteLine(serverClient);
        _output.Flush();
    }

    public void writeToServerChatArea(String playerName, String message)
    {
        String serverClient = "%$!*&#020";
        serverClient += playerName;
        serverClient += "%";
        serverClient += message;
        serverClient += "%";

        _output.WriteLine(serverClient);
        _output.Flush();
    }

    public void writeToPendingGameChatArea(String playerName, String message)
    {
        String serverClient = "%$!*&#022";
        serverClient += playerName;
        serverClient += "%";
        serverClient += message;
        serverClient += "%";

        _output.WriteLine(serverClient);
        _output.Flush();
    }

    public void createPendingGame(String gameName)
    {
        _output.WriteLine("%$!*&#0310" + gameName);
        _output.Flush();
    }

    //NEW cancels the game you are created
    public void cancelPendingGame(String gameName)
    {
        _output.WriteLine("%$!*&#0314" + gameName);
        _output.Flush();
    }

    //NEW tells the server that a player needs the list of currently created games
    public void getListOfStrategoGames()
    {
        _output.WriteLine("%$!*&#0312");
        _output.Flush();
    }

    public void joinPendingGame(String gameName)
    {
        _output.WriteLine("%$!*&#0313" + gameName);
        _output.Flush();
    }

    public void leavePendingGame()
    {
        _output.WriteLine("%$!*&#0315");
        _output.Flush();
    }

    public void bootPlayer()
    {
        _output.WriteLine("%$!*&#0316");
        _output.Flush();
    }

    public void startPendingGame()
    {
        _output.WriteLine("%$!*&#0317");
        _output.Flush();
    }

    public void showPiecesAtend(int show)
    {
        String serverClient = "%$!*&#27";
        serverClient += show;

        _output.WriteLine(serverClient);
        _output.Flush();
    }

    public void writeToStrategoGameChat(String message)
    {
        String serverClient = "%$!*&#20";
        serverClient += message;

        _output.WriteLine(serverClient);
        _output.Flush();
    }

    public void writeFinalized(BoardSquare[][] boardSquares, Player currentPlayer)
    {
        String serverClient = "%$!*&#21";
        serverClient += _protocol.encodeStrategoBoard(boardSquares, currentPlayer);

        _output.WriteLine(serverClient);
        _output.Flush();
    }

    public void writePieceClicked(int index)
    {
        String serverClient = "%$!*&#22" + index;

        _output.WriteLine(serverClient);
        _output.Flush();
    }

    public void writeChangeTurn()
    {
        String serverClient = "%$!*&#24";

        _output.WriteLine(serverClient);
        _output.Flush();
    }

    public void proposeDraw(int draw)
    {
        String serverClient = "%$!*&#25";
        serverClient += draw;

        _output.WriteLine(serverClient);
        _output.Flush();
    }

    public void writeMoveToServer(bool attackedUnit, int lastSquare, int newSquare)
    {
        String serverClient = "%$!*&#23";
        serverClient += _protocol.encodeMovingPiece(attackedUnit, lastSquare, newSquare);

        _output.WriteLine(serverClient);
        _output.Flush();
    }

    public void leaveStrategoGame()
    {
        String serverClient = "%$!*&#26";

        _output.WriteLine(serverClient);
        _output.Flush();

        this.exit();
    }

    public void exitServer()
    {
        if (_clientSocket != null && _clientSocket.Connected == true)
        {
            _output.WriteLine("%$!*&#00");
            _output.Flush();
        }

        this.exit();
    }

    public void exit()
    {
        _input.Close();
        _output.Close();
        _clientSocket.Close();
    }

    public void run()
    {
        String fromServer;
        try
        {
            //while the client is still connected and can read from the server
            while (_clientSocket.Connected == true && (fromServer = _input.ReadLine()) != null)
            {
                if (fromServer.StartsWith("%$!*&#"))
                {
                    switch ((fromServer[6] - 48))
                    {
                        //in server
                        case 0:
                            switch ((fromServer[7] - 48))
                            {
                                //closure from server
                                case 0:

                                    //server disconnected

                                    break;
                                //connecting to server/receiving name validation
                                case 1:
                                    //if the name is valid
                                    if ((fromServer[8] - 48) == 0)
                                    {
                                        _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.createServerScreenDelegate(_loadingScreen.createServerScreen));
                                        _protocol.decodeClientNames(fromServer.Substring(9));
                                        _loadingScreen.setPlayerNamesBox(_protocol.getClientNames());
                                    }
                                    //if the name is not valid
                                    else
                                    {
                                        this.exit();

                                        if (_loadingScreen.isFailureScreenShowing() == true)
                                        {
                                            _loadingScreen.setFailureScreenMessage("That name is already taken.\nEnter a different name.");
                                        }
                                        else
                                        {
                                            _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.createFailureScreenDelegate(_loadingScreen.createFailureScreen), "That name is already taken.\nEnter a different name.");
                                        }
                                    }
                                    break;
                                //updates the client names in the server chat
                                case 2:
                                    _protocol.decodeClientNames(fromServer.Substring(8));
                                    _loadingScreen.setPlayerNamesBox(_protocol.getClientNames());
                                    break;
                                //chat inside server screen
                                case 3:
                                    _protocol.decodeServerChatFieldVariables(fromServer.Substring(8));
                                    _loadingScreen.setChatBoxText(_protocol.getPlayerName(), _protocol.getMessage());
                                    break;
                                //game name validation
                                case 4:
                                    //name was not valid
                                    if ((fromServer[8] - 48) == 0)
                                    {
                                        _loadingScreen.setFailureScreenMessage("Game name is already taken");
                                    }
                                    //game name is valid
                                    else
                                    {
                                        //send check box values with game name so server can create a new game
                                        _output.WriteLine("%$!*&#0311" + _protocol.encodeCheckBoxValues(_loadingScreen.getCheckBoxValues()));
                                        _output.Flush();
                                        _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.createServerScreenHostDelegate(_loadingScreen.createServerScreenHost));
                                    }
                                    break;
                                //joining a game
                                case 5:
                                    String gameName = "";
                                    int index = 8;
                                    while (fromServer[index] != 37)//%
                                    {
                                        gameName += fromServer[index];
                                        index++;
                                    }
                                    index++;
                                    _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.createServerScreenClientDelegate(_loadingScreen.createServerScreenClient), gameName, _protocol.decodeCheckBoxValues(fromServer.Substring(index)));
                                    break;
                                //exiting from a game
                                case 6:
                                    switch ((fromServer[8] - 48))
                                    {
                                        //game canceled by host
                                        case 0:
                                            _loadingScreen.getChatBox().Text = "";
                                            _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.createServerScreenDelegate(_loadingScreen.createServerScreen));
                                            _loadingScreen.getChatBox().Text = "Game has been canceled!";
                                            break;
                                        //booted by host
                                        case 1:
                                            this.leavePendingGame();
                                            _loadingScreen.getChatBox().Text = "";
                                            _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.createServerScreenDelegate(_loadingScreen.createServerScreen));
                                            _loadingScreen.getChatBox().Text = "Booted from game by host!";
                                            break;
                                    }
                                    break;
                                //looking for games to join
                                case 7:
                                    switch ((fromServer[8] - 48))
                                    {
                                        //gets list of games
                                        case 0:
                                            _protocol.decodeStrategoGame(fromServer.Substring(9));
                                            _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.createStrategoGameButtonsDelegate(_loadingScreen.createStrategoGameButtons), _protocol.getGameNames(), _protocol.getHostNames(), _protocol.getCheckBoxValues());
                                            break;
                                        //no games currently exist
                                        case 1:
                                            _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.setFindGameInfoBoxDelegate(_loadingScreen.setFindGameInfoBox), "No Games Found\r\n" + "\r\nHost: \r\n" + "\r\nGame Name: ");
                                            break;
                                    }
                                    break;
                                //failed join game attempts and updates
                                case 8:
                                    switch ((fromServer[8] - 48))
                                    {
                                        //game doesn't exist
                                        case 0:
                                            _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.setFindGameInfoBoxDelegate(_loadingScreen.setFindGameInfoBox), "Game canceled\r\n" + "\r\nHost: \r\n" + "\r\nGame Name: ");
                                            break;
                                        //game is full
                                        case 1:
                                            _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.setFindGameInfoBoxDelegate(_loadingScreen.setFindGameInfoBox), "Game is full\r\n" + "\r\nHost: \r\n" + "\r\nGame Name: ");
                                            break;
                                        //update names of players in game
                                        case 2:
                                            _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.setPlayerLabelsDelegate(_loadingScreen.setPlayerLabels), _protocol.decodePlayerNames(fromServer.Substring(9)));
                                            break;
                                        //enable booting of a player that entered the game
                                        case 3:
                                            int value = (fromServer[9] - 48);
                                            //if there is only 1 player in the game
                                            if (value == 1)
                                            {
                                                _loadingScreen.setPlayerBooting(false);
                                            }
                                            //if there are 2 players in the game
                                            else
                                            {
                                                _loadingScreen.setPlayerBooting(true);
                                            }
                                            break;
                                    }
                                    break;
                                //starting the game
                                case 9:
                                    _inAGame = true;
                                    _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.startGameDelegate(_loadingScreen.startGame), fromServer.Substring(8));
                                    break;
                            }
                            break;
                        //in game chat message
                        case 2:
                            _gameLogic.getGameGraphics().appendToChatBox(fromServer.Substring(7));
                            break;
                        //starting game after set-up
                        case 3:
                            _gameLogic.updateBoard(_protocol.decodeStrategoBoard(fromServer.Substring(7), _gameLogic.getPlayers()[0]));
                            break;
                        //display piece clicked by other player
                        case 4:
                            _gameLogic.getGameGraphics().setLastClickedSquare(int.Parse(fromServer.Substring(7)));
                            break;
                        //update board and change turn
                        case 5:
                            _protocol.decodeStrategoUpdates(fromServer.Substring(7), _gameLogic.getPlayers()[0]);

                            _gameLogic.getGameGraphics().getGameForm().Invoke(new GameLogic.displayResultsOfMoveDelegate(_gameLogic.displayResultsOfMove), _protocol.getOutCome(), _protocol.getLastSquare(), _protocol.getNewSquare(), _protocol.getOtherPiece(), _protocol.getBoardSquares());
                            break;
                        //ends game
                        case 6:
                            _protocol.decodeEndGame(fromServer.Substring(7));
                            _gameLogic.getGameGraphics().getGameForm().Invoke(new GameLogic.checkGameOverDelegate(_gameLogic.checkGameOver), _protocol.getWinningPlayer(), _protocol.getWinType(), _protocol.getLastSquare(), _protocol.getNewSquare(), _protocol.getOtherPiece());
                            break;
                        //changes turn or finalize set-up
                        case 7:
                            switch ((fromServer[7] - 48))
                            {
                                //change turn
                                case 0:
                                    _gameLogic.nextTurn();
                                    break;
                                //player has finalized set-up
                                case 1:
                                    _gameLogic.getGameGraphics().getGameForm().Invoke(new GameGraphics.setPlayerFinalizedAlertDelegate(_gameLogic.getGameGraphics().setPlayerFinalizedAlert));
                                    break;
                                //a player wants to show their pieces at the end of the game
                                case 2:
                                    _gameLogic.updateBoardPieces(_protocol.decodeStrategoBoardPieces(fromServer.Substring(8), _gameLogic.getPlayers()[0]));
                                    break;
                            }
                            break;
                        //player left game
                        case 8:
                            _gameLogic.getGameGraphics().getGameForm().Invoke(new GameLogic.checkGameOverDelegate(_gameLogic.checkGameOver), 2, 5, 100, 100, 12);
                            break;
                        //ends display of units attacking
                        case 9:
                            _gameLogic.getGameGraphics().getGameForm().Invoke(new GameGraphics.displayPieceDelegate(_gameLogic.getGameGraphics().displayPiece), null, null);
                            break;
                    }
                }
            }
        }
        //if the server disconnects for any reason or has a connection problem
        catch (IOException)
        {
            //if the game is still potentially pending
            if (_inAGame == false)
            {
                _loadingScreen.getLoadingForm().Invoke(new LoadingScreen.createFailureScreenDelegate(_loadingScreen.createFailureScreen), "Server is down. Please try connecting later.");
            }
            //if you are playing a game
            else
            {
                _gameLogic.gameOver(null, 6);
            }
            this.exit();
        }
        catch
        {

        }
    }
}
