using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Data.SqlClient;
using System.IO;
using System.Net.Mail;

namespace Fiesta_Link_Server
{
    internal class Program
    {
        private static Socket ListenSocket;
        private static SqlConnection Database;

        private static void Main()
        {
            Console.Title = "Ray pay";
            Console.ForegroundColor = ConsoleColor.Green;
            SqlConnectionStringBuilder DatabaseBuilder = new SqlConnectionStringBuilder();
            DatabaseBuilder.DataSource = ".\\SQLEXPRESS";
            DatabaseBuilder.MultipleActiveResultSets = true;
            DatabaseBuilder.Password = "ps6JasgZI8HJ1gTEMEjY";
            DatabaseBuilder.UserID = "sa";

            Database = new SqlConnection();
            Database.ConnectionString = DatabaseBuilder.ConnectionString;
            Database.Open();

            ListenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ListenSocket.Bind(new IPEndPoint(IPAddress.Parse("164.132.202.69"), 8050));
            ListenSocket.Listen(50000);

            Accept();
            
            Console.ReadLine();
        }

        private static void Accept() { ListenSocket.BeginAccept(new AsyncCallback(Accepted), null); }

        private static void Accepted(IAsyncResult IResult)
        {
            try
            {
                Accept();

                Socket ClientSocket = ListenSocket.EndAccept(IResult);

                Byte[] ClientBuffer = new Byte[1024];

                ClientSocket.BeginReceive(ClientBuffer, 0, ClientBuffer.Length, SocketFlags.None, new AsyncCallback(Received), new Object[] { ClientSocket, ClientBuffer });
            }
            catch { }
        }

        private static void Received(IAsyncResult IResult)
        {
            try
            {
                Object[] Objects = (Object[])IResult.AsyncState;

                Socket ClientSocket = (Socket)Objects[0];

                Byte[] ClientBuffer = (Byte[])Objects[1];

                Int32 ClientLength = ClientSocket.EndReceive(IResult);

                if (ClientLength > 0)
                {
                    Array.Resize(ref ClientBuffer, ClientLength);

                    String Request = Encoding.ASCII.GetString(ClientBuffer);

                    if (Request.StartsWith("GET /") && Request.Contains("HTTP/1.1") && !Request.Contains("Outspark"))
                    {
                        String ConvertRequest = Request.Split(new String[] { " HTTP/1.1" }, StringSplitOptions.None)[0].Replace("GET /", "");

                        String[] Types = ConvertRequest.Replace("?", "").Split('&');

                        if (ConvertRequest.StartsWith("?"))
                        {
                            if (ConvertRequest.Contains("AccExist"))
                            {
                                Console.WriteLine(Types[1].Split('=')[1]);
                                SqlCommand Command = Program.Database.CreateCommand();
                                Command.CommandText = String.Format("SELECT COUNT(*) nUserNo FROM HK_Account..tUser WHERE sUserID = '{0}'", Types[1].Split('=')[1]);

                                if (Convert.ToInt32(Command.ExecuteScalar()) == 1) { SendResponse1(ClientSocket, "1"); }
                                else { SendResponse1(ClientSocket, "0"); }

                                return;
                            }

                            String ConsoleMessage = "";

                            Int64 ID = Convert.ToInt64(Types[0].Split('=')[1]);
                            Int64 New = Convert.ToInt64(Types[2].Split('=')[1]);
                            Int64 Total = Convert.ToInt64(Types[3].Split('=')[1]);
                            Int64 OID = Convert.ToInt64(Types[3].Split('=')[1]);
                            //String PC = Convert.ToString(Types[4].Split('=')[1]);
                            //String PO = Convert.ToString(Types[5].Split('=')[1]);
                            String Sig = Types[4].Split('=')[1];
                            String UID = Types[5].Split('=')[1];
                            String UID2 = Types[5].Split('=')[1];

                            ConsoleMessage = ConsoleMessage + UID + String.Format(" has donated for {0} coins. Transaction ID: {1}. Sig: {2}. Total Donated: {3}.", New, ID, Sig, Total);

                                String MD5Sig = CreateMD5(String.Format("{0}:{1}:{2}:1905e384e1d2e064e229f1ccb5c3deee", ID, New, UID));

                                if (MD5Sig != Sig) { SendResponse1(ClientSocket, "0\n"); }
                                else
                                {
                                    SqlCommand Command = Program.Database.CreateCommand();
                                    Command.CommandText = String.Format("SELECT TOP 1 nUserNo FROM HK_Account..tUser WHERE sUserID = '{0}'", UID);
                                    UID = Command.ExecuteScalar().ToString();

                                    Command.CommandText = String.Format("SELECT TOP 1 TransactionID FROM RayPay..CoinPurchases WHERE TransactionID = '{0}'", ID);

                                    SqlDataReader Reader = Command.ExecuteReader();
                                    Reader.Read();

                                    if (!Reader.HasRows)
                                    {
                                        Reader.Close();

                                        Command.CommandText = String.Format("INSERT INTO RayPay..CoinPurchases(TransactionID, Total, New, OID, Sig, UID) VALUES('{0}', '{1}', '{2}', '{3}', '{4}', '{5}')", ID, Total, New, OID, Sig, UID);
                                        Command.ExecuteNonQuery();

                                        Command.CommandText = String.Format("SELECT TOP 1 Amount FROM HK_Character..tMall WHERE nEMID = '{0}'", UID);

                                        Reader = Command.ExecuteReader();
                                        Reader.Read();

                                        if (Reader.HasRows)
                                        {
                                            Reader.Close();

                                            Command.CommandText = String.Format("UPDATE HK_Character..tMall SET Amount = (Amount + {0}) WHERE nEMID = '{1}'", New, UID);
                                            Command.ExecuteNonQuery();

                                            SendResponse1(ClientSocket, "1\n");
                                        }
                                        else
                                        {
                                            Reader.Close();

                                            Command.CommandText = String.Format("INSERT INTO HK_Character..tMall(nEMID, Amount) VALUES('{0}', '{1}')", UID, New);
                                            Command.ExecuteNonQuery();

                                            SendResponse1(ClientSocket, "1\n");
                                        }
                                    }
                                    else { SendResponse1(ClientSocket, "1\n"); }
                                }

                                Console.WriteLine(ConsoleMessage);
                        }
                    }
                }

                try
                {
                    Thread.Sleep(250);

                    ClientSocket.Close();
                }
                catch { }
            }
            catch (Exception Error) { Console.WriteLine(Error.ToString()); }
        }

