/// <summary>
        /// Gets the country code for a string IP address
        /// </summary>
        /// <param name="IP"></param>
        /// <returns></returns>
        public static string GetCountryCode(IPAddress IP)
        {
            // Return default config Country Code
            if (IPAddress.IsLoopback(IP) || HttpServer.LocalIPs.Contains(IP))
                return Program.Config.ASP_LocalIpCountryCode;

            try
            {
                using (DatabaseDriver Driver = new DatabaseDriver(DatabaseEngine.Sqlite, ConnectionString))
                {
                    // Fetch country code from Ip2Nation
                    Driver.Connect();
                    List<Dictionary<string, object>> Rows = Driver.Query(
                        "SELECT country FROM ip2nation WHERE ip < @P0 ORDER BY ip DESC LIMIT 1",
                        Networking.IP2Long(IP.ToString())
                    );
                    string CC = (Rows.Count == 0) ? "xx" : Rows[0]["country"].ToString();

                    // Fix country!
                    return (CC == "xx" || CC == "01") ? Program.Config.ASP_LocalIpCountryCode : CC;
                }
            }
            catch
            {
                return Program.Config.ASP_LocalIpCountryCode;
            }
        }
        /// <summary>
        /// This request provides details of the leaderboard
        /// </summary>
        /// <queryParam name="pid" type="int">The unique player ID</queryParam>
        /// <queryParam name ="nick" type="string">Unique player nickname</queryParam>
        /// <queryParam name="type" type="string">"score", "kit", "vehicle", "weapon"</queryParam>
        /// <queryParam name="id" type="int|string (score)">the various fetch variables (kit ids, vehicle id's etc etc)</queryParam>
        /// <queryParam name="before" type="int">The number of players before</queryParam>
        /// <queryParam name="after" type="int">The number of players after</queryParam>
        /// <param name="Client">The HttpClient who made the request</param>
        /// <param name="Driver">The Stats Database Driver. Connection errors are handled in the calling object</param>
        public GetLeaderBoard(HttpClient Client, StatsDatabase Database)
        {
            // Set internal variables
            this.Response = Client.Response;
            this.QueryString = Client.Request.QueryString;
            this.Driver = Database;

            // We need a type!
            if (!QueryString.ContainsKey("type"))
            {
                Response.WriteResponseStart(false);
                Response.WriteHeaderLine("asof", "err");
                Response.WriteDataLine(DateTime.UtcNow.ToUnixTimestamp(), "Invalid Syntax!");
                Response.Send();
                return;
            }

            // Setup Params
            if(QueryString.ContainsKey("pid"))
                Int32.TryParse(QueryString["pid"], out Pid);
            if (QueryString.ContainsKey("id"))
                Id = QueryString["id"];
            if (QueryString.ContainsKey("before"))
                Int32.TryParse(QueryString["before"], out Before);
            if(QueryString.ContainsKey("after"))
                Int32.TryParse(QueryString["after"], out After);
            if (QueryString.ContainsKey("pos"))
                Int32.TryParse(QueryString["pos"], out Pos);

            Min = (Pos - 1) - Before;
            Max = After + 1;

            // Do our requested Task
            switch (QueryString["type"])
            {
                case "score":
                    DoScore();
                    break;
                case "risingstar":
                    DoRisingStar();
                    break;
                case "kit":
                    DoKit();
                    break;
                case "vehicle":
                    DoVehicles();
                    break;
                case "weapon":
                    DoWeapons();
                    break;
                default:
                    //Response.HTTPStatusCode = ASPResponse.HTTPStatus.BadRequest;
                    Response.Send();
                    break;
            }
        }
        /// <summary>
        /// Creates the connection to the database, and handles
        /// the excpetion (if any) that are thrown
        /// </summary>
        public void CheckConnection()
        {
            if(Driver == null || !Driver.IsConnected)
            {
                try
                {
                    // First time connection
                    if (Driver == null)
                    {
                        Driver = new DatabaseDriver(
                            MainForm.Config.GamespyDBEngine,
                            MainForm.Config.GamespyDBHost,
                            MainForm.Config.GamespyDBPort,
                            MainForm.Config.GamespyDBName,
                            MainForm.Config.GamespyDBUser,
                            MainForm.Config.GamespyDBPass
                        );

                        // Create SQL tables on new SQLite DB's
                        if (Driver.IsNewDatabase)
                        {
                            // Connect to DB
                            Driver.Connect();
                            string SQL = Utils.GetResourceAsString("BF2Statistics.SQL.SQLite.Gamespy.sql");
                            Driver.Execute(SQL);
                            return;
                        }
                        else
                        {
                            // Connect to DB
                            Driver.Connect();
                            CheckDatabase();
                            return;
                        }
                    }

                    // Connect to DB
                    Driver.Connect();
                }
                catch (Exception E)
                {
                    string Message = "Database Connect Error: " +
                        Environment.NewLine +
                        E.Message +
                        Environment.NewLine
                        + "Forcing Server Shutdown...";
                    throw new Exception(Message);
                }
            }
        }
        //&info=per*,cmb*,twsc,cpcp,cacp,dfcp,kila,heal,rviv,rsup,rpar,tgte,dkas,dsab,cdsc,rank,cmsc,kick,kill,deth,suic,ospm,klpm,klpr,dtpr,bksk,wdsk,bbrs,tcdr,ban,dtpm,lbtl,osaa,vrk,tsql,tsqm,tlwf,mvks,vmks,mvn*,vmr*,fkit,fmap,fveh,fwea,wtm-,wkl-,wdt-,wac-,wkd-,vtm-,vkl-,vdt-,vkd-,vkr-,atm-,awn-,alo-,abr-,ktm-,kkl-,kdt-,kkd-
        public GetPlayerInfo(HttpClient Client)
        {
            // Load class Variables
            this.Response = Client.Response;
            this.QueryString = Client.Request.QueryString;
            this.Driver = ASPServer.Database.Driver;

            // Setup Params
            if (QueryString.ContainsKey("pid"))
                Int32.TryParse(QueryString["pid"], out Pid);
            if (QueryString.ContainsKey("transpose"))
                Int32.TryParse(QueryString["transpose"], out Transpose);
            if (QueryString.ContainsKey("info"))
                Info = QueryString["info"].Split(',').ToList<string>();

            // Make sure our required params are indeed passed
            if (Pid == 0 || Info.Count == 0)
            {
                Client.Response.WriteResponseStart(false);
                Client.Response.WriteHeaderLine("asof", "err");
                Client.Response.WriteDataLine(DateTime.UtcNow.ToUnixTimestamp(), "Invalid Syntax!");
                Client.Response.Send();
                return;
            }

            // Get Missing keys for a standard request
            List<string> ReqKeys = RequiredKeys.Split(',').ToList<string>();
            IEnumerable<string> Diff = from item in ReqKeys where !Info.Contains(item) select item;
            List<string> MissingKeys = new List<string>(Diff);

            // Standard BF2HQ Request
            if (MissingKeys.Count == 0)
                DoFullRequest();
            // Time Info
            else if (QueryString["info"] == "ktm-,vtm-,wtm-,mtm-")
                DoTimeRequest();
            // Map Info
            else if (QueryString["info"].StartsWith("mtm-,mwn-,mls-"))
                DoMapRequest();
            else if (QueryString["info"].StartsWith("rank") && QueryString["info"].EndsWith("vac-"))
                DoServerRequest();
            else
                Response.Send();
        }
        /// <summary>
        /// This request provides claninfo to the bf2server, to set the filter rules
        /// for players joining the server.
        /// </summary>
        /// <queryParam name="type" type="int">The Listype (whitelist, blacklist)</queryParam>
        /// <queryParam name ="clantag" type="string">Specified the required clantag</queryParam>
        /// <queryParam name="score" type="int">The minimum required score</queryParam>
        /// <queryParam name="rank" type="int">The minimum required ranked</queryParam>
        /// <queryParam name="time" type="int">The minimum required global time</queryParam>
        /// <queryParam name="kdratio" type="float">The minimum required kill/death ratio</queryParam>
        /// <queryParam name="country" type="string">The country code (Ex: us, br, no) required, seperated by comma, that is required</queryParam>
        /// <queryParam name="banned" type="int">Specified the maximum ban count to be accepted into the list</queryParam>
        /// <param name="Client">The HttpClient who made the request</param>
        /// <param name="Driver">The Stats Database Driver. Connection errors are handled in the calling object</param>
        public GetClanInfo(HttpClient Client, StatsDatabase Database)
        {
            int Type = 0;
            Dictionary<string, string> QueryString = Client.Request.QueryString;
            Driver = Database;

            // make sure we have a valid player ID
            if (!QueryString.ContainsKey("type") || !Int32.TryParse(QueryString["type"], out Type))
            {
                Client.Response.WriteResponseStart(false);
                Client.Response.WriteHeaderLine("asof", "err");
                Client.Response.WriteDataLine(DateTime.UtcNow.ToUnixTimestamp(), "Invalid Syntax!");
                Client.Response.Send();
                return;
            }

            // Filler Variables
            int I = 0;
            float F;
            string S;
            List<DbParameter> Params = new List<DbParameter>();

            // Prepare Query
            SelectQueryBuilder Query = new SelectQueryBuilder(Driver);
            Query.SelectColumns("id", "name");
            Query.SelectFromTable("player");
            Query.SetWhereOperator(LogicOperator.And);
            Query.AddWhere("ip", Comparison.NotEqualTo, "0.0.0.0");
            Query.AddOrderBy("id", Sorting.Ascending);
            WhereClause Where = null;

            switch (Type)
            {
                // Blacklist
                case 0:
                    int BanLimit = (QueryString.ContainsKey("banned") && Int32.TryParse(QueryString["banned"], out I)) ? I : 100;
                    Where = new WhereClause("banned", Comparison.GreaterOrEquals, BanLimit);
                    Where.AddClause(LogicOperator.Or, "permban", Comparison.Equals, 1);
                    break;
                // Whitelist
                case 1:
                    if (QueryString.ContainsKey("clantag"))
                    {
                        Where = new WhereClause("clantag", Comparison.Equals, QueryString["clantag"]);
                        Where.AddClause(LogicOperator.And, "permban", Comparison.Equals, 0);
                    }
                    break;
                // Greylist
                case 2:
                    // List of possible query's
                    string[] Queries = new string[] { "score", "rank", "time", "kdratio", "country", "banned" };
                    foreach (string Param in Queries)
                    {
                        if (QueryString.ContainsKey(Param))
                        {
                            switch (Param)
                            {
                                case "score":
                                case "time":
                                case "rank":
                                    if (Int32.TryParse(QueryString[Param], out I))
                                    {
                                        if (Where == null)
                                            Where = new WhereClause(Param, Comparison.GreaterOrEquals, I);
                                        else
                                            Where.AddClause(LogicOperator.And, Param, Comparison.GreaterOrEquals, I);
                                    }
                                    break;
                                case "kdratio":
                                    if (float.TryParse(QueryString["kdratio"], out F))
                                    {
                                        if (Where == null)
                                            Where = new WhereClause("(kills / deaths)", Comparison.GreaterOrEquals, F);
                                        else
                                            Where.AddClause(LogicOperator.And, "(kills / deaths)", Comparison.GreaterOrEquals, F);
                                    }
                                    break;
                                case "country":
                                    S = QueryString["country"].Replace(",", "','");
                                    if (Where == null)
                                        Where = new WhereClause(Param, Comparison.In, S.Split(','));
                                    else
                                        Where.AddClause(LogicOperator.And, Param, Comparison.In, S.Split(','));
                                    break;
                                case "banned":
                                    if (Int32.TryParse(QueryString["banned"], out I))
                                    {
                                        if(Where == null)
                                            Where = new WhereClause("banned", Comparison.LessThan, I);
                                        else
                                            Where.AddClause(LogicOperator.And,"banned", Comparison.LessThan, I);

                                        Where.AddClause(LogicOperator.And, "permban", Comparison.Equals, 0);
                                    }
                                    break;
                            }
                        }
                    }
                    break;
            }

            // Pepare 2 output headers
            int size = 0;
            FormattedOutput Output1 = new FormattedOutput("size", "asof");
            FormattedOutput Output2 = new FormattedOutput("pid", "nick");

            // Query the database, add each player to Output 2
            if(Where != null) Query.AddWhere(Where);
            List<Dictionary<string, object>> Players = Driver.ExecuteReader(Query.BuildCommand());
            foreach (Dictionary<string, object> P in Players)
            {
                size++;
                Output2.AddRow(P["id"].ToString(), P["name"].ToString());
            }

            // Send Response
            Output1.AddRow(size, DateTime.UtcNow.ToUnixTimestamp());
            Client.Response.AddData(Output1);
            Client.Response.AddData(Output2);
            Client.Response.Send();
        }
        /// <summary>
        /// Initializes a new Snapshot, with the specified Date it was Posted
        /// </summary>
        /// <param name="Snapshot">The snapshot source</param>
        /// <param name="Date">The original date in which this snapshot was created</param>
        public Snapshot(string Snapshot, DateTime Date)
        {
            // Load out database connection
            this.Driver = ASPServer.Database.Driver;
            this.Date = Date;
            this.TimeStamp = Date.ToUnixTimestamp();

            // Get our snapshot key value pairs
            string[] Data = Snapshot.Split('\\');
            Snapshot = null;

            // Check for invalid snapshot string
            if (Data.Length < 36 || Data.Length % 2 != 0)
            {
                IsValidSnapshot = false;
                return;
            }

            // Make sure we have an End of file
            if (Data[Data.Length - 2] != "EOF" && Data[Data.Length - 4] != "EOF")
            {
                IsValidSnapshot = false;
                return;
            }

            // Define if we are central update
            this.IsCentralUpdate = (Data[Data.Length - 2] == "cdb_update");

            // Server data
            this.ServerPrefix = Data[0];
            this.ServerName = Data[1];
            this.ServerPort = int.Parse(Data[3].ToString());
            this.QueryPort = int.Parse(Data[5].ToString());

            // Map Data
            this.MapName = Data[7];
            this.MapId = int.Parse(Data[9]);
            this.MapStart = (int)Math.Round(float.Parse(Data[11]), 0);
            this.MapEnd = (int)Math.Round(float.Parse(Data[13]), 0);

            // Misc Data
            this.WinningTeam = int.Parse(Data[15]);
            this.GameMode = int.Parse(Data[17]);
            this.Mod = Data[21];
            this.PlayersConnected = int.Parse(Data[23]);

            // Army Data
            this.WinningArmy = int.Parse(Data[25]);
            this.Team1Army = int.Parse(Data[27]);
            this.Team1Tickets = int.Parse(Data[29]);
            this.Team2Army = int.Parse(Data[31]);
            this.Team2Tickets = int.Parse(Data[33]);

            // Setup snapshot variables
            PlayerData = new List<Dictionary<string, string>>();
            KillData = new List<Dictionary<string, string>>();

            // Check for custom map, with no ID
            if (MapId == 99)
            {
                IsCustomMap = true;

                // Check for existing data
                List<Dictionary<string, object>> Rows = Driver.Query("SELECT id FROM mapinfo WHERE name=@P0", MapName);
                if (Rows.Count == 0)
                {
                    // Create new MapId
                    Rows = Driver.Query("SELECT MAX(id) AS id FROM mapinfo WHERE id >= " + MainForm.Config.ASP_CustomMapID);
                    MapId = (Rows.Count == 0 || String.IsNullOrWhiteSpace(Rows[0]["id"].ToString()))
                        ? MainForm.Config.ASP_CustomMapID
                        : (Int32.Parse(Rows[0]["id"].ToString()) + 1);

                    // make sure the mapid is at least the min custom map id
                    if (MapId < MainForm.Config.ASP_CustomMapID)
                        MapId = MainForm.Config.ASP_CustomMapID;

                    // Insert map data, so we dont lose this mapid we generated
                    if (Rows.Count == 0 || MapId == MainForm.Config.ASP_CustomMapID)
                        Driver.Execute("INSERT INTO mapinfo(id, name) VALUES (@P0, @P1)", MapId, MapName);
                }
                else
                    MapId = Int32.Parse(Rows[0]["id"].ToString());
            }
            else
                IsCustomMap = (MapId > MainForm.Config.ASP_CustomMapID);

            // Do player snapshots, 36 is first player
            for (int i = 36; i < Data.Length; i += 2)
            {
                string[] Parts = Data[i].Split('_');

                // Ignore uncomplete snapshots
                if (Parts.Length == 1)
                {
                    if (Parts[0] == "EOF")
                        break;
                    else
                        IsValidSnapshot = false;
                    return;
                }

                int id = int.Parse(Parts[1]);
                if (Parts[0] == "pID")
                {
                    PlayerData.Add(new Dictionary<string, string>());
                    KillData.Add(new Dictionary<string, string>());
                }

                // Kill and death data has its own array key
                if (Parts[0] == "mvks")
                    continue;

                if (Parts[0] == "mvns")
                    KillData[id].Add(Data[i + 1], Data[i + 3]);
                else
                    PlayerData[id].Add(Parts[0], Data[i + 1]);
            }

            // Set that we appear to be valid
            IsValidSnapshot = true;
        }
        /// <summary>
        /// Fethces the full country name from a country code supplied from GetCountryCode()
        /// </summary>
        /// <param name="Code"></param>
        /// <returns></returns>
        public static string GetCountyNameFromCode(string Code)
        {
            try
            {
                using (DatabaseDriver Driver = new DatabaseDriver(DatabaseEngine.Sqlite, ConnectionString))
                {
                    // Fetch country code from Ip2Nation
                    Driver.Connect();
                    List<Dictionary<string, object>> Rows = Driver.Query(
                        "SELECT country FROM ip2nationcountries WHERE iso_code_2 = @P0", Code.ToUpperInvariant()
                    );

                    return (Rows.Count == 0) ? Code: Rows[0]["country"].ToString();
                }
            }
            catch
            {
                return Code;
            }
        }
        /// <summary>
        /// This request provides details of the players unlocked weapons
        /// </summary>
        /// <queryParam name="pid" type="int">The unique player ID</queryParam>
        /// <queryParam name ="nick" type="string">Unique player nickname</queryParam>
        /// <param name="Client">The HttpClient who made the request</param>
        /// <param name="Driver">The Stats Database Driver. Connection errors are handled in the calling object</param>
        public GetUnlocksInfo(HttpClient Client, StatsDatabase Database)
        {
            // Load class Variables
            this.Response = Client.Response;
            this.Driver = Database;

            // Earned and Available Unlocks
            int HasUsed = 0;
            int Earned = 0;
            int Available = 0;

            // Get player ID
            if (Client.Request.QueryString.ContainsKey("pid"))
                Int32.TryParse(Client.Request.QueryString["pid"], out Pid);

            // Prepare Output
            Response.WriteResponseStart();
            Response.WriteHeaderLine("pid", "nick", "asof");

            // Our ourput changes based on the selected Unlocks config setting
            switch(MainForm.Config.ASP_UnlocksMode)
            {
                // Player Based - Unlocks are earned
                case 0:
                    // Make sure the player exists
                    Rows = Driver.Query("SELECT name, score, rank, availunlocks, usedunlocks FROM player WHERE id=@P0", Pid);
                    if(Rows.Count == 0)
                        goto case 2; // No Unlocks

                    // Start Output
                    Response.WriteDataLine(Pid, Rows[0]["name"].ToString().Trim(), DateTime.UtcNow.ToUnixTimestamp());

                    // Get total number of unlocks player is allowed to have givin his rank, and bonus unlocks
                    Rank = Int32.Parse(Rows[0]["rank"].ToString());
                    HasUsed = Int32.Parse(Rows[0]["usedunlocks"].ToString());
                    Available = Int32.Parse(Rows[0]["availunlocks"].ToString());
                    Earned = GetBonusUnlocks();

                    // Determine total unlocks available, based on what he has earned, minus what he has used already
                    Rows = Driver.Query("SELECT COUNT(id) AS count FROM unlocks WHERE id = @P0 AND state = 's'", Pid);
                    int Used = Int32.Parse(Rows[0]["count"].ToString());
                    Earned -= Used;

                    // Update database if the database is off
                    if (Earned != Available || HasUsed != Used)
                        Driver.Execute("UPDATE player SET availunlocks = @P0, usedunlocks = @P1 WHERE id = @P2", Earned, Used, Pid);

                    // Output more
                    Response.WriteHeaderLine("enlisted", "officer");
                    Response.WriteDataLine(Earned, 0);
                    Response.WriteHeaderLine("id", "state");

                    // Add each unlock's state
                    Dictionary<string, bool> Unlocks = new Dictionary<string, bool>();
                    Rows = Driver.Query("SELECT kit, state FROM unlocks WHERE id=@P0 ORDER BY kit ASC", Pid);
                    if (Rows.Count == 0)
                    {
                        // Create Player Unlock Data
                        StringBuilder Query = new StringBuilder("INSERT INTO unlocks VALUES ");

                        // Normal unlocks
                        for (int i = 11; i < 100; i += 11)
                        {
                            // 88 and above are Special Forces unlocks, and wont display at all if the base unlocks are not earned
                            if (i < 78 )
                                Response.WriteDataLine(i, "n");
                            Query.AppendFormat("({0}, {1}, 'n'), ", Pid, i);
                        }

                        // Sf Unlocks, Dont display these because thats how Gamespy does it
                        for (int i = 111; i < 556; i += 111)
                        {
                            Query.AppendFormat("({0}, {1}, 'n')", Pid, i);
                            if (i != 555)
                                Query.Append(", ");
                        }

                        // Do Insert
                        Driver.Execute(Query.ToString());
                    }
                    else
                    {
                        foreach (Dictionary<string, object> Unlock in Rows)
                        {
                            // Add unlock to output if its a base unlock
                            int Id = Int32.Parse(Unlock["kit"].ToString());
                            if (Id < 78)
                                Response.WriteDataLine(Unlock["kit"], Unlock["state"]);

                            // Add Unlock to list
                            Unlocks.Add(Unlock["kit"].ToString(), (Unlock["state"].ToString() == "s"));
                        }

                        // Add SF Unlocks... We need the base class unlock unlocked first
                        CheckUnlock(88, 22, Unlocks);
                        CheckUnlock(99, 33, Unlocks);
                        CheckUnlock(111, 44, Unlocks);
                        CheckUnlock(222, 55, Unlocks);
                        CheckUnlock(333, 66, Unlocks);
                        CheckUnlock(444, 11, Unlocks);
                        CheckUnlock(555, 77, Unlocks);
                    }
                    break;

                // All Unlocked
                case 1:
                    Response.WriteDataLine(Pid, "All_Unlocks", DateTime.UtcNow.ToUnixTimestamp());
                    Response.WriteHeaderLine("enlisted", "officer");
                    Response.WriteDataLine(0, 0);
                    Response.WriteHeaderLine("id", "state");
                    for (int i = 11; i < 100; i += 11)
                        Response.WriteDataLine(i, "s");
                    for (int i = 111; i < 556; i += 111)
                        Response.WriteDataLine(i, "s");
                    break;

                // Unlocks Disabled
                case 2:
                default:
                    Response.WriteDataLine(Pid, "No_Unlocks", DateTime.UtcNow.ToUnixTimestamp());
                    Response.WriteHeaderLine("enlisted", "officer");
                    Response.WriteDataLine(0, 0);
                    Response.WriteHeaderLine("id", "state");
                    for (int i = 11; i < 78; i += 11)
                        Response.WriteDataLine(i, "n");
                    break;
            }

            // Send Response
            Response.Send();
        }
        /// <summary>
        /// Creates the connection to the database, and handles
        /// the excpetion (if any) that are thrown
        /// </summary>
        /// <summary>
        /// Creates the connection to the database, and handles
        /// the excpetion (if any) that are thrown
        /// </summary>
        public void CheckConnection()
        {
            if (Driver == null || !Driver.IsConnected)
            {
                try
                {
                    // First time connection
                    if (Driver == null)
                    {
                        // Create database connection
                        Driver = new DatabaseDriver(
                            MainForm.Config.StatsDBEngine,
                            MainForm.Config.StatsDBHost,
                            MainForm.Config.StatsDBPort,
                            MainForm.Config.StatsDBName,
                            MainForm.Config.StatsDBUser,
                            MainForm.Config.StatsDBPass
                        );
                        Driver.Connect();

                        // Create SQL tables on new SQLite DB's
                        if (Driver.IsNewDatabase)
                        {
                            CreateSqliteTables(Driver);
                            return;
                        }
                        else
                        {
                            // Try and get database version
                            try
                            {
                                var Rows = Driver.Query("SELECT dbver FROM _version LIMIT 1");
                                if (Rows.Count == 0)
                                    throw new Exception(); // Force insert of IP2Nation
                            }
                            catch
                            {
                                // Table doesnt contain a _version table, so run the createTables.sql
                                if (Driver.DatabaseEngine == DatabaseEngine.Sqlite)
                                    CreateSqliteTables(Driver);
                                else
                                    CreateMysqlTables(Driver);
                            }

                            return;
                        }
                    }

                    // Connect to DB
                    Driver.Connect();

                    // Set global packet size with MySql
                    if (Driver.DatabaseEngine == DatabaseEngine.Mysql)
                        Driver.Execute("SET GLOBAL max_allowed_packet=51200");
                }
                catch (Exception E)
                {
                    throw new Exception(
                        "Database Connect Error: " +
                        Environment.NewLine +
                        E.Message +
                        Environment.NewLine +
                        "Forcing Server Shutdown..."
                    );
                }
            }
        }
        /// <summary>
        /// On a new Sqlite database, this method will create the default tables
        /// </summary>
        /// <param name="Driver"></param>
        private void CreateSqliteTables(DatabaseDriver Driver)
        {
            // Show Progress Form
            MainForm.Disable();
            bool TaskFormWasOpen = TaskForm.IsOpen;
            if(!TaskFormWasOpen)
                TaskForm.Show(MainForm.Instance, "Create Database", "Creating Bf2Stats SQLite Database...", false);

            // Create Tables
            TaskForm.UpdateStatus("Creating Stats Tables");
            string SQL = Utils.GetResourceAsString("BF2Statistics.SQL.SQLite.Stats.sql");
            Driver.Execute(SQL);

            // Insert Ip2Nation data
            TaskForm.UpdateStatus("Inserting Ip2Nation Data");
            SQL = Utils.GetResourceAsString("BF2Statistics.SQL.Ip2nation.sql");
            DbTransaction Transaction = Driver.BeginTransaction();
            Driver.Execute(SQL);

            // Attempt to do the transaction
            try
            {
                Transaction.Commit();
            }
            catch (Exception E)
            {
                Transaction.Rollback();
                if(!TaskFormWasOpen)
                    TaskForm.CloseForm();
                MainForm.Enable();
                throw E;
            }

            // Close update progress form
            if(!TaskFormWasOpen) TaskForm.CloseForm();
            MainForm.Enable();
        }
        /// <summary>
        /// On a new Mysql database, this method will create the default tables
        /// </summary>
        /// <param name="Driver"></param>
        private void CreateMysqlTables(DatabaseDriver Driver)
        {
            // Show Progress Form
            MainForm.Disable();
            bool TaskFormWasOpen = TaskForm.IsOpen;
            if (!TaskFormWasOpen)
                TaskForm.Show(MainForm.Instance, "Create Database", "Creating Bf2Stats Mysql Tables...", false);

            // To prevent packet size errors
            Driver.Execute("SET GLOBAL max_allowed_packet=51200");

            // Start Transaction
            DbTransaction Transaction = Driver.BeginTransaction();
            TaskForm.UpdateStatus("Creating Stats Tables");

            // Gets Table Queries
            string[] SQL = Utils.GetResourceFileLines("BF2Statistics.SQL.MySQL.Stats.sql");
            List<string> Queries = Utilities.Sql.ExtractQueries(SQL);

            // Attempt to do the transaction
            try
            {
                // Create Tables
                foreach (string Query in Queries)
                    Driver.Execute(Query);

                // Commit
                Transaction.Commit();
            }
            catch (Exception E)
            {
                Transaction.Rollback();
                if (!TaskFormWasOpen)
                    TaskForm.CloseForm();
                MainForm.Enable();
                throw E;
            }

            // Insert Ip2Nation data
            Transaction = Driver.BeginTransaction();
            TaskForm.UpdateStatus("Inserting Ip2Nation Data");
            SQL = Utils.GetResourceFileLines("BF2Statistics.SQL.Ip2nation.sql");
            Queries = Utilities.Sql.ExtractQueries(SQL);

            // Attempt to do the transaction
            try
            {
                // Insert rows
                foreach (string Query in Queries)
                    Driver.Execute(Query);

                // Commit
                Transaction.Commit();
            }
            catch (Exception E)
            {
                Transaction.Rollback();
                if(!TaskFormWasOpen)
                    TaskForm.CloseForm();
                MainForm.Enable();
                throw E;
            }

            // Close update progress form
            if (!TaskFormWasOpen) TaskForm.CloseForm();
            MainForm.Enable();
        }
        /// <summary>
        /// Clears all stats data from the stats database
        /// </summary>
        public void Truncate()
        {
            // Sqlite Database doesnt have a truncate method, so we will just recreate the database
            if (Driver.DatabaseEngine == DatabaseEngine.Sqlite)
            {
                // Stop the server to delete the file
                ASPServer.Stop();
                File.Delete(Path.Combine(MainForm.Root, MainForm.Config.StatsDBName + ".sqlite3"));
                System.Threading.Thread.Sleep(500); // Make sure the file deletes before the ASP server starts again!

                // Reset driver, start ASP again
                Driver = null;
                ASPServer.Start();
            }
            else
            {
                // Use MySQL's truncate method to clear the tables;
                foreach (string Table in StatsTables)
                    Driver.Execute("TRUNCATE TABLE " + Table);
            }
        }