//  
//  infoPlayer.cs
//  
//  Author:
//       Robert BRACCAGNI alias Gai-Luron <lfsgailuron@free.fr>
// 
//  Copyright (c) 2010 Gai-Luron
// 
//  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 System.Collections;

namespace LFSLapper
{
    public class infoPlayer
    {
        public class elpasedInfo
        {
            public long[] elapsedTime = new long[(int)paramLapper.maxSplit + 1];
            public long[] lap = new long[(int)paramLapper.maxSplit + 1];
        }

        public int UCID;
        public int PLID;
        public bool isHost = false;

        public int finishedPLID;
        private InSim.Connect IC;
        public string userName;
        public string OfflineuserName;
        public string nickName;
        public string Language;
        public string Plate;	// License plate

        public string SkinName;	// Name of the skin

        public int CarConfig;    // Car Configuration
        public int FuelValue;    // Fuel of the players car
        public int PitFuel;    // Fuel add during pitstop

        public bool isServerAdmin = false;	// Is the player a server admin
        public string idLang = "en";

        // Connection Info NCI InsimPacket
        public string IPAddress;	        // Converted IP Adress from decimal number (See NCI Packet in insim4.cs)
        public UInt32 DecimalIPAddress;	// Decimal IP Adress straight from NCI Packet (See NCI Packet in insim4.cs)
        public string LFS_Language;	    // Player Selected Langauge in LFS (See NCI Packet in insim4.cs)
        public UInt32 LFS_UserID;	        // Unique LFSUserID (See NCI Packet in insim4.cs)

        public bool isConfigPlayerChanged = false;

        public bool OnTrack = false;
        public bool OnPit = false;
        public string pitWork;
        public bool allowIdleOnTrack = false;

        //result race
        public int finishedPos = -1;
        public long raceTotalTime = 0;
        public long racePBTime = 0;

        //Tyre
        public InSim.tyre oldTyreRearLeft = InSim.tyre.TYRE_NUM;
        public InSim.tyre oldTyreRearRight = InSim.tyre.TYRE_NUM;
        public InSim.tyre oldTyreFrontLeft = InSim.tyre.TYRE_NUM;
        public InSim.tyre oldTyreFrontRight = InSim.tyre.TYRE_NUM;

        public InSim.tyre tyreRearLeft = InSim.tyre.TYRE_NUM;
        public InSim.tyre tyreRearRight = InSim.tyre.TYRE_NUM;
        public InSim.tyre tyreFrontLeft = InSim.tyre.TYRE_NUM;
        public InSim.tyre tyreFrontRight = InSim.tyre.TYRE_NUM;

        public int FrontWheelsAdj = 0;
        public int RearWheelsAdj = 0;

        public int currPos = 0;
		public int lastPos = 0;
		public string UNameBehind = "";
        public string UNameBefore = "";
        public string gapBehind = "";
        public string gapBefore = "";
        public long MaxLapTime;

        public int sessLaps;
        public int lapsDone;

        public string sPlayerFlags = "";

        public int lastIdxSplit = -1;
        public int currSector = -1;
        public int lastSector  = -1;
        public long[] split = new long[(int)paramLapper.maxSplit];
        public long lapTime;
        public long diffLapTimeToPb;
        public long[] sectorSplit = new long[(int)paramLapper.maxSplit];
        public long sectorSplitLast;
        public long[] bestSectorSplit = new long[(int)paramLapper.maxSplit];
        public long bestSectorSplitLast;
        public long[] diffSectorSplit = new long[(int)paramLapper.maxSplit];
        public long diffSectorSplitLast;
        public bool[] isBestSectorSplit = new bool[(int)paramLapper.maxSplit];
        public bool isBestSectorSplitLast;

        public long splitLast;

        public long currSplit;
        public long diffSplit;
        public long bestSplit;
        public bool newSessBestSplit;
        public bool newPBBestSplit;
        public bool newWRBestSplit;

        public long lastSplitTime;


        public long[] sessBestSplitDiff = new long[(int)paramLapper.maxSplit];
        public long sessBestSplitDiffLast;


        public double bestSpeed;
        public string CName;
        public string TName;