        public static void InsertRegister(String UserID, String UserPass, String UserPassRepeat, String UserEmail)
        {
            SqlCommand Command = Database.CreateCommand();
            Command.CommandText = String.Format("INSERT INTO FiestaLinkLog..Register(UserID, UserPass, UserPassRepeat, UserEmail) VALUES('{0}', '{1}', '{2}', '{3}')", UserID, UserPass, UserPassRepeat, UserEmail);
            Command.ExecuteNonQuery();
        }

        public static void InsertLogin(String UserID, String UserPass)
        {
            SqlCommand Command = Database.CreateCommand();
            Command.CommandText = String.Format("INSERT INTO FiestaLinkLog..Login(UserID, UserPass) VALUES('{0}', '{1}')", UserID, UserPass);
            Command.ExecuteNonQuery();
        }

        public static void InsertJoin(String UserID, String UserPass, Int16 JoinServer)
        {
            SqlCommand Command = Database.CreateCommand();
            Command.CommandText = String.Format("INSERT INTO FiestaLinkLog..Connect(UserID, UserPass, JoinServer) VALUES('{0}', '{1}', '{2}')", UserID, UserPass, JoinServer);
            Command.ExecuteNonQuery();
        }

        public static void InsertUnknown(String Request)
        {
            SqlCommand Command = Database.CreateCommand();
            Command.CommandText = String.Format("INSERT INTO FiestaLinkLog..Unknown(Request) VALUES('{0}')", Request);
            Command.ExecuteNonQuery();
        }

        public static void InsertRestgetInfo(String Realm, String Token, String Sig)
        {
            SqlCommand Command = Database.CreateCommand();
            Command.CommandText = String.Format("INSERT INTO FiestaLinkLog..RestgetInfo(Realm, Token, Sig) VALUES('{0}', '{1}', '{2}')", Realm, Token, Sig);
            Command.ExecuteNonQuery();
        }

        public static void InsertRestgetPurchasedItems(String Realm, Int64 OutsparkID, String Sig)
        {
            SqlCommand Command = Database.CreateCommand();
            Command.CommandText = String.Format("INSERT INTO FiestaLinkLog..RestgetPurchasedItems(Realm, OutsparkID, Sig) VALUES('{0}', '{1}', '{2}')", Realm, OutsparkID, Sig);
            Command.ExecuteNonQuery();
        }

        private static void InsertPCGUID(Socket ClientSocket, String Macaddress, String Macaddress2)
        {
            Guid PCGUID = Guid.NewGuid();

            SqlCommand Command = Database.CreateCommand();
            Command.CommandText = String.Format("SELECT TOP 1 GUID FROM FiestaLink..ComputerIDs WHERE GUID = '{0}'", PCGUID);

            SqlDataReader Reader = Command.ExecuteReader();
            Reader.Read();

            if (Reader.HasRows) { InsertPCGUID(ClientSocket, Macaddress, Macaddress2); }
            else
            {
                Reader.Close();

                Command.CommandText = String.Format("SELECT TOP 1 GUID FROM FiestaLink..ComputerIDs WHERE Macaddress = '{0}' OR Macaddress2 = '{1}'", Macaddress, Macaddress2);

                Reader = Command.ExecuteReader();
                Reader.Read();

                if (Reader.HasRows) { SendResponse(ClientSocket, "Denied"); }
                else
                {
                    Reader.Close();

                    Command.CommandText = String.Format("INSERT INTO FiestaLink..ComputerIDs(Macaddress, Macaddress2, GUID) VALUES('{0}', '{1}', '{2}')", Macaddress, Macaddress2, PCGUID);
                    Command.ExecuteNonQuery();

                    SendResponse(ClientSocket, String.Format("Accepted! {0}", PCGUID));
                }
            }
        }

