/// ----------------------------------------------
        /// FUNCTION:		Process Incoming packet
        ///
        /// DATE:			March 5 , 2019
        ///
        /// REVISIONS:
        ///
        /// DESIGNER:  Cameron Roberts,
        ///
        /// PROGRAMMER: Kieran Lee
        ///
        /// INTERFACE:      static void ProcessIncomingPacket (Object packetInfo)
        ///
        /// RETURNS:        void
        ///
        /// NOTES:
        /// ----------------------------------------------
        static void ProcessIncomingPacket(Object packetInfo)
        {
            UnpackedPacket up = (UnpackedPacket)packetInfo;             //may not work if not serializable, will have to look into that

            foreach (var element in up.UnreliableElements)
            {
                element.UpdateState(new ServerStateMessageBridge(state));                   //maybe should also keep a single bridge object in state instead of making a new one every time?
            }
            foreach (var element in up.ReliableElements)
            {
                element.UpdateState(new ServerStateMessageBridge(state));                   //maybe should also keep a single bridge object in state instead of making a new one every time?
            }
        }
        /// ----------------------------------------------
        /// 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;
                    }
                }
            }
        }
        public static void Main(string[] args)
        {
            // Create a list of elements to send. Using the same list for unreliable and reliable
            List <UpdateElement> elements = new List <UpdateElement>();

            elements.Add(new HealthElement(10, 0));

            // Create a ClientStateMessageBridge to use later
            ClientStateMessageBridge bridge = new ClientStateMessageBridge();

            // Create a UDPSocket
            UDPSocket socket = new UDPSocket();

            // Bind the socket. No parameter means ephemeral port.
            socket.Bind();

            // Create a ReliableUDPConnection
            ReliableUDPConnection connection = new ReliableUDPConnection();

            // Create a packet using the connection
            Packet packet = connection.CreatePacket(elements, elements);

            // Create a destination to send to. Address and port must be in network order
            Destination destination = new Destination((uint)System.Net.IPAddress.HostToNetworkOrder(2130706433), (ushort)System.Net.IPAddress.HostToNetworkOrder((short)8000));

            Console.WriteLine("Sending packet");
            // Send the packet to the destination
            socket.Send(packet, destination);

            // Receive a response packet. Receive calls block.
            packet = socket.Receive();

            Console.WriteLine("Got packet response");
            // Process the received packet
            UnpackedPacket unpacked = connection.ProcessPacket(packet, new ElementId[] { ElementId.HealthElement });

            // Iterate through the unreliable elements and call their UpdateState function.
            foreach (var element in unpacked.UnreliableElements)
            {
                element.UpdateState(bridge);
            }
            // Close the socket when done
            socket.Close();
        }
        public static void Main(string[] args)
        {
            // Create a list of elements to send. Using the same list for unreliable and reliable
            List <UpdateElement> elements = new List <UpdateElement>();

            elements.Add(new HealthElement(15, 6));

            // Create a UDPSocket
            UDPSocket socket = new UDPSocket();

            // Bind the socket. Address must be in network byte order
            socket.Bind((ushort)System.Net.IPAddress.HostToNetworkOrder((short)8000));

            // Create a ReliableUDPConnection
            ReliableUDPConnection connection = new ReliableUDPConnection();

            // Create a ServerStateMessageBridge to use later
            ServerStateMessageBridge bridge = new ServerStateMessageBridge();

            while (true)
            {
                // Receive a packet. Receive calls block
                Packet packet = socket.Receive();

                Console.WriteLine("Got packet.");

                // Unpack the packet using the ReliableUDPConnection
                UnpackedPacket unpacked = connection.ProcessPacket(packet, new ElementId[] { ElementId.HealthElement });
                // Iterate through the unreliable elements and call their UpdateState function.
                foreach (var element in unpacked.UnreliableElements)
                {
                    element.UpdateState(bridge);
                }

                Console.WriteLine("Sending response packet.");
                // Create a new packet
                packet = connection.CreatePacket(elements, elements);

                // Send the packet
                socket.Send(packet, socket.LastReceivedFrom);
            }
        }
        /// ----------------------------------------------
        /// 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");
                }
            }
        }