        public LFSDbs.gripDbs.DriverLapEntry gripDriverLapInfo;
        public LFSDbs.driftDbs.DriverLapEntry driftDriverLapInfo;
        public bool isNewPBLtime;
        public bool isNewPBLap;
        // Drift Infos
        public double absangle;
        public double totaldriftscore, lastdriftscore, driftticks;
		public bool isDriftReset = false;
        public bool showedgooddriftscore = false;
        public bool isNewBestdriftScore;
		public double instantSpeed, direction, heading;

        // Car Reset
        public int NumCarResets = 0;

        // Node Infos
        public int LastNode = -1;
        public int CurrNode = -1;

        // Lap Info
        public int CurrLap = 1;

        //Player Flags
        public int CausedBlueFlag       = 0; // not used
        public int CausedYellowFLag     = 0; // not used
        public int  UNameOfCarBehind  = 0; //which player is behind PLID

        // Zone Infos
        public int LastZone = -1;
        public int CurrZone = -1;
		public string idCurrZone = "NONE";
        public string CurrentIDTask = "";

        public string PlayerDetectorInfo = "";

        //StreetCode Yisc[NL]
        public int LastStreet = -1;
        public int CurrStreet = -1;
        public string idCurrStreet = "NONE";
        public string CurrentStreetIDTask = "";

        // Idle Infos
        public int idleticks;
        public bool OnIdleAction1Sended = false;

        public int stuntticks;
        public double AngVel;
        public double FixedAngleVelocity;

        public double maxLapSpeed;
        // Acceleration Info
        public bool accelerationInProgress;
        public bool accelerationInProgress2;
        public System.DateTime timeSinceZeroSpeed;
        public System.DateTime timeSinceZeroSpeed2;
        public System.TimeSpan accelerationTime;
        public System.TimeSpan accelerationTime2;
        public bool accelerationTimeValid;
        public bool accelerationTimeValid2;

        public double x = 0, y = 0, z = 0;
		public double old_x = 0, old_y = 0, old_z = 0;
		public double totDistMeter = 0;
		public double lastTotDistMeter = 0; // Last tot dist meter. Reset to totDistMeter when OnDistDone triggered

        public int floodcount;
        public DateTime LastLine = DateTime.Now;

        public DateTime LastCrossedFinishLine = DateTime.Now;
        public bool MaxLapTime1Reached = false;
        public bool MaxLapTime2Reached = false;

        public int swearWordsCount = 0;

        public int drivethroughcount;
        public int NbFastDriveOnPit;

        public int PenaltyNew = (int)InSim.pen.PENALTY_NONE;
        public int PenaltyOld = (int)InSim.pen.PENALTY_NONE;

        public long totalPitTime;
        public long pitTime;
        public long pitStops;

        public int showButton = 0;
        // Config for the curr player
        public string viewSPBSplit = "P";
        public bool showSplitPB = true;
        public bool unitSpeedKmh = true;
        //Handicap info
        public int P_Mass = 0; // Player Handicap Mass
        public int P_TRes = 0; // Player Handicap ntake restriction
        public int H_Mass = 0; // Player Handicap Mass
        public int H_TRes = 0; // Player Handicap ntake restriction

        // Vote
        public int currVote = (int)InSim.vtn.VOTE_NONE;
        public DateTime expireVote = DateTime.Now;

        // Player Box
        public ISMB.msgBox playerBox;

        // GLScript
        // GLScript Player Function
        public System.Collections.Hashtable functionsLapper = new System.Collections.Hashtable();
        // GLSCript Player Var
        public GLScript.SetOfVars playerVars;

        // Elpased Info
        public elpasedInfo currElapsedInfo = new elpasedInfo();

        // Player Tasks
        public System.Collections.ArrayList tasks = new System.Collections.ArrayList();
        public System.Collections.ArrayList PlayerDetectors = new System.Collections.ArrayList();

        private GLDebug.Debug myDebug;

		public LapperThreads.PubStatUser pubStatUser; 

		public LapperThreads.PubStatUser.PubStatUserPb pubStatUserPB;
		public LapperThreads.PubStatUser.PubStatUserPst pubStatUserPst;

		// limit update statistic with skip counter
		private int skipCounter = 0;

        //ObjectCollision
        //Player Info
        public double ObjectContactSpeed;
        public int ObjectContactDir;
        public int ObjectContactHead;