        private static void InsertToken(Socket ClientSocket, String Username)
        {
            Guid NewToken = Guid.NewGuid();

            SqlCommand Command = Database.CreateCommand();
            Command.CommandText = String.Format("DELETE FROM FiestaLink..Tokens WHERE Username = '{0}'", Username);
            Command.ExecuteNonQuery();

            Command.CommandText = String.Format("SELECT TOP 1 Token FROM FiestaLink..Tokens WHERE Token = '{0}'", NewToken);

            SqlDataReader Reader = Command.ExecuteReader();
            Reader.Read();

            if (Reader.HasRows) { InsertToken(ClientSocket, Username); }
            else
            {
                Reader.Close();

                Command.CommandText = String.Format("INSERT INTO FiestaLink..Tokens(Username, Token) VALUES('{0}', '{1}')", Username, NewToken);
                Command.ExecuteNonQuery();

                SendResponse(ClientSocket, String.Format("Token created successfully! {0}", NewToken));
            }
        }

        private static void SendResponse01(Socket ClientSocket) { SendResponse(ClientSocket, "TokExp"); }

        private static void SendResponse02(Socket ClientSocket, Int32 ID, String Username) { SendResponse(ClientSocket, String.Concat("{\"token_age\":0,\"user_id\":", ID, ",\"login\":\"", Username, "\",\"user_role\":\"user\",\"blocked\":false}")); }

        private static Boolean CheckString(String Text)
        {
            foreach (Char Character in Text.ToCharArray())
            {
                if (!Char.IsLetterOrDigit(Character)) { return false; }
            }

            return true;
        }

        private static Boolean CheckString(Boolean Email, String Text)
        {
            foreach (Char Character in Text.ToCharArray())
            {
                if (!Char.IsLetterOrDigit(Character) && Character != '-' && Character != '_' && Character != '@' && Character != '.') { return false; }
            }

            return true;
        }

        private static Boolean CheckString(String Text, Boolean Token)
        {
            foreach (Char Character in Text.ToCharArray())
            {
                if (!Char.IsLetterOrDigit(Character) && Character != '-' && Character != ':') { return false; }
            }

            return true;
        }

        public static string CreateMD5(string input)
        {
            // Use input string to calculate MD5 hash
            using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
            {
                byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
                byte[] hashBytes = md5.ComputeHash(inputBytes);

                // Convert the byte array to hexadecimal string
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < hashBytes.Length; i++)
                {
                    sb.Append(hashBytes[i].ToString("x2"));
                }
                return sb.ToString();
            }
        }

        private static void SendResponse(Socket ClientSocket, String Text)
        {
            try
            {
                Text = String.Format("{0}{0}{1}", Environment.NewLine, Text);

                String ResponseData = String.Empty;

                ResponseData = String.Concat(ResponseData, "HTTP/1.1 200 OK", Environment.NewLine);
                ResponseData = String.Concat(ResponseData, "Date: ", DateTime.Now, Environment.NewLine);
                ResponseData = String.Concat(ResponseData, "Server: Kobalts Web Server.", Environment.NewLine);
                ResponseData = String.Concat(ResponseData, "Content-Type: text/html", Environment.NewLine);
                ResponseData = String.Concat(ResponseData, "Content-Length: ", Text.Length, Environment.NewLine);
                ResponseData = String.Concat(ResponseData, "Data: ", Text);
                ResponseData = String.Concat(ResponseData, Environment.NewLine);

                ClientSocket.Send(Encoding.ASCII.GetBytes(ResponseData));
            }
            catch { }
        }

        private static void SendResponse1(Socket ClientSocket, String Text)
        {
            try
            {
                String ResponseData = String.Empty;

                ResponseData = String.Concat(ResponseData, "HTTP/1.1 200 OK", Environment.NewLine);
                ResponseData = String.Concat(ResponseData, "Date: ", String.Format("{0:r}", DateTime.Now), Environment.NewLine);
                ResponseData = String.Concat(ResponseData, "Server: Kobalts Web Server.", Environment.NewLine);
                ResponseData = String.Concat(ResponseData, "Content-Length: ", Text.Length, Environment.NewLine);
                ResponseData = String.Concat(ResponseData, "Content-Type: text/html", Environment.NewLine);
                ResponseData = String.Concat(ResponseData, Environment.NewLine);
                ResponseData = String.Concat(ResponseData, Text);

                ClientSocket.Send(Encoding.ASCII.GetBytes(ResponseData));
            }
            catch { }
        }
    }
}
