/*
    LFSLapper, Insim Race and qualification Manager for Live For Speed Game
    Copyright (C) 2007  Robert B. alias Gai-Luron and Monkster: lfsgailuron@free.fr

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using WebCamService;
using System.Threading;
using System.Diagnostics;
using System.IO;
using LapperThreads;
using System.Data;
using Mono.Data.SqliteClient;

namespace LFS
{

    public class PlayerStats
    {
        public int laps;
        public long personalBestLapTime;
        public string userName;
        public string nickName;
        public string datePb;
        public string timePb;
        public long[] splitTime = new long[(int)lexCfg.paramLapper.maxSplit];
        public long[] PBBestSplitDiff = new long[(int)lexCfg.paramLapper.maxSplit];
        public long PBBestSplitDiffLast;
        public PlayerStats(string uN, string nN, int laps, string dPb, string tPb, long pb, long[] s, long[] PBBestSplitDiff, long PBBestSplitDiffLast )
        {
            this.userName = uN;
            this.nickName = nN;
            this.laps = laps;
            this.datePb = dPb;
            this.timePb = tPb;
            //            datePb = System.DateTime.Now.ToShortDateString();
            //            timePb = System.DateTime.Now.ToShortTimeString();
            this.personalBestLapTime = pb;
            for (int i = 0; i < (int)lexCfg.paramLapper.maxSplit; i++)
                this.splitTime[i] = s[i];
            for (int i = 0; i < (int)lexCfg.paramLapper.maxSplit; i++)
                this.PBBestSplitDiff[i] = PBBestSplitDiff[i];
            this.PBBestSplitDiffLast = PBBestSplitDiffLast;

        }
    }

    public class Db
    {

        System.Collections.Hashtable trackTable = new System.Collections.Hashtable();
        string filepath = null;
        string FtpServer = null;
        string FtpLogin = null;
        string FtpPasswd = null;
        string FtpRemotePath = null;
        IDbConnection dbcon;
        IDbCommand dbcmd;
        ThreadUpload threadHandle;
        LapperThreads.clSyncUploadDownload threadSyncUpload;
        Mutex myMutex;
        string curVersion = "USERNAME 02";
        
        public Db( Mutex mut )
        {
            //Process myProcess = System.Diagnostics.Process.GetCurrentProcess();
            //            this.stringMutex = "LFSDb" + myProcess.Id.ToString();
            this.myMutex = mut;
        }
        string replace_group_car(string cars ){
            cars = cars.Replace("TBO", "XRT+RB4+FXO");
            cars = cars.Replace("LRF", "LX6+RAC+FZ5");
            cars = cars.Replace("GTR", "FXR+XRR+FZR");
            return cars;
        }
        public bool Load(string filepath, string FtpServer, string FtpLogin, string FtpPasswd, string FtpRemotePath, bool modeSync, string SyncID, string SyncDir, string[] SyncIDsToSync )
        {
            this.filepath = filepath;
            this.FtpServer = FtpServer;
            this.FtpLogin = FtpLogin;
            this.FtpPasswd = FtpPasswd;
            this.FtpRemotePath = FtpRemotePath;
            string sql;


            this.threadHandle = new ThreadUpload(FtpServer, FtpRemotePath, FtpLogin, FtpPasswd, filepath, myMutex);
            if (modeSync)
            {
                this.threadSyncUpload = new clSyncUploadDownload(SyncID, SyncDir, SyncIDsToSync);
                Thread syncUpload = new Thread(new ThreadStart(threadSyncUpload.SendInfoToSync));
                syncUpload.Start();
            }
            string connectionString = "URI=file:" + filepath + ".db,version=3";
            IDbConnection dbcon;

            dbcon = (IDbConnection)new SqliteConnection(connectionString);
            dbcon.Open();
            dbcmd = dbcon.CreateCommand();
            dbcmd.CommandText = "CREATE TABLE fi_pb "
                    + "("
                        + "username varchar(24)"
                        + ",nickname varchar(24)"
                        + ",laps integer(4)"
                        + ",PBDate varchar(10)"
                        + ",PBTime varchar(5)"
                        + ",CName varchar(3)"
                        + ",PBLTime int(4)"
                        + ",TName varchar(4)"
                        + ",PBSplit1 int(4)"
                        + ",PBSplit2 int(4)"
                        + ",PBSplit3 int(4)"
                        + ",PBSectorSplit1 int(4)"
                        + ",PBSectorSplit2 int(4)"
                        + ",PBSectorSplit3 int(4)"
                        + ",PBSectorLast int(4)"
                        + ",UNIQUE( TName,CName,username )"
                    + ")";
            try
            {
                dbcmd.ExecuteNonQuery();
            }
            catch { }

        fin_load:
            return true;
        }

        public bool Save()
        {
            try
            {
                //Exclude Concurrent access to same file when upload to FTP
                if (myMutex.WaitOne())
                {
                    using (System.IO.StreamWriter sw = new System.IO.StreamWriter(filepath))
                    {
                        sw.WriteLine( curVersion );
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("(" + filepath + ")" + ex.Message);   // Print the error message.
                return false;
            }
            finally
            {
                myMutex.ReleaseMutex();
            }
            if (FtpServer != "")
            {
                Thread ftpUpload = new Thread(new ThreadStart(threadHandle.UpLoad));
                ftpUpload.Start();
            }
            return true;
        }

        public void updateRow( LFSLapper.infoPlayer currInfoPlayer, string trackName ,bool modeSync,string SyncID,string SyncDir, string[] SyncIDsToSync)
        {
            updateRow(currInfoPlayer.userName,
                    currInfoPlayer.nickName,
                    currInfoPlayer.laps,
                    currInfoPlayer.PBDate,
                    currInfoPlayer.PBTime,
                    currInfoPlayer.CName,
                    currInfoPlayer.PBLTime,
                    trackName,
                    currInfoPlayer.PBSplit,
                    currInfoPlayer.PBBestSplitDiff,
                    currInfoPlayer.PBBestSplitDiffLast,
                    false
            );
// Push Info for sync
            if ( currInfoPlayer.PBLTime != 0 && modeSync )
                threadSyncUpload.pushInfoToSync(currInfoPlayer,trackName);
 
        }
        public void updateRow(string userName, string nickName, int laps, string datePb, string timePb, 
            string carName, long timeLap, string trackName, long[] split, 
            long[] PBBestSplitDiff, long PBBestSplitDiffLast, bool isFusion )
        {
            dbcmd.CommandText = "BEGIN TRANSACTION";
            dbcmd.ExecuteNonQuery();

            dbcmd.CommandText = "UPDATE fi_db SET "
                                    + "username = \"" + userName + "\""
                                    + ",nickname = \"" + nickName + "\""
                                    + ",laps = " + laps.ToString()
                                    + ",PBDate = \"" + datePb + "\""
                                    + ",PBTime = \"" + timePb + "\""
                                    + ",CName = \"" + carName + "\""
                                    + ",PBLtime = " + timeLap.ToString()
                                    + ",TName = \"" + trackName + "\""
                                    + ",PBSplit1 = " + split[0].ToString()
                                    + ",PBSplit2 = " + split[1].ToString()
                                    + ",PBSplit3 = " + split[2].ToString()
                                    + ",PBSectorSplit1 = " + PBBestSplitDiff[0].ToString()
                                    + ",PBSectorSplit2 = " + PBBestSplitDiff[1].ToString()
                                    + ",PBSectorSplit3 = " + PBBestSplitDiff[2].ToString()
                                    + ",PBSectorLast = " + PBBestSplitDiffLast.ToString()

                                    ;
            if (dbcmd.ExecuteNonQuery() != 1)
            {
                dbcmd.CommandText = "INSERT INTO fi_db "
                                        + "("
                                        + " username,nickname,laps"
                                        + ",PBDate,PBTime,CName,PBLtime,TName,PBSplit1,PBSplit2,PBSplit3"
                                        + ",PBSectorSplit1,PBSectorSplit2,PBSectorSplit3"
                                        + ",PBSectorLast"
                                    + ")"
                                    + " VALUES "
                                    +"("
                                    + ")"
                    ;
            }

            dbcmd.CommandText = "COMMIT TRANSACTION";
            dbcmd.ExecuteNonQuery();

            // create new track table if it doesnt exist yet
            if (!trackTable.ContainsKey(trackName))
                trackTable[trackName] = new System.Collections.Hashtable();

            System.Collections.Hashtable carTable = trackTable[trackName] as System.Collections.Hashtable;

            // create new car table if it doesnt exist yet
            if (!carTable.ContainsKey(carName))
                carTable[carName] = new System.Collections.Hashtable();

            System.Collections.Hashtable playerTable = carTable[carName] as System.Collections.Hashtable;
            string key = userName.ToLower();
            if (isFusion && playerTable.ContainsKey(key))
            {
                PlayerStats p = (PlayerStats)playerTable[key];
                if (laps > p.laps)
                    p.laps = laps;
                if (timeLap < p.personalBestLapTime)
                {
                    p.personalBestLapTime = timeLap;
                    p.timePb = timePb;
                    p.datePb = datePb;
                    p.splitTime[0] = split[0];
                    p.splitTime[1] = split[1];
                    p.splitTime[2] = split[2];
                }
                for (int i = 0; i < 3; i++)
                {
                    if ((PBBestSplitDiff[i] != 0 && PBBestSplitDiff[i] < p.PBBestSplitDiff[i]) || p.PBBestSplitDiff[i] == 0)
                        p.PBBestSplitDiff[i] = PBBestSplitDiff[i];
                }
                if ((PBBestSplitDiffLast != 0 && PBBestSplitDiffLast < p.PBBestSplitDiffLast) || p.PBBestSplitDiffLast == 0)
                    p.PBBestSplitDiffLast = PBBestSplitDiffLast;
            }
            else
            {
                // Save Only if One Lap are do
                if (timeLap != 0)
                    playerTable[key] = new PlayerStats(userName, nickName, laps, datePb, timePb, timeLap, split, PBBestSplitDiff, PBBestSplitDiffLast);


            }
        }
        public PlayerStats retreiveRow(string userName, string carName, string trackName)
        {
            if (trackTable.ContainsKey(trackName))
            {
                System.Collections.Hashtable carTable = trackTable[trackName] as System.Collections.Hashtable;
                if (carTable.ContainsKey(carName))
                {
                    System.Collections.Hashtable playerTable = carTable[carName] as System.Collections.Hashtable;
                    if (playerTable.ContainsKey(userName.ToLower()))
                    {
                        return (PlayerStats)playerTable[userName.ToLower()];
                    }
                }
            }
            return null;

        }
        public long retreivePB(string userName, string carName, string trackName)
        {
            if (trackTable.ContainsKey(trackName))
            {
                System.Collections.Hashtable carTable = trackTable[trackName] as System.Collections.Hashtable;
                if (carTable.ContainsKey(carName))
                {
                    System.Collections.Hashtable playerTable = carTable[carName] as System.Collections.Hashtable;
                    if (playerTable.ContainsKey(userName.ToLower()))
                    {
                        PlayerStats p;
                        p = (PlayerStats)playerTable[userName.ToLower()];
                        return( p.personalBestLapTime );
                    }
                }
            }
            return -1;
        }

        public class DriverLapEntry : System.IComparable
        {
            public string userName;
            public string carName;
            public int group;
            public PlayerStats playerStats;

            public DriverLapEntry(string user, PlayerStats ps, string cnam)
            {
                userName = user;
                group = -1;
                playerStats = ps;
                carName = cnam;
            }

            public int CompareTo(object x)
            {
                if ((x as DriverLapEntry).playerStats.personalBestLapTime < playerStats.personalBestLapTime)
                    return 1;
                else if ((x as DriverLapEntry).playerStats.personalBestLapTime > playerStats.personalBestLapTime)
                    return -1;
                else
                    return 0;
            }

        }
        public System.Collections.ArrayList MkGroup(int MaxGroupQual, int MaxUserGroupQual, int MinUserGroupQual, int NbRacer)
        {
        
        System.Collections.ArrayList group = new System.Collections.ArrayList();

	    int NbGroup =  NbRacer/MaxUserGroupQual;
	    int reste = NbRacer - ( NbGroup * MaxUserGroupQual );
//        Console.WriteLine("NbGroup:" + NbGroup + " Reste:" + reste);
	    if( NbGroup >= MaxGroupQual ){
		    NbGroup = MaxGroupQual-1;
		    reste = MaxUserGroupQual;
	    }
// Mettre le nombre de pilote pour chaque poule
        int i;
	    for( i = 0; i < NbGroup;i++ )
		    group.Add( MaxUserGroupQual );
        if (reste != 0)
            group.Add(reste);

// Equilibration des poules
	    if( (reste < MinUserGroupQual) && (NbGroup > 0) && (reste != 0)){
//            Console.WriteLine(group[i]);
            group[i] = MinUserGroupQual;
		    int diff = MinUserGroupQual - reste;
		    while( true ){
                for (int j = NbGroup-1; j >= 0; j--)
                {
                    group[j] = ( int )group[j] - 1;
                    diff--;
		    		if( diff == 0 ){
		    			return group;
		    		}
		    	}
		    }
	    }
	    else
		    return group;
	
}
        public System.Collections.ArrayList GetTable(string trackName, string carName, string Filter, bool colorSensitive, System.Collections.Hashtable playerFilter, int MaxGroupQual, int MaxUserGroupQual, int MinUserGroupQual  )
        {
            System.Collections.ArrayList list = new System.Collections.ArrayList();
            if (!trackTable.ContainsKey(trackName))
                return list;

            System.Collections.Hashtable carTable = trackTable[trackName] as System.Collections.Hashtable;
            carName = replace_group_car(carName);
            string[] lcarName = carName.Split('+');
//            Console.WriteLine("String->" + carName);
            for (int curr_idx_carName = 0; curr_idx_carName < lcarName.Length; curr_idx_carName++)
            {
                string curr_carName = lcarName[curr_idx_carName].Trim();
                if (!carTable.ContainsKey(curr_carName))
                    continue;
                System.Collections.Hashtable playerTable = carTable[curr_carName] as System.Collections.Hashtable;
                System.Collections.IDictionaryEnumerator entryenum = playerTable.GetEnumerator();
                while (entryenum.MoveNext())
                {
                    string userName = entryenum.Key.ToString();
                    bool flag_ok = false;
                    if (playerFilter == null)
                    {
                        flag_ok = true;
                    }
                    else
                    {
                        if (playerFilter.ContainsKey(userName) && ((string)playerFilter[userName] == "" || (string)playerFilter[userName] == curr_carName))
                        {
                            flag_ok = true;
                        }
                    }
                    if (flag_ok)
                    {
                        if (colorSensitive)
                        {
                            if ((entryenum.Value as PlayerStats).nickName.ToString().IndexOf(Filter) != -1)
                            {
                                list.Add(new DriverLapEntry(entryenum.Key.ToString(), entryenum.Value as PlayerStats, curr_carName));
                            }
                        }
                        else
                        {
                            if ((entryenum.Value as PlayerStats).nickName.ToString().Replace("^0", "").Replace("^1", "").Replace("^2", "").Replace("^3", "").Replace("^4", "").Replace("^5", "").Replace("^6", "").Replace("^7", "").Replace("^8", "").IndexOf(Filter) != -1)
                            {
                                list.Add(new DriverLapEntry(entryenum.Key.ToString(), entryenum.Value as PlayerStats, curr_carName));
                            }
                        }
                    }
                }
            }
            list.Sort();
// Mise à jour des poules, par défaut tous le monde est -1 = Not Qualified
            if (MaxGroupQual != 0)
            {
                System.Collections.ArrayList group = MkGroup(MaxGroupQual, MaxUserGroupQual, MinUserGroupQual, list.Count);
                int j = 0;
                for (int i = 0; i < group.Count; i++)
                {
                    int k = (int)group[i];
                    while (k-- != 0)
                    {
                        (list[j] as DriverLapEntry).group = i;
                        j++;
                    }
                }
            }
            return list;
        }

        public System.Collections.ArrayList GetTable(string trackName, string carName, System.Collections.Hashtable playerFilter, int MaxGroupQual, int MaxUserGroupQual, int MinUserGroupQual)
        {
            System.Collections.ArrayList list = new System.Collections.ArrayList();

            if (!trackTable.ContainsKey(trackName))
                return list;

            System.Collections.Hashtable carTable = trackTable[trackName] as System.Collections.Hashtable;

            carName = replace_group_car(carName);
            string[] lcarName = carName.Split('+');

            for (int curr_idx_carName = 0; curr_idx_carName < lcarName.Length; curr_idx_carName++)
            {
                string curr_carName = lcarName[curr_idx_carName].Trim();
                if (!carTable.ContainsKey(curr_carName))
                    continue;
                System.Collections.Hashtable playerTable = carTable[curr_carName] as System.Collections.Hashtable;
                System.Collections.IDictionaryEnumerator entryenum = playerTable.GetEnumerator();

                while (entryenum.MoveNext())
                {
                    string userName = entryenum.Key.ToString();

                    bool flag_ok = false;
                    if (playerFilter == null)
                    {
                        flag_ok = true;
                    }
                    else
                    {
                        if (playerFilter.ContainsKey(userName) && ((string)playerFilter[userName] == "" || (string)playerFilter[userName] == curr_carName))
                        {
                            flag_ok = true;
                        }
                    }
                    if (flag_ok)
                    {
                       list.Add(new DriverLapEntry(entryenum.Key.ToString(), entryenum.Value as PlayerStats, curr_carName));
                    }
                }
            }
            list.Sort();
            // Mise à jour des poules, par défaut tous le monde est -1 = Not Qualified
            if (MaxGroupQual != 0)
            {
                System.Collections.ArrayList group = MkGroup(MaxGroupQual, MaxUserGroupQual, MinUserGroupQual, list.Count);
                int j = 0;
                for (int i = 0; i < group.Count; i++)
                {
                    int k = (int)group[i];
                    while (k-- != 0)
                    {
                        (list[j] as DriverLapEntry).group = i;
                        j++;
                    }
                }
            }
                return list;

        }

        /// <summary>
        /// Gets list of car names.
        /// </summary>
        /// <param name="trackName">Name of track.</param>
        /// <returns></returns>
        public System.Collections.ArrayList GetCars(string trackName)
        {
            System.Collections.ArrayList list = new System.Collections.ArrayList();
            System.Collections.IDictionaryEnumerator carTable = (trackTable[trackName] as System.Collections.Hashtable).GetEnumerator();

            while (carTable.MoveNext())
            {
                list.Add(carTable.Key.ToString());
            }

            list.Sort();
            return list;
        }
        private bool updPbVersion(string filepath)
        {
// If it's not a new version, do nothing
            using (System.IO.StreamReader sr = new System.IO.StreamReader(filepath))
            {
                string pbVersion = sr.ReadLine();
                if (pbVersion.IndexOf("USERNAME") == -1) // Si pas fichier avec Username, on efface et on recrer
                    return false;
                if (pbVersion == curVersion )
                    return false;
            }
            string orig = Path.GetDirectoryName(filepath) + Path.DirectorySeparatorChar + "TMP_" + Path.GetFileName(filepath);
            File.Delete( orig );
            File.Move(filepath, orig);
            using (System.IO.StreamReader sr = new System.IO.StreamReader(orig))
            {
                using (System.IO.StreamWriter sw = new System.IO.StreamWriter(filepath,false))
                {
                    string userName, carName;

                    string pbVersion = sr.ReadLine();
                    switch( pbVersion ){
                        case "USERNAME": // To version 01
                            sw.WriteLine("USERNAME 01");
                            Console.WriteLine("Convert file PB : USERNAME -> USERNAME 01");
                            while (true)
                            {
                                userName = sr.ReadLine();
                                if (userName == null)
                                {
                                    sw.Close();
                                    sr.Close();
                                    File.Delete(orig);
                                    return true;
                                }
                                sw.WriteLine(userName); //username
                                sw.WriteLine(sr.ReadLine()); //nickname
                                sw.WriteLine(sr.ReadLine()); //datePb
                                sw.WriteLine(sr.ReadLine()); //timepB
                                carName = sr.ReadLine();
                                switch (carName)
                                {
                                    case "UF 1000":
                                        carName = "UF1";
                                        break;
                                    case "XF GTI":
                                        carName = "XFG";
                                        break;
                                    case "XR GT":
                                        carName = "XRG";
                                        break;
                                    case "XR GT TURBO":
                                        carName = "XRT";
                                        break;
                                    case "RB4 GT":
                                        carName = "RB4";
                                        break;
                                    case "FXO TURBO":
                                        carName = "FXO";
                                        break;
                                    case "LX4":
                                        carName = "LX4";
                                        break;
                                    case "LX6":
                                        carName = "LX6";
                                        break;
                                    case "RA":
                                        carName = "RAC";
                                        break;
                                    case "FZ50":
                                        carName = "FZ5";
                                        break;
                                    case "MRT5":
                                        carName = "MRT";
                                        break;
                                    case "XF GTR":
                                        carName = "XFR";
                                        break;
                                    case "UF GTR":
                                        carName = "UFR";
                                        break;
                                    case "FORMULA XR":
                                        carName = "FOX";
                                        break;
                                    case "FORMULA V8":
                                        carName = "FO8";
                                        break;
                                    case "FXO GTR":
                                        carName = "FXR";
                                        break;
                                    case "XR GTR":
                                        carName = "XRR";
                                        break;
                                    case "FZ50 GTR":
                                        carName = "FZR";
                                        break;
                                    case "BMW SAUBER":
                                        carName = "BF1";
                                        break;
                                }
                                sw.WriteLine(carName);
                                sw.WriteLine(sr.ReadLine()); //time
                                sw.WriteLine(sr.ReadLine()); //trackName;
                                sw.WriteLine(sr.ReadLine()); //split1time;
                                sw.WriteLine(sr.ReadLine()); //split2time;
                                sw.WriteLine(sr.ReadLine()); //split3time;
                            }
                        case "USERNAME 01": // To version 01
                            sw.WriteLine("USERNAME 02");
                            Console.WriteLine("Convert file PB : USERNAME 01 -> USERNAME 02");
                            while (true)
                            {
                                userName = sr.ReadLine();
                                if (userName == null)
                                {
                                    sw.Close();
                                    sr.Close();
                                    File.Delete(orig);
                                    return true;
                                }
                                sw.WriteLine(userName); //username
                                sw.WriteLine(sr.ReadLine()); //nickname
                                sw.WriteLine(0); // laps
                                sw.WriteLine(sr.ReadLine()); //datePb
                                sw.WriteLine(sr.ReadLine()); //timepB
                                sw.WriteLine(sr.ReadLine()); //CName
                                sw.WriteLine(sr.ReadLine()); //time
                                sw.WriteLine(sr.ReadLine()); //trackName;
                                sw.WriteLine(sr.ReadLine()); //split1time;
                                sw.WriteLine(sr.ReadLine()); //split2time;
                                sw.WriteLine(sr.ReadLine()); //split3time;
                                sw.WriteLine(LFSLapper.unitConv.LongToHMS(0)); // PBBestSplitDiff1
                                sw.WriteLine(LFSLapper.unitConv.LongToHMS(0)); // PBBestSplitDiff2
                                sw.WriteLine(LFSLapper.unitConv.LongToHMS(0)); // PBBestSplitDiff3
                                sw.WriteLine(LFSLapper.unitConv.LongToHMS(0)); // PBBestSplitDiffLast
                            }
                        default:
                            break;
                    }
                    return false;
                }
            }
        }
    }
}