/// ----------------------------------------------
        /// FUNCTION:		GameState
        ///
        /// DATE:			April 2 , 2019
        ///
        /// REVISIONS:
        ///
        /// DESIGNER:  Cameron Roberts, Kieran Lee
        ///
        /// PROGRAMMER: Cameron Roberts, Kieran Lee
        ///
        /// INTERFACE:      private static void GameState (UDPSocket socket, State state, ServerStateMessageBridge bridge)
        ///                 socket: socket to send and receive from
        ///                 state:  gives access to client manager
        ///                 ServerStateMessageBridge: allows packets to be processed properly
        ///
        /// RETURNS:        void
        ///
        /// NOTES:		    function that server remains while game is in progress
        ///                 Expect gameplay packets, discard all else.
        ///                 does initial game setup then
        ///                 receives and processes packets
        /// ----------------------------------------------
        private static void GameState(UDPSocket socket, State state, ServerStateMessageBridge bridge)
        {
            state.GameState.CollisionBuffer.requiredValidity = Math.Max((int)(state.ClientManager.CountCurrConnections * 0.8), 1);
            Console.WriteLine("set required validity to {0}", state.GameState.CollisionBuffer.requiredValidity);

            //give each player their starting ability
            for (int i = 0; i < state.GameState.CreatedPlayersCount; i++)
            {
                Player player     = (Player)state.GameState.actors [i];
                int    newSkillId = AbilityEffects.ReturnRandomAbilityId(player);
                state.GameState.OutgoingReliableElements.Enqueue(new AbilityAssignmentElement(i, newSkillId));
                player.AddAbility(1, (AbilityType)newSkillId);
            }

            //spawn test tower at 100,100
            state.GameState.AddTower(new GameUtility.Coordinate(355, 187));
            state.GameState.AddTower(new GameUtility.Coordinate(356, 302));
            state.GameState.AddTower(new GameUtility.Coordinate(150, 312));
            state.GameState.AddTower(new GameUtility.Coordinate(152, 193));

            // Fire Timer.Elapsed event every 1/30th second (sending Game State at 30 fps)
            StartGameStateTimer(socket, state);


            //Timer for keeping track of the game progress
            state.GameState.StartGamePlayTimer();

            while (state.TimesEndGameSent < 80)
            {
                if (!state.GameOver)
                {
                    try {
                        Packet packet = socket.Receive();
                        if (ReliableUDPConnection.GetPacketType(packet) != PacketType.GameplayPacket)
                        {
                            continue;
                        }
                        int actorId = ReliableUDPConnection.GetPlayerID(packet);
                        state.ClientManager.FindClientByActorId(actorId).MarkPacketReceive();

                        UnpackedPacket unpacked = state.ClientManager.FindClientByActorId(actorId).Connection.ProcessPacket(packet, unpackingArr);
                        ThreadPool.QueueUserWorkItem(ProcessIncomingPacket, unpacked);
                    } catch (Exception e) {
                        Console.WriteLine(e);
                        continue;
                    }
                }
            }
        }
        /// ----------------------------------------------
        /// FUNCTION:		LobbyState
        ///
        /// DATE:			April 2 , 2019
        ///
        /// REVISIONS:
        ///
        /// DESIGNER:  Cameron Roberts, Kieran Lee
        ///
        /// PROGRAMMER: Cameron Roberts, Kieran Lee, Segal Au
        ///
        /// INTERFACE:      private static void LobbyState (UDPSocket socket, State state, ServerStateMessageBridge bridge)
        ///                 socket: socket to send and receive from
        ///                 state:  gives access to client manager
        ///                 ServerStateMessageBridge: allows packets to be processed properly
        ///
        /// RETURNS:        void
        ///
        /// NOTES:		    function that server remains in until lobyy is left and game is started.
        ///                 Expect request packets and hearbeat packets, discard all else.
        ///                 waits until all players are ready, then sends start game message.
        ///                 does not move to game state until all clients have started sending game packets
        /// ----------------------------------------------
        private static void LobbyState(UDPSocket socket, State state, ServerStateMessageBridge bridge)
        {
            StartHeartBeat(socket, state);
            while (state.TimesEndGameSent < 80)
            {
                Packet packet;
                try {
                    Console.WriteLine("Waiting for packet in lobby state");
                    packet = socket.Receive();
                } catch (TimeoutException e) {
                    Console.WriteLine("Timeout");
                    continue;
                }
                switch (ReliableUDPConnection.GetPacketType(packet))
                {
                case PacketType.HeartbeatPacket:
                    Console.WriteLine("Got heartbeat packet");
                    int clientId = ReliableUDPConnection.GetPlayerID(packet);
                    state.ClientManager.Connections [clientId].MarkPacketReceive();
                    if (state.ClientManager.Connections [clientId] == null)
                    {
                        continue;
                    }
                    UnpackedPacket unpacked = state.ClientManager.Connections [clientId].Connection.ProcessPacket(packet, lobbyUnpackingArr);
                    foreach (var element in unpacked.UnreliableElements)
                    {
                        element.UpdateState(bridge);
                    }
                    foreach (var element in unpacked.ReliableElements)
                    {
                        element.UpdateState(bridge);
                    }

                    break;

                case PacketType.RequestPacket:
                    Console.WriteLine("Got request packet");
                    try {
                        string name      = ReliableUDPConnection.GetClientNameFromRequestPacket(packet);
                        int    newClient = state.ClientManager.AddConnection(socket.LastReceivedFrom, name);
                        socket.Send(ReliableUDPConnection.CreateConfirmationPacket(newClient), state.ClientManager.Connections [newClient].Destination);
                        Console.WriteLine("Sent confirmation packet to client " + newClient + " with name " + state.ClientManager.Connections [newClient].Name);
                    } catch (OutOfMemoryException e) {
                    }

                    break;

                default:
                    Log.V("Got unexpected packet type, discarding");
                    break;
                }

                Console.WriteLine("Checking if all players ready");
                //TODO If all players ready start game send start packet and go to gamestate.
                Log.V("Checking if all players are ready");

                bool allReady = state.ClientManager.CountCurrConnections > 0;
                for (int i = 0; i < state.ClientManager.CountCurrConnections; i++)
                {
                    if (state.ClientManager.Connections [i] == null)
                    {
                        continue;
                    }
                    PlayerConnection client = state.ClientManager.Connections [i];
                    Log.V("Client " + i + ", " + client.Ready);
                    allReady &= client.Ready;                     //bitwise operater to check that every connection is ready
                }
                Log.V("Current connections " + state.ClientManager.CountCurrConnections);


                if (allReady)
                {
                    Log.D("All are ready sending startgame packet");
                    List <UpdateElement> unreliableElements = new List <UpdateElement> ();
                    List <UpdateElement> reliableElements   = new List <UpdateElement> ();
                    int players = 0;
                    for (int i = 0; i < state.ClientManager.CountCurrConnections; i++)
                    {
                        if (state.ClientManager.Connections [i] == null)
                        {
                            continue;
                        }
                        state.ClientManager.Connections [i].ActorId = state.GameState.AddPlayer(state.ClientManager.Connections [i].Team);
                        players++;
                    }

                    reliableElements.Add(new GameStartElement(players));
                    var playerInfo = new List <LobbyStatusElement.PlayerInfo> ();
                    for (int i = 0; i < state.ClientManager.CountCurrConnections; i++)
                    {
                        if (state.ClientManager.Connections [i] == null)
                        {
                            continue;
                        }
                        playerInfo.Add(new LobbyStatusElement.PlayerInfo(state.ClientManager.Connections [i].ClientId,
                                                                         state.ClientManager.Connections [i].Name,
                                                                         state.ClientManager.Connections [i].Team,
                                                                         state.ClientManager.Connections [i].Ready));
                    }
                    unreliableElements.Add(new LobbyStatusElement(playerInfo));

                    for (int i = 0; i < state.ClientManager.CountCurrConnections; i++)
                    {
                        if (state.ClientManager.Connections [i] == null)
                        {
                            continue;
                        }
                        PlayerConnection client      = state.ClientManager.Connections [i];
                        Packet           startPacket = client.Connection.CreatePacket(unreliableElements, reliableElements, PacketType.HeartbeatPacket);
                        socket.Send(startPacket, client.Destination);
                    }

                    //wait for each client to start sending gameplay packets, which indicates
                    //that each client has received the gamestart Message
                    bool allSendGame = false;
                    int  playerId;
                    while (!allSendGame)
                    {
                        try{
                            packet = socket.Receive();
                            switch (ReliableUDPConnection.GetPacketType(packet))
                            {
                            case PacketType.GameplayPacket:
                                playerId = ReliableUDPConnection.GetPlayerID(packet);
                                state.ClientManager.FindClientByActorId(playerId).startedGame = true;
                                allSendGame = true;
                                Console.WriteLine("Received packet from {0}", playerId);
                                for (int i = 0; i < state.ClientManager.CountCurrConnections; i++)
                                {
                                    if (state.ClientManager.Connections [i] == null)
                                    {
                                        continue;
                                    }
                                    PlayerConnection client = state.ClientManager.Connections [i];
                                    Console.WriteLine("Client {0}, {1} sent a game packet while server in lobby", i, client.startedGame);
                                    allSendGame &= client.startedGame;                                     //bitwise operater to check that every connection is ready
                                }
                                break;

                            default:
                                break;
                            }
                        } catch (TimeoutException e) {
                            continue;
                        }
                    }
                    sendHeartBeatPing.Enabled = false;
                    GameState(socket, state, bridge);
                    return;
                }
                else
                {
                    Log.V("All players not ready");
                }
            }
        }