        public infoPlayer( LapperThreads.PubStatUser ppubStatUser, GLDebug.Debug pmyDebug, InSim.Connect pIC, int SUCID, string Susername, string Snickname, string SProduct)
        {
			this.pubStatUser = ppubStatUser;
            this.myDebug = pmyDebug;
            this.UCID = SUCID;
            if (this.UCID == 0)
            {
                this.isHost = true;
            }
            this.PLID = -1;
            this.IC = pIC;
            this.userName = Susername;
            this.nickName = Snickname;

            for (int i = 0; i < (int)paramLapper.maxSplit; i++)
            {
                this.split[i] = 0;
                this.sectorSplit[i] = 0;
                this.sessBestSplitDiff[i] = 0;
                this.bestSectorSplit[i] = 0;
                this.diffSectorSplit[i] = 0;
                this.isBestSectorSplit[i] = false;
            }
            this.sectorSplitLast = 0;
            this.bestSectorSplitLast = 0;
            this.diffSectorSplitLast = 0;
            this.lapTime = 0;
            this.splitLast = 0;
            this.sessBestSplitDiffLast = 0;
            this.isBestSectorSplitLast = false;
            this.bestSpeed = (double)0;
            this.CName = "";

            this.sessLaps = 0;
            this.lapsDone = 0;

            this.showSplitPB = true;

            this.totalPitTime = 0;
            this.pitStops = 0;
            playerBox = new ISMB.msgBox(IC);
			if( pubStatUser != null )
				pubStatUser.requestPubStat(this.userName);
        }
        public void privateButtonClose(string argString)
        {

            string[] butToClose = argString.Split('&');
            for (int i = 0; i < butToClose.Length; i++)
            {
                this.playerBox.delete(butToClose[i], this.UCID);
                //                Console.WriteLine("Close : " + butToClose[i]);
            }

        }
        public void clearSessionInfo()
        {
            for (int i = 0; i < (int)paramLapper.maxSplit; i++)
            {
                this.sessBestSplitDiff[i] = 0;
            }
            this.sessBestSplitDiffLast = 0;
            this.sessLaps = 0;

        }


        //public void UpdateState(int currPos, int node, int zone, string idZone, int x, int y, int z, double speed, double direction, double heading, double anglevelocity, int minspeed, int minangle, int maxangle, int MinVel, double accelStartSpeed, double accelEndSpeed, double accelStartSpeed2, double accelEndSpeed2)
        public void UpdateState(int currPos, int lap, int node, int zone, string idZone,int street,string idStreet, double x, double y, double z, double speed, double direction, double heading, double anglevelocity, int realtimedriftscore, int minspeed, int minangle, int maxangle, int MinVel, double accelStartSpeed, double accelEndSpeed, double accelStartSpeed2, double accelEndSpeed2, bool OnTrack)
        {
            this.lastPos = this.currPos;
			this.currPos = currPos;
			this.LastNode = this.CurrNode;
            this.LastZone = this.CurrZone;
            this.CurrNode = node;
            this.CurrZone = zone;
            this.CurrLap = lap;
            this.idCurrZone = idZone;
            //Streetcode Yisc[NL]
            this.CurrStreet = street;
            this.idCurrStreet = idStreet;

            this.absangle = AbsoluteAngleDifference(this.direction, this.heading);
            double driftscoreinthistick = this.absangle * speed * speed / 10000d;

                if (speed < minspeed) // Reset drift
                {
                    if( totaldriftscore != 0 )
                        this.isDriftReset = true;
                    else
                        this.isDriftReset = false;
                    this.driftticks = 0;
                    this.totaldriftscore = 0;
                    this.lastdriftscore = 0;
                }
                else
                {
                    this.isDriftReset = false;
                    if (this.absangle > minangle && this.absangle < maxangle)
                    {
                        this.driftticks++;
                        this.lastdriftscore += driftscoreinthistick;
                        this.totaldriftscore += driftscoreinthistick;
                    }
                    else
                    {
                        if (realtimedriftscore == 1) //reset CurrentDriftscore when Realtime scoring is enabled.
                        {
                            this.lastdriftscore = 0;
                        }
                    this.driftticks = 0;
                    }
                }

            this.direction = direction;
            this.heading = heading;
            this.instantSpeed = speed;
            this.AngVel = anglevelocity;
            this.x = x;
            this.y = y;
            this.z = z;

            // Fixed Negative Anglevelocity
            if (anglevelocity < 0)
                FixedAngleVelocity = (anglevelocity * -1);
            else
                FixedAngleVelocity = anglevelocity;

            //if Anglevelocity is larger than MinVelocity 
            if (FixedAngleVelocity > MinVel)
                this.stuntticks++;
            else
                this.stuntticks = 0;

            // voting
            if ((this.currVote != (int)InSim.vtn.VOTE_NONE) && (this.expireVote < DateTime.Now))
                this.currVote = (int)InSim.vtn.VOTE_NONE;

            // idling
            this.idleticks = speed > 1 ? 0 : this.idleticks + 1;

            if (speed > 1)
                this.OnIdleAction1Sended = false;

            // speed
            if (speed > this.maxLapSpeed)
                this.maxLapSpeed = speed;

            if (speed > this.bestSpeed)
                this.bestSpeed = speed;
#region Code executed not every time, limit CPU usage
			if (skipCounter == 5 ) 
			{
				skipCounter = 0;
#region Update distance do by a player
				if (old_x != 0 && old_y != 0 && old_x != 0)
				{
					double diffx = this.old_x - x;
					double diffy = this.old_y - y;
					double addMeter = Math.Sqrt((diffx * diffx) + (diffy * diffy));
					this.totDistMeter += addMeter;
//									Console.WriteLine(this.totDistMeter);
				}
				this.old_x = x;
				this.old_y = y;
				this.old_z = z;
#endregion
			}
			skipCounter++;
            #endregion

            // acceleration1
            if (speed < accelStartSpeed/*0.1*/)
            {
                this.timeSinceZeroSpeed = System.DateTime.Now;
                this.accelerationInProgress = true;
            }
            else if (speed > accelEndSpeed/*100*/ && this.accelerationInProgress)
            {
                this.accelerationTime = System.DateTime.Now - this.timeSinceZeroSpeed;
                this.accelerationInProgress = false;
                this.accelerationTimeValid = true;
            }

            // acceleration2
            if (speed < accelStartSpeed2 /*100*/)
            {
                this.timeSinceZeroSpeed2 = System.DateTime.Now;
                this.accelerationInProgress2 = true;
            }
            else if (speed > accelEndSpeed2 /*160*/ && this.accelerationInProgress2)
            {
                this.accelerationTime2 = System.DateTime.Now - this.timeSinceZeroSpeed2;
                this.accelerationInProgress2 = false;
                this.accelerationTimeValid2 = true;
            }
        }
        public void Rename(string newnick)
        {
            this.nickName = newnick;
        }

