using System;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Text;

namespace GameServer
{
    class cUDP
    {
        private UdpClient UDPSocket_1;
        private UdpClient UDPSocket_2;

        public void StartUDPServer()
        {
            try
            {
                Thread RecvThread1 = new Thread(new ThreadStart(recvData_1));
                RecvThread1.Start();
                Thread RecvThread2 = new Thread(new ThreadStart(recvData_2));
                RecvThread2.Start();

                new cLog(C.MSG_DEBUG, "UDP Socket 1 [5350] Started");
                new cLog(C.MSG_DEBUG, "UDP Socket 2 [5351] Started");

                Program.ServerClass.StartServer();
            }
            catch
            {
                new cLog(C.MSG_ERROR, "Couldn't start UDP Sockets on Ports: 5350 | 5351");
                Console.Read();
            }
        }

        private void OnReceive(IAsyncResult ar)
        {
            UdpClient udpClient = (UdpClient)ar.AsyncState;
            try
            {
                IPEndPoint remoteEndpoint = (IPEndPoint)udpClient.Client.LocalEndPoint;
                byte[] received = udpClient.EndReceive(ar, ref remoteEndpoint);
                if (received.Length > 0)
                {
                    byte[] analyzed = AnalyzePacket(received, remoteEndpoint);
                    if (analyzed.Length > 0)
                    {
                        udpClient.Send(analyzed, analyzed.Length, remoteEndpoint);
                    }
                }
            }
            catch (Exception ex)
            {
                new cLog(C.MSG_ERROR, "UDP Error (OnReceive)");

            }
            try
            {
                udpClient.BeginReceive(OnReceive, udpClient);
            }
            catch (SocketException ex)
            {
                int localPort = ((IPEndPoint)udpClient.Client.LocalEndPoint).Port;
                udpClient.Close();
                udpClient = new UdpClient(localPort);
                udpClient.BeginReceive(OnReceive, udpClient);
            }
        }

        private void recvData_1()
        {
            UDPSocket_1 = new UdpClient(5350);
            
            UDPSocket_1.BeginReceive(OnReceive, UDPSocket_1);

        }

        private void recvData_2()
        {
            UDPSocket_2 = new UdpClient(5351);
            
            UDPSocket_2.BeginReceive(OnReceive, UDPSocket_2);
        }

        private byte[] AnalyzePacket(byte[] RecvPacket, IPEndPoint IPeo)
        {
            byte[] Response = new Byte[0];
             if (RecvPacket[0] == 0x10 && RecvPacket[1] == 0x01 && RecvPacket[2] == 0x01)
            {
                Response = new Byte[14] {0x10, 0x01, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00,
                    RecvPacket[RecvPacket.Length - 4], 
                    RecvPacket[RecvPacket.Length - 3],
                    RecvPacket[RecvPacket.Length - 2], 
                    RecvPacket[RecvPacket.Length - 1] };

                /*10 01 01 00 14 e7 00 00 00 00 00 d4 d9 f7*/

                //here get client shitt(id, ip, etc)
                int tID = (RecvPacket[RecvPacket.Length - 4] << 24) | (RecvPacket[RecvPacket.Length - 3] << 16) | (RecvPacket[RecvPacket.Length - 2] << 8) | RecvPacket[RecvPacket.Length - 1];

                foreach (cWRClient _Client in Program.WRServer.getPlayers(false))
                {
                    if (_Client.getUserID() == tID)
                    {
                        _Client.setNetwork(IPeo); // Set UDP EndPoint @Client, because it needs this Information(Network IP, Network Port, Local IP, Local Port)
                        new cLog(C.MSG_NETWORK, "IP: " + IPeo.Address.ToString());
                        break;
                    }
                }
            }
            else if (RecvPacket[0] == 0x10 && RecvPacket[1] == 0x10 && RecvPacket[2] == 0x0 && RecvPacket[14] == 0x21)
            {
                byte[] LocalIPBytes = new byte[4] { RecvPacket[34], RecvPacket[35], RecvPacket[36], RecvPacket[37] };

                LocalIPBytes[0] = Convert.ToByte(LocalIPBytes[0] ^ 0x45);
                LocalIPBytes[1] = Convert.ToByte(LocalIPBytes[1] ^ 0x45);
                LocalIPBytes[2] = Convert.ToByte(LocalIPBytes[2] ^ 0x45);
                LocalIPBytes[3] = Convert.ToByte(LocalIPBytes[3] ^ 0x45);

                byte[] LocalPortBytes = new byte[2] { RecvPacket[32], RecvPacket[33] };

                LocalPortBytes[0] = Convert.ToByte(LocalPortBytes[0] ^ 0x45);
                LocalPortBytes[1] = Convert.ToByte(LocalPortBytes[1] ^ 0x45);
                
                IPEndPoint LocalIPeo = new IPEndPoint(new IPAddress(BitConverter.ToUInt32(LocalIPBytes, 0)), BitConverter.ToUInt16(LocalPortBytes, 0));

                byte[] SessionIDBytes = new byte[2] { RecvPacket[3], RecvPacket[4] };
                ushort SessionID = BitConverter.ToUInt16(SessionIDBytes, 0);

                foreach (cWRClient _Client in Program.WRServer.getPlayers(false))
                {
                    if (_Client.getSessionID() == SessionID)
                    {
                        _Client.setNetwork(LocalIPeo);
                        break;
                    }
                }
                
                byte[] IPBytes = IPeo.Address.GetAddressBytes();
             
                byte[] PortBytes = BitConverter.GetBytes(IPeo.Port);

                
                   Response = new Byte[65] 
                {
                   0x10, 0x10, 0x00, 0x00, RecvPacket[4], RecvPacket[5],
			    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x21,
			    0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
			    0x0, 0x0, 0x0, 0x1, 0x11, 0x13, 0x11, 
                PortBytes[1], PortBytes[0], IPBytes[3], IPBytes[2], IPBytes[1], IPBytes[0], /* Remote Stuff */
			    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x01,
			    0x11, 0x13, 0x11, 
                LocalPortBytes[1], LocalPortBytes[0],LocalIPBytes[3], LocalIPBytes[2], LocalIPBytes[1], LocalIPBytes[0], /* Local Stuff */
                0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x11
                };


               
            }
             else if (RecvPacket[0] == 0x10 && RecvPacket[1] == 0x10 && RecvPacket[2] == 0x0 && RecvPacket[14] == 0x31)
             {
                 //If Ping is 999 Make Player Visible =)
                 Response = RecvPacket;
                 ushort RoomID = BitConverter.ToUInt16(RecvPacket, 7);

                 cWRRoom r =  Program.WRServer.getRoomByID(RoomID,1);
                 if (r is cWRRoom && r.getPlayers().Count > 1)
                 {
                     foreach (cWRClient _Client in r.getPlayers())
                     {
                         UDPSocket_1.Send(RecvPacket, RecvPacket.Length, Program.WRServer.IPToAddr(_Client.nIP), Convert.ToUInt16(_Client.nPort));
                         UDPSocket_2.Send(RecvPacket, RecvPacket.Length, Program.WRServer.IPToAddr(_Client.nIP), Convert.ToUInt16(_Client.nPort));
                     }
                 }
             }
                 
                
            
            else
            {
                Response = new Byte[1] { 0x00 };
            }

            return Response;
        }
    }
}