        static double AbsoluteAngleDifference(double d, double h)
        {
            double absdiff = Math.Abs(d - h);

            if (absdiff <= 180)
                return absdiff;

            if (d < 180)
            {
                h -= 360;
                return d - h;
            }
            else
            {
                d -= 360;
                return h - d;
            }

        }
        public void updateLapDriftScore(string currentTrackName)
        {
            if (this.totaldriftscore > this.driftDriverLapInfo.driftPoints)
            {
                this.driftDriverLapInfo.driftPoints = (long)this.totaldriftscore;
                this.driftDriverLapInfo.datePb = System.DateTime.Now.ToString("yyyy/MM/dd");
                this.driftDriverLapInfo.timePb = System.DateTime.Now.ToString("HH:mm");

                this.isNewBestdriftScore = true;
            }
            else
            {
                this.isNewBestdriftScore = false;
            }
        }
        public void updateLap(wr currWr, InSim.Decoder.LAP lapDec, long maxLtime, string currentTrackName, int LapTimeUsedForPb)
        {
            long sessDiffSplit;
            long sessBestSplit;
            long PBDiffSplit;
            long PBBestSplit;

            this.lastIdxSplit = (int)paramLapper.maxSplit;
            this.gripDriverLapInfo.laps++;
            this.sessLaps++;
            this.currSplit = lapDec.LTime - this.splitLast;
            this.sectorSplitLast = this.currSplit;
            this.lastSplitTime = lapDec.LTime;
            this.lapTime = lapDec.LTime;

            this.lapsDone = lapDec.LapsDone;
            this.currElapsedInfo.elapsedTime[(int)paramLapper.maxSplit] = lapDec.ETime;
            this.currElapsedInfo.lap[(int)paramLapper.maxSplit] = this.lapsDone;

            sessDiffSplit = currSplit - this.sessBestSplitDiffLast;
            sessBestSplit = this.sessBestSplitDiffLast;
            if (currSplit < this.sessBestSplitDiffLast || this.sessBestSplitDiffLast == 0)
            {
                this.sessBestSplitDiffLast = currSplit;
                this.newSessBestSplit = true;
            }
            else
            {
                this.newSessBestSplit = false;
            }

            PBDiffSplit = currSplit - this.gripDriverLapInfo.PBBestSplitDiffLast;
            PBBestSplit = this.gripDriverLapInfo.PBBestSplitDiffLast;
            if (currSplit < this.gripDriverLapInfo.PBBestSplitDiffLast || this.gripDriverLapInfo.PBBestSplitDiffLast == 0)
            {
                this.gripDriverLapInfo.PBBestSplitDiffLast = currSplit;
                this.newPBBestSplit = true;
            }
            else
            {
                this.newPBBestSplit = false;
            }
            this.isBestSectorSplitLast = false;
            switch (this.viewSPBSplit)
            {
                case "P":
                    this.diffSplit = PBDiffSplit;
                    this.bestSplit = PBBestSplit;
                    if (this.newPBBestSplit)
                        this.isBestSectorSplitLast = true;
                    break;
                case "S":
                    this.diffSplit = sessDiffSplit;
                    this.bestSplit = sessBestSplit;
                    if (this.newSessBestSplit)
                        this.isBestSectorSplitLast = true;
                    break;
                case "W":
                    try
                    {
                        wr.wrInfo wi = currWr.getWR(currentTrackName, this.CName);
                        this.diffSplit = this.currSplit - wi.sectorSplitLast;
                        this.bestSplit = wi.sectorSplitLast;
                        if (this.newWRBestSplit)
                            this.isBestSectorSplitLast = true;
                    }
                    catch
                    {
                        this.diffSplit = 0;
                        this.bestSplit = 0;
                        myDebug.WriteLine("mss","WR not found for combo :" + currentTrackName + "/" + this.CName);
                    }
                    break;
            }
            this.bestSectorSplitLast = this.bestSplit;
            this.diffSectorSplitLast = this.diffSplit;
            this.splitLast = 0;

            this.diffLapTimeToPb = lapDec.LTime - this.gripDriverLapInfo.personalBestLapTime;
            this.isNewPBLap = false;
            this.isNewPBLtime = false;
            if (lapDec.LTime < maxLtime)
            {
                long lastPB = this.gripDriverLapInfo.personalBestLapTime;
                if (this.gripDriverLapInfo.listPB.Count < 10)
                {
                    this.gripDriverLapInfo.listPB.Add(new LFSDbs.gripDbs.PbLapEntry(lapDec.LTime, this.split[0], this.split[1], this.split[2], System.DateTime.Now.ToString("yyyy/MM/dd"), System.DateTime.Now.ToString("HH:mm")));
                    this.gripDriverLapInfo.majPB(LapTimeUsedForPb);
                    this.isNewPBLap = true;
                }
                else
                {
                    if ((this.gripDriverLapInfo.listPB[9] as LFSDbs.gripDbs.PbLapEntry).personalBestLapTime > lapDec.LTime)
                    {
                        this.gripDriverLapInfo.listPB.RemoveAt(9);
                        this.gripDriverLapInfo.listPB.Add(new LFSDbs.gripDbs.PbLapEntry(lapDec.LTime, this.split[0], this.split[1], this.split[2], System.DateTime.Now.ToString("yyyy/MM/dd"), System.DateTime.Now.ToString("HH:mm")));
                        this.gripDriverLapInfo.majPB(LapTimeUsedForPb);
                        this.isNewPBLap = true;
                    }
                }
                if (this.gripDriverLapInfo.personalBestLapTime != lastPB && this.gripDriverLapInfo.personalBestLapTime < (3600000 - 1000))
                {
                    if (this.gripDriverLapInfo.personalBestLapTime < this.wr || this.wr == -1 ) // If new PB retreive WR
						pubStatUser.requestPubStat(this.userName);
                    this.isNewPBLtime = true;
                }
                else
                    this.isNewPBLtime = false;
            }
            LastCrossedFinishLine = DateTime.Now;
        }
        public void updateSplit(wr currWr, InSim.Decoder.SPX splitDec, string track)
        {
            long sessDiffSplit;
            long sessBestSplit;
            long PBDiffSplit;
            long PBBestSplit;

            int idxSplit = splitDec.Split - 1;

            this.lastIdxSplit = idxSplit;


            this.currElapsedInfo.elapsedTime[idxSplit] = splitDec.ETime;
            this.currElapsedInfo.lap[idxSplit] = this.lapsDone;

            // Clear Last Split on first Split
            if (idxSplit == 0)
            {
                this.splitLast = 0;
                for (int i = 0; i < (int)paramLapper.maxSplit; i++)
                {
                    this.split[i] = 0;
                    this.sectorSplit[i] = 0;
                    this.sectorSplitLast = 0;
                }
            }

            this.lastSplitTime = splitDec.STime;
            this.split[idxSplit] = splitDec.STime;
            this.currSplit = splitDec.STime - this.splitLast;
            this.sectorSplit[idxSplit] = this.currSplit;

            sessDiffSplit = this.currSplit - this.sessBestSplitDiff[idxSplit];
            sessBestSplit = this.sessBestSplitDiff[idxSplit];
            if (sessDiffSplit < 0 || this.sessBestSplitDiff[idxSplit] == 0)
            {
                this.sessBestSplitDiff[idxSplit] = this.currSplit;
                this.newSessBestSplit = true;
            }
            else
            {
                this.newSessBestSplit = false;
            }

            PBDiffSplit = this.currSplit - this.gripDriverLapInfo.PBBestSplitDiff[idxSplit];
            PBBestSplit = this.gripDriverLapInfo.PBBestSplitDiff[idxSplit];
            if (PBDiffSplit < 0 || this.gripDriverLapInfo.PBBestSplitDiff[idxSplit] == 0)
            {
                this.gripDriverLapInfo.PBBestSplitDiff[idxSplit] = this.currSplit;
                this.newPBBestSplit = true;
            }
            else
            {
                this.newPBBestSplit = false;
            }
            this.isBestSectorSplit[idxSplit] = false;
            switch (this.viewSPBSplit)
            {
                case "P":
                    this.diffSplit = PBDiffSplit;
                    this.bestSplit = PBBestSplit;
                    if (this.newPBBestSplit)
                        this.isBestSectorSplit[idxSplit] = true;
                    break;
                case "S":
                    this.diffSplit = sessDiffSplit;
                    this.bestSplit = sessBestSplit;
                    if (this.newSessBestSplit)
                        this.isBestSectorSplit[idxSplit] = true;
                    break;
                case "W":
                    try
                    {
                        wr.wrInfo wi = currWr.getWR(track, this.CName);
                        this.diffSplit = this.currSplit - wi.sectorSplit[idxSplit];
                        this.bestSplit = wi.sectorSplit[idxSplit];
                        if (this.newWRBestSplit)
                            this.isBestSectorSplit[idxSplit] = true;
                    }
                    catch
                    {
                        this.diffSplit = 0;
                        this.bestSplit = 0;
                        myDebug.WriteLine( "mss","WR not found for combo :" + track + "/" + this.CName);
                    }
                    break;
            }
            this.bestSectorSplit[idxSplit] = this.bestSplit;
            this.diffSectorSplit[idxSplit] = this.diffSplit;
            this.splitLast = splitDec.STime;
            this.lastSector = splitDec.Split;
            this.currSector = splitDec.Split + 1;
        }

        public void loopTask( Configurator.lexConfigurator newCfg ){

			Task[] listTask = (Task[])tasks.ToArray(typeof(Task));
			for (int i = 0; i < listTask.Length; i++)
            {
				Task currTask = listTask[i];
                if (currTask.IsToExecuteNow(currTask.ScheduledAction))
                {
                    if (currTask.executed == false)
                    {
                        currTask.executed = true;

                        //Debug messages in console.
                        if (newCfg.varsLapper.DisplayLoops == 1)
                        {
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.WriteLine(System.DateTime.Now + " : PlayerLoop: [ " + this.userName + " ] Sub: " + currTask.Command + " Executed");
                        }

                        //Execute function
                        string[] args = new string[1];
                        args[0] = this.userName;
                        newCfg.executeFunction(currTask.Command, this, args);

                        //delete Task after executing
                        if (currTask.IsOnceToExecuteOnly)
                            currTask.toRemove = true;
                    }
                }
                else
                {
                    currTask.executed = false;
                    if (newCfg.varsLapper.DisplayLoops == 1)
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine(System.DateTime.Now + " : PlayerLoop: [ " + this.userName + " ] Sub: " + currTask.Command + " Canceled");
                        Console.ForegroundColor = ConsoleColor.White;
                    }
                }
            }
			for (int i = tasks.Count - 1; i >= 0; i--)
			{
				if ((tasks[i] as Task).toRemove)
					tasks.RemoveAt(i);
			}
        }
		public ArrayList getUserListPb(string car, string track)
		{
			ArrayList retPbUser = new ArrayList();
			if (pubStatUserPB == null)
				return retPbUser;
			
			if (car != "" && track != "")
			{
				for (int i = 0; i < pubStatUserPB.listPbElem.Count; i++)
				{
					LapperThreads.PubStatUser.PubStatUserPb.PbElem elem = ( LapperThreads.PubStatUser.PubStatUserPb.PbElem )pubStatUserPB.listPbElem[i];
					if (elem.car == car && elem.track == track)
					{
						retPbUser.Add( elem );
						return retPbUser;
					}
				}
				return retPbUser;
			}
			if (car == "" && track == "")
			{
				pubStatUserPB.sortByCar();
				return pubStatUserPB.listPbElem;
			}
			if (car == "")
			{
				for (int i = 0; i < pubStatUserPB.listPbElem.Count; i++)
				{
					LapperThreads.PubStatUser.PubStatUserPb.PbElem elem = (LapperThreads.PubStatUser.PubStatUserPb.PbElem)pubStatUserPB.listPbElem[i];
					if (elem.track == track)
					{
						retPbUser.Add(elem);
					}
				}
				retPbUser.Sort(new LapperThreads.PubStatUser.PubStatUserPb.sByCar());
				return retPbUser;
			}
			if (track == "")
			{
				for (int i = 0; i < pubStatUserPB.listPbElem.Count; i++)
				{
					LapperThreads.PubStatUser.PubStatUserPb.PbElem elem = (LapperThreads.PubStatUser.PubStatUserPb.PbElem)pubStatUserPB.listPbElem[i];
					if (elem.car == car)
					{
						retPbUser.Add(elem);
					}
				}
				retPbUser.Sort(new LapperThreads.PubStatUser.PubStatUserPb.sByTrack());
				return retPbUser;
			}
			return retPbUser;
		}
        public long wr
        {
            get
            {
	            long wr = -1;
		        if (this.CName != "" && this.TName != "")
			    {
				    System.Collections.ArrayList list = getUserListPb( this.CName,this.TName );
					if (list != null && list.Count != 0)
	                    wr = (list[0] as LapperThreads.PubStatUser.PubStatUserPb.PbElem).lapTime;
		            else
			            wr = -1;
				}
				else
					wr = -1;
            return wr;
			}
        }
		public long PSDistance
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.distance;
				else
					return 0;
			}
		}
		public long PSFuel
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.fuel;
				else
					return 0;
			}
		}
		public long PSLaps
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.laps;
				else
					return 0;
			}
		}
		public long PSHostsJoined
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.hostsJoined;
				else
					return 0;
			}
		}
		public long PSWins
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.wins;
				else
					return 0;
			}
		}
		public long PSSecond
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.second;
				else
					return 0;
			}
		}
		public long PSThird
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.third;
				else
					return 0;
			}
		}
		public long PSFinished
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.finished;
				else
					return 0;
			}
		}
		public long PSQuals
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.quals;
				else
					return 0;
			}
		}
		public long PSPole
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.pole;
				else
					return 0;
			}
		}
		public long PSDrags
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.drags;
				else
					return 0;
			}
		}
		public long PSDragWins
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.dragWins;
				else
					return 0;
			}
		}
		public string PSCountry
		{
			get
			{
				if (this.pubStatUserPst != null)
					return this.pubStatUserPst.country;
				else
					return "";
			}
		}
		public long TPb
        {
            get
            {
                long Tpb = 0;
                switch (this.viewSPBSplit)
                {
                    case "W":
                    case "P":
                        for (int i = 0; i < (int)paramLapper.maxSplit; i++)
                            Tpb += this.gripDriverLapInfo.PBBestSplitDiff[i];
                        Tpb += this.gripDriverLapInfo.PBBestSplitDiffLast;
                        break;
                    case "S":
                        for (int i = 0; i < (int)paramLapper.maxSplit; i++)
                            Tpb += this.sessBestSplitDiff[i];
                        Tpb += this.sessBestSplitDiffLast;
                        break;
                }
                return Tpb;
            }
        }
    }
}
