//  
//  managePacket.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/>.


namespace LFSLapper
{
    using System;
    partial class LFSClient
    {
        System.Collections.ArrayList PLIDChangePos = new System.Collections.ArrayList();

        bool flagFirstInState = true;


        void managePacket(InSim.Decoder.TINY tiny)
        {
            // Keep alive connection
//            Console.WriteLine(tiny.SubT);
            switch( tiny.SubT ){
                case "TINY_NONE":
                    byte[] stiny = myEncoder.TINY_NONE();
                    insimConnection.Send(stiny, stiny.Length);
                    break;
                case "TINY_CLR": // "CLear Race - all players removed from race in one go"
                    if (newCfg.varsLapper.AutoRestartRaceSec > 0)
                    {
                        currRace.nextRestart = DateTime.Now.AddSeconds((double)newCfg.varsLapper.AutoRestartRaceSec);
                        currRace.autoRestartOn = true;
                    }
                    break;
            }
        }
        void managePacket(InSim.Decoder.NCN newConnection)
        {
            infoPlayer currInfoPlayer;

            listOfPlayers.newPlayer(objPubStatUser, newCfg, newConnection.UCID, newConnection.userName, newConnection.nickName, newConnection.Flags, insimConnection.Product);
            currInfoPlayer = listOfPlayers.getPlayerByUCID(newConnection.UCID);
            createPlayerSetOfVars(currInfoPlayer); // Creation of player set of Vars

            if (currInfoPlayer == null)
                return;
            cfu.retreive(currInfoPlayer);
            if (currInfoPlayer.CName == "" ){
				string[] ar_CNAME = newCfg.varsLapper.DefaultTopCar.Split('+');
				if (ar_CNAME.Length != 0)
					currInfoPlayer.CName = ar_CNAME[0].Trim();
				else
					currInfoPlayer.CName = "XFG";
			}

			gripSqlDbs.retreiveRow(currInfoPlayer, currState.ShortTrackName);
            driftSqlDbs.retreiveRow(currInfoPlayer, currState.ShortTrackName);

			string testExclude = "," + newCfg.varsLapper.IdleExclude.ToLower() + ",";
            if (testExclude.IndexOf("," + currInfoPlayer.userName.ToLower() + ",") != -1)
                currInfoPlayer.allowIdleOnTrack = true;
            else
                currInfoPlayer.allowIdleOnTrack = false;

			// Set the admin flag if the player is a server  admin
			if (newConnection.Admin == 1)
			{
				currInfoPlayer.isServerAdmin = true;
				uGroup.addUser("superUsers", currInfoPlayer.userName);
			}
            string[] args = new string[1];
            args[0] = currInfoPlayer.userName;
            newCfg.executeFunction("OnConnect", currInfoPlayer, args);

        }
        void managePacket(InSim.Decoder.CNL lostConnection)
        {
            infoPlayer currInfoPlayer;

            currInfoPlayer = listOfPlayers.getPlayerByUCID(lostConnection.UCID);
            if (currInfoPlayer == null)
                return;
			if( currInfoPlayer.isConfigPlayerChanged ) // On disconnect of player, save config file
				cfu.update(currInfoPlayer);
            string[] args = new string[2];
			args[0] = currInfoPlayer.userName;
			args[1] = lostConnection.Reason.ToString();
			newCfg.executeFunction("OnDisConnect", currInfoPlayer, args);
            listOfPlayers.removePlayer(newCfg,lostConnection.UCID);
        }
        void managePacket(InSim.Decoder.NPL newPlayer)
        {
            int FL_Changed = 0;
            int FR_Changed = 0;
            int RL_Changed = 0;
            int RR_Changed = 0;

            infoPlayer currInfoPlayer;

            currInfoPlayer = listOfPlayers.getPlayerByUCID(newPlayer.UCID);
            if (currInfoPlayer == null)
                return;
            currInfoPlayer.OnTrack = true;

            currInfoPlayer.LastCrossedFinishLine = System.DateTime.Now;
            currInfoPlayer.MaxLapTime1Reached = false;
            currInfoPlayer.MaxLapTime2Reached = false;
            currInfoPlayer.MaxLapTime = trackInfo.getMaxLapTime(currState.ShortTrackName, newPlayer.CName);
            listOfPlayers.majIndexPLID(newPlayer.UCID, newPlayer.PLID);
            // If player change car, Clear session
            if (currInfoPlayer.CName != newPlayer.CName)
            {
                currInfoPlayer.clearSessionInfo();
				currInfoPlayer.CName = newPlayer.CName;
            }

            gripSqlDbs.retreiveRow(currInfoPlayer, currState.ShortTrackName);
            driftSqlDbs.retreiveRow(currInfoPlayer, currState.ShortTrackName);


            #region Handicap User
            // If indication of Handicap of current player
            currInfoPlayer.P_Mass = newPlayer.H_Mass;
            currInfoPlayer.P_TRes = newPlayer.H_TRes;
            int cumH_Mass = 0;
            int cumH_Tres = 0;
            // Handicap priority
            // Player, CarTrack, Car
            // Car handicap

            if (newCfg.varsLapper.RefreshHandicapUsers)
                UpdateHandicapUsers(newCfg.varsLapper.HandicapUsers);

            if (carHandicap.ContainsKey(currInfoPlayer.CName.ToLower()))
            {
                cumH_Mass = (carHandicap[currInfoPlayer.CName.ToLower()] as infoHandicap).H_Mass;
                cumH_Tres = (carHandicap[currInfoPlayer.CName.ToLower()] as infoHandicap).H_TRes;
            }
            // Car/Tarck handicap
            string carTrack = currInfoPlayer.CName.ToLower() + "/" + currState.ShortTrackName.ToLower();
            if (carTrackHandicap.ContainsKey(carTrack))
            {
                cumH_Mass = (carTrackHandicap[carTrack] as infoHandicap).H_Mass;
                cumH_Tres = (carTrackHandicap[carTrack] as infoHandicap).H_TRes;
            }
            // Player handicap
            if (playerHandicap.ContainsKey(currInfoPlayer.userName.ToLower()))
            {
                cumH_Mass = (playerHandicap[currInfoPlayer.userName.ToLower()] as infoHandicap).H_Mass;
                cumH_Tres = (playerHandicap[currInfoPlayer.userName.ToLower()] as infoHandicap).H_TRes;
            }
            currInfoPlayer.H_Mass = cumH_Mass;
            currInfoPlayer.H_TRes = cumH_Tres;
            if (newPlayer.H_Mass < cumH_Mass || newPlayer.H_TRes < cumH_Tres)
            {
                string[] args = new string[1];
                args[0] = currInfoPlayer.userName;
                newCfg.executeFunction("OnToLowHandicap", currInfoPlayer, args);
            }
            #endregion

            if (currInfoPlayer.oldTyreRearLeft == InSim.tyre.TYRE_NUM)
                currInfoPlayer.oldTyreRearLeft = newPlayer.TyreRearLeft;
            if (currInfoPlayer.oldTyreRearRight == InSim.tyre.TYRE_NUM)
                currInfoPlayer.oldTyreRearRight = newPlayer.TyreRearRight;
            if (currInfoPlayer.oldTyreFrontLeft == InSim.tyre.TYRE_NUM)
                currInfoPlayer.oldTyreFrontLeft = newPlayer.TyreFrontLeft;
            if (currInfoPlayer.oldTyreFrontRight == InSim.tyre.TYRE_NUM)
                currInfoPlayer.oldTyreFrontRight = newPlayer.TyreFrontRight;

            currInfoPlayer.tyreRearLeft = newPlayer.TyreRearLeft;
            currInfoPlayer.tyreRearRight = newPlayer.TyreRearRight;
            currInfoPlayer.tyreFrontLeft = newPlayer.TyreFrontLeft;
            currInfoPlayer.tyreFrontRight = newPlayer.TyreFrontRight;

            if (currInfoPlayer.oldTyreRearLeft != currInfoPlayer.tyreRearLeft)
                RL_Changed = 1;
            if (currInfoPlayer.oldTyreRearRight != currInfoPlayer.tyreRearRight)
                RR_Changed = 1;
            if (currInfoPlayer.oldTyreFrontLeft != currInfoPlayer.tyreFrontLeft)
                FL_Changed = 1;
            if (currInfoPlayer.oldTyreFrontRight != currInfoPlayer.tyreFrontRight)
                FR_Changed = 1;

            if (FL_Changed != 0 || FR_Changed != 0 || RL_Changed != 0 || RR_Changed != 0)
            {
                string[] args = new string[5];
                args[0] = currInfoPlayer.userName;
                args[1] = FL_Changed.ToString();
                args[2] = FR_Changed.ToString();
                args[3] = RL_Changed.ToString();
                args[4] = RR_Changed.ToString();
                newCfg.executeFunction("OnChangeTyres", currInfoPlayer, args);
            }


            #region Authorization for this player to join race
            // Player Authorisation

            if (listOfPlayers.countPlayer() > newCfg.varsLapper.AuthMinPlayer)
            {
                if (isAllowedPlayer(currInfoPlayer))
                {
                    if (SystemAuth.AllowAllPlayer == false)
                    {
                        string[] args = new string[1];
                        args[0] = currInfoPlayer.userName;
                        newCfg.executeFunction("OnAuthAllowed", currInfoPlayer, args);
                    }
                }
                else
                {
                    string[] args = new string[1];
                    args[0] = currInfoPlayer.userName;
                    newCfg.executeFunction("OnAuthNotAllowed", currInfoPlayer, args);
                }
            }
            #endregion

            #region flags for this player
            actionPlayerFlags(newPlayer.Flags, currInfoPlayer, true);
            #endregion
            {
                string[] args = new string[1];
                args[0] = currInfoPlayer.userName;
                newCfg.executeFunction("OnNewPlayerJoin", currInfoPlayer, args);
            }

        }
        void managePacket(InSim.Decoder.CRS crs)
        {
            // Krayy car reset code
            infoPlayer currInfoPlayer;
            currInfoPlayer = listOfPlayers.getPlayerByPLID(crs.PLID);
            if (currInfoPlayer == null)
                return;
            string[] args = new string[1];
            if (currRace.inRace)
            {
                if ( newCfg.varsLapper.MaxCarResets > 0 ) {
                    currInfoPlayer.NumCarResets++;

                    args[0] = currInfoPlayer.userName;
                    if (currInfoPlayer.NumCarResets >= newCfg.varsLapper.MaxCarResets)
                        newCfg.executeFunction("OnMaxCarResets", currInfoPlayer, args);
                    else
                        newCfg.executeFunction("OnCarReset", currInfoPlayer, args);
                } 
                else
                    newCfg.executeFunction("OnCarReset", currInfoPlayer, args);
            }
        }

        void managePacket(InSim.Decoder.PLP plp)
        {
            infoPlayer currInfoPlayer;
            currInfoPlayer = listOfPlayers.getPlayerByPLID(plp.PLID);
            if (currInfoPlayer == null)
                return;
            currInfoPlayer.OnPit = false;
            currInfoPlayer.OnTrack = false;
			currInfoPlayer.idCurrZone = "NONE";
			currInfoPlayer.old_x = 0;
			currInfoPlayer.old_y = 0;
			currInfoPlayer.old_z = 0;
			currInfoPlayer.oldTyreRearLeft = currInfoPlayer.tyreRearLeft;
            currInfoPlayer.oldTyreRearRight = currInfoPlayer.tyreRearRight;
            currInfoPlayer.oldTyreFrontLeft = currInfoPlayer.tyreFrontLeft;
            currInfoPlayer.oldTyreFrontRight = currInfoPlayer.tyreFrontRight;
            currInfoPlayer.currVote = (int)InSim.vtn.VOTE_NONE;

            string[] args = new string[1];
            args[0] = currInfoPlayer.userName;
            newCfg.executeFunction("OnPit", currInfoPlayer,args);

        }
        void managePacket(InSim.Decoder.PLL pll)
        {
            infoPlayer currInfoPlayer;
            currInfoPlayer = listOfPlayers.getPlayerByPLID(pll.PLID);
            if (currInfoPlayer == null)
                return;
            currInfoPlayer.OnTrack = false;
			currInfoPlayer.idCurrZone = "NONE";
			currInfoPlayer.x = -1;
			currInfoPlayer.y = -1;
			currInfoPlayer.CurrZone = -1;
			currInfoPlayer.LastZone = -1;
			currInfoPlayer.CurrNode = -1;
			currInfoPlayer.LastNode = -1;
			currInfoPlayer.old_x = 0;
			currInfoPlayer.old_y = 0;
			currInfoPlayer.old_z = 0;
			currInfoPlayer.OnPit = false;
            currInfoPlayer.currVote = (int)InSim.vtn.VOTE_NONE;

            currInfoPlayer.showButton = 0;
            //ClearButton(currInfoPlayer);
            string[] args = new string[1];
            args[0] = currInfoPlayer.userName;
            newCfg.executeFunction("OnLeaveRace", currInfoPlayer, args);
            listOfPlayers.removeIndexPLID(pll.PLID);
        }
        void managePacket(InSim.Decoder.CPR cpr)
        {
            infoPlayer currInfoPlayer;


            currInfoPlayer = listOfPlayers.getPlayerByUCID(cpr.UCID);
            if (currInfoPlayer == null)
                return;
            // For make bug programm to test object debug
            /*
                                        currInfoPlayer = listOfPlayers.getPlayerByUCID(300);
                                        currInfoPlayer.CName = "";
            */

            string oldNickName = currInfoPlayer.nickName;
            currInfoPlayer.Rename(cpr.newNickName);
            // Sometime LFS send a CPR with same oldNickname and newNickname, maybe a new uniqueID
            // if you do .Remove of Var the programm crash because the ref of var are same and the program crash;
 			// Update user database & execute OnNameChange function
 			string[] par = new string[3];
            par[0] = currInfoPlayer.userName;
 			par[1] = oldNickName;
 			par[2] = cpr.newNickName;
 			newCfg.executeFunction("OnNameChange", currInfoPlayer, par);


        }
        void managePacket(InSim.Decoder.LAP lapDec)
        {
            infoPlayer currInfoPlayer;

            currInfoPlayer = listOfPlayers.getPlayerByPLID(lapDec.PLID);
            if (currInfoPlayer == null)
                return;

            if (currRace.inRace == true)
            {
                // Calculate reminaing race time each time a racer passes the start/finish line if this is a timed race - between 1 and 48 hours
                if (currRace.raceLaps >= 191)
                {
                    currRace.raceMins = (int)(((currRace.raceLaps - 190) * 60) - (lapDec.ETime / 60000));
                    // Set RaceLapsLeft to 1 if there is less than 5 mins of the race remaining
                    if (currRace.raceMins <= 5)
                    {
                        currRace.raceLapsLeft = 1;
                    }
                }
                // If this racer is the race leader...
                if (lapDec.LapsDone > currRace.lapsDone)
                {
                    currRace.lapsDone = lapDec.LapsDone;
                    // Set raceLapsLeft based on LAP.RaceLaps
                    if (currRace.raceLaps < 100)
                    {
                        // Short race - less than 100 laps
                        currRace.raceLapsLeft = currRace.raceLaps - lapDec.LapsDone;
                    }
                    else if (currRace.raceLaps < 191)
                    {
                        // Long race - between 100 and 1000 laps
                        currRace.raceLapsLeft = (((currRace.raceLaps - 100) * 10) + 100) - lapDec.LapsDone;
                    }
                }
            }

            #region test flags for this player
            actionPlayerFlags(lapDec.Flags, currInfoPlayer, false);
            #endregion

            if (lapDec.LTime == unitConv.HMSToLong(60, 0, 0))
                goto endOfLap;

            #region Drift scores
            if (currInfoPlayer.totaldriftscore >= newCfg.varsLapper.MinimumDriftScore)
            {
                currInfoPlayer.updateLapDriftScore(currState.ShortTrackName);
                if (currInfoPlayer.isNewBestdriftScore)
                {
                    driftSqlDbs.updateRow(true, currInfoPlayer, currState.ShortTrackName);
                    //                    driftSqlDbs.updateRow(true, currInfoPlayer, currentTrackName, modeSync, newCfg.varsLapper.SyncID, newCfg.varsLapper.SyncDir, SyncIDsToSync);
                    try
                    {
                        string[] args = new string[1];
                        args[0] = currInfoPlayer.userName;
                        newCfg.executeFunction("OnDriftPB", currInfoPlayer, args);
                    }
                    catch (Exception ex)
                    {
                        myDebug.WriteLine("err", "Error OnDriftPb " + ex.ToString());
                    }
                }
                else
                {
                    try
                    {
                        string[] args = new string[1];
                        args[0] = currInfoPlayer.userName;
                        newCfg.executeFunction("OnDriftLap", currInfoPlayer, args);
                    }
                    catch (Exception ex)
                    {
                        myDebug.WriteLine("err", "Error OnDriftLap " + ex.ToString());
                    }
                }
            }
            else
            {
                try
                {
                    string[] args = new string[1];
                    args[0] = currInfoPlayer.userName;
                    newCfg.executeFunction("OnDriftTooLow", currInfoPlayer, args);
                }
                catch (Exception ex)
                {
                    myDebug.WriteLine("err", "Error OnDriftTooLow " + ex.ToString());
                }
            }
            #endregion

            // For non drifting Update row of DB Every Laps
            currInfoPlayer.updateLap(currWr, lapDec, trackInfo.getMaxLapTime(currState.ShortTrackName, currInfoPlayer.CName), currState.ShortTrackName, newCfg.varsLapper.LapTimeUsedForPb);
            listOfPlayers.updateGap(lapDec.PLID, (int)paramLapper.maxSplit, newCfg, false);
            //            gripSqlDbs.updateRow(currInfoPlayer.isNewPBLtime | currInfoPlayer.isNewPBLap, currInfoPlayer, currentTrackName, modeSync, newCfg.varsLapper.SyncID, newCfg.varsLapper.SyncDir, SyncIDsToSync);
            string levels1 = "";
            string levels2 = "";
            bool newPB = currInfoPlayer.isNewPBLtime;
            if (newPB)
            {
                levels1 = StringLevelsAllowedPlayer(currInfoPlayer, false);
            }
            gripSqlDbs.updateRow(currInfoPlayer.isNewPBLtime | currInfoPlayer.isNewPBLap, currInfoPlayer, currState.ShortTrackName);
            if (newPB)
            {
                levels2 = StringLevelsAllowedPlayer(currInfoPlayer, false);

                for (int i = 0; i < levels2.Length; i++)
                {
                    if (levels2[i] != ',' && levels1.IndexOf(levels2[i]) == -1)
                    {
                        string[] par = new string[2];
                        try
                        {
                            int idxLevel = (int.Parse(levels2[i].ToString()));
                            par[0] = currInfoPlayer.userName;
                            par[1] = SystemAuth.LibelLevel[idxLevel];
                            newCfg.executeFunction("OnAuthReached", currInfoPlayer, par);
                        }
                        catch
                        {
                            myDebug.WriteLine("err", "Error on auth level");
                        }
                    }
                }
            }

            // On new Split
            #region Show splitting
            if (newCfg.varsLapper.ShowSplitPB && currInfoPlayer.showSplitPB)
            {
                string[] args = new string[1];
                args[0] = currInfoPlayer.userName;
                newCfg.executeFunction("OnSpbLast", currInfoPlayer, args);
            }

            {
                string[] args = new string[1];
                args[0] = currInfoPlayer.userName;
                newCfg.executeFunction("OnLap", currInfoPlayer, args);
            }
            #endregion

            #region Message relative to time do by player ( good time, incerdible time, etc... )
            string lapTimeAction = trackInfo.getLapAction(currState.ShortTrackName, currInfoPlayer.CName, lapDec.LTime);
            if (lapTimeAction != "")
            {
                string[] args = new string[1];
                args[0] = currInfoPlayer.userName;
                newCfg.executeFunction(lapTimeAction, currInfoPlayer, args);
            }
            #endregion

            #region Show if new PB
            if (currInfoPlayer.isNewPBLtime)
            {
                string CurAction = "";
                try
                {
                    if (playerFilter.ContainsKey(currInfoPlayer.userName.ToLower()))
                        CurAction = "OnPBQual";
                    else
                        CurAction = "OnPB";
                    string[] args = new string[1];
                    args[0] = currInfoPlayer.userName;
                    newCfg.executeFunction(CurAction, currInfoPlayer, args);
                }
                catch (Exception ex)
                {
                    myDebug.WriteLine("err", "Error " + CurAction + " " + ex.ToString());
                }
            }
            #endregion

            #region Max session laps
            if (currInfoPlayer.sessLaps >= newCfg.varsLapper.MaxSessionLaps)
            {
                string[] args = new string[1];
                args[0] = currInfoPlayer.userName;
                newCfg.executeFunction("OnMaxSessionLaps", currInfoPlayer, args);
            }
            #endregion

            #region pit windows open and close
            if (newCfg.varsLapper.PitWindowStop != 0 && currRace.inRace)
            {
                if ((currInfoPlayer.lapsDone + 1) == newCfg.varsLapper.PitWindowStart)
                {
                    string[] args = new string[1];
                    args[0] = currInfoPlayer.userName;
                    newCfg.executeFunction("OnBeginPitWindow", currInfoPlayer, args);
                }
                if (currInfoPlayer.lapsDone == newCfg.varsLapper.PitWindowStop)
                {
                    string[] args = new string[1];
                    args[0] = currInfoPlayer.userName;
                    newCfg.executeFunction("OnEndPitWindow", currInfoPlayer, args);
                }
            }

            #endregion
        endOfLap:
            currInfoPlayer.totaldriftscore = 0;//reset score to 0
            currInfoPlayer.maxLapSpeed = 0;//reset maxlapspeed to 0
            currInfoPlayer.bestSpeed = (double)0;

        }
        void managePacket(InSim.Decoder.SPX splitdec)
        {
            infoPlayer currInfoPlayer;
            currInfoPlayer = listOfPlayers.getPlayerByPLID(splitdec.PLID);
            if (currInfoPlayer == null)
                return;
            if (splitdec.STime == unitConv.HMSToLong(60, 0, 0))
                return;
            currInfoPlayer.updateSplit(currWr, splitdec, currState.ShortTrackName);
            listOfPlayers.updateGap( splitdec.PLID, splitdec.Split-1, newCfg ,false);
// Allow idle after firts split when a race is started
            if( currRace.inRace )
                currRace.allowIdleOnTrack = false;
            // If new splitting on PB file, save value and query save delayed DB
            if (currInfoPlayer.newPBBestSplit)
            {
//                gripSqlDbs.updateRow(true, currInfoPlayer, currentTrackName, modeSync, newCfg.varsLapper.SyncID, newCfg.varsLapper.SyncDir, SyncIDsToSync);
                gripSqlDbs.updateRow(true, currInfoPlayer, currState.ShortTrackName);
            }

            SplitNotify(currInfoPlayer, splitdec);
        }
        void managePacket(InSim.Decoder.RST rst) // Restart a Race
        {
            currRace.lapsDone = 0;
            if (newCfg.varsLapper.EnableRotation)
                currRace.racesDone++;

            currRace.started = DateTime.Now;
            currRace.allowIdleOnTrack = true;
            if (newCfg.varsLapper.AutoRestartRaceSec > 0)
            { // When a player finish a race, next race is n minute after his finish
                currRace.nextRestart = DateTime.Now.AddDays(365); // Next restart after 365 Day, ie no restart
                currRace.autoRestartOn = false;
            }
            //Raz of idle on start
            listOfPlayers.raceRestart();

            string[] par = new string[1];
            currRace.raceFlags = rst.Flags;
            currRace.raceLaps = 0;
            currRace.raceMins = 0;
            currRace.qualMins = 0;
            if (rst.QualMins == 0 && rst.RaceLaps == 0)  // In practice session
            {
                par[0] = rst.NumP.ToString();
                newCfg.executeFunction("OnPracStart", null, par);
            }
            else if (rst.QualMins == 0) // In Race Start
            {
                par[0] = rst.NumP.ToString();
                currRace.raceLaps = rst.RaceLaps;
                // Set raceLapsLeft based on LAP.RaceLaps
                if (currRace.raceLaps < 100)
                {
                    // Short race - less than 100 laps
                    currRace.raceLapsLeft = currRace.raceLaps;
                }
                else if (currRace.raceLaps < 191)
                {
                    // Long race - between 100 and 1000 laps
                    currRace.raceLapsLeft = (((currRace.raceLaps - 100) * 10) + 100);
                }
                else
                {
                    // Timed race - 1 to 48 hours
                    currRace.raceMins = ((currRace.raceLaps - 190) * 60); // Use raceMins to store race time
                    currRace.raceLapsLeft = 99;
                }
                newCfg.executeFunction("OnRaceStart", null, par);
            }
            else if (rst.RaceLaps == 0) // In Qualification
            {
                currRace.qualMins = rst.QualMins;
                par[0] = rst.NumP.ToString();
                newCfg.executeFunction("OnQualSTart", null, par);
            }
        }
        void managePacket(InSim.Decoder.FIN fin) // FINished race notification (not a final result - use IS_RES)
        {
            infoPlayer currInfoPlayer;
            currInfoPlayer = listOfPlayers.getPlayerByPLID(fin.PLID);
            if (currInfoPlayer == null)
                return;
            currInfoPlayer.allowIdleOnTrack = true;
            currInfoPlayer.finishedPLID = fin.PLID;

            string[] par = new string[1];
            par[0] = currInfoPlayer.userName;
            newCfg.executeFunction("OnFinish", currInfoPlayer, par);


        }
        void managePacket(InSim.Decoder.RES res) // RESult (qualify or confirmed finish)
        {
            if (newCfg.varsLapper.AutoRestartRaceSec > 0 && ( currRace.autoRestartOn == false || newCfg.varsLapper.AutoRestartOnFirstFinished == false ))
            { // When a player finish a race or qualify, next race is n minute after his finish
                currRace.nextRestart = DateTime.Now.AddSeconds((double)newCfg.varsLapper.AutoRestartRaceSec);
                currRace.autoRestartOn = true;
            }
            infoPlayer currInfoPlayer;
            currInfoPlayer = listOfPlayers.getPlayerByFinishedPLID(res.PLID);
            if (currInfoPlayer == null)
                return;

            currInfoPlayer.finishedPos = res.ResultNum + 1;
            currInfoPlayer.racePBTime = res.BTime;
            currInfoPlayer.raceTotalTime = res.TTime;
            string [] par = new string[2];
            par[0] = currInfoPlayer.userName;
            par[1] = res.Confirm.ToString();
            newCfg.executeFunction("OnResult", currInfoPlayer, par);

        }
        void managePacket(InSim.Decoder.REO reo)
        {
//            Console.WriteLine("Receive an ISP_REO");
            //            infoPlayer currInfoPlayer;
        }
        void managePacket(InSim.Decoder.STA newState)
        {
            if ( currState == null || currState.ShortTrackName != newState.ShortTrackName)
            {
                listOfPlayers.changeTrack(newState.ShortTrackName, gripSqlDbs, driftSqlDbs );
            }
            currState = newState;
            if (newState.RaceInProg == 1)
                currRace.inRace = true;
            else
                currRace.inRace = false;
            if (flagFirstInState == true && (newState.Flags & 512) == 512)
            {
                flagFirstInState = false;

                byte[] sism = myEncoder.ISM();
                insimConnection.Send(sism, sism.Length);
            }
        }
        void managePacket(InSim.Decoder.MSO mso)
        {
            infoPlayer currInfoPlayer;

            currInfoPlayer = listOfPlayers.getPlayerByUCID(mso.UCID);
            if (currInfoPlayer == null)
                return;

//            Console.WriteLine(mso.completeMessage);

            string nicknamewithoutcolors = currInfoPlayer.nickName;
            if (mso.UserType == 1)
            {
                if (swearWordObj.findSwearWords(mso.message)) // On swear word
                {
                    currInfoPlayer.swearWordsCount++;
                    string tmpStr;
                    if (currInfoPlayer.swearWordsCount >= newCfg.varsLapper.SwearWordsMax)
                    {
                        tmpStr = "OnSwearWords2";
                        currInfoPlayer.swearWordsCount = 0;
                    }
                    else
                    {
                        tmpStr = "OnSwearWords1";
                    }
                    string[] par = new string[1];
                    par[0] = currInfoPlayer.userName;
                    newCfg.executeFunction(tmpStr, currInfoPlayer, par);
                }
                CheckForFlooding(currInfoPlayer);

            }
            if (mso.UserType == 1 || mso.UserType == 2) 
            {
                if (mso.message.ToLower() == "!status")
                {
                    SendMsgToConnection(currInfoPlayer.UCID, "LFSLapper in working mode");
                }
                else if (mso.message.ToLower() == "!start")
                {
                    SendMsgToConnection(currInfoPlayer.UCID, "LFSLapper is already working");
                }
                else if (mso.message.ToLower() == "!stop")
                {
                    userGroups ugroup = new userGroups( myDebug );
                    ugroup.addUserFromFile("superUsers", newCfg.varsLapper.WorkingDir + "/" + newCfg.varsLapper.superUsers);
                    if (ugroup.userExist("superUsers", currInfoPlayer.userName.ToLower()) || (currInfoPlayer.UCID == 0 ) )
                    {
                        this.TermProg = true;
                        SendMsgToConnection(currInfoPlayer.UCID, "Go to stand by mode!");
                    }
                    else
                    {
                        SendMsgToConnection(currInfoPlayer.UCID, "Only for admin!");
                    }

                }
                else
                {
                    string[] par = new string[2];
                    par[0] = currInfoPlayer.userName;
                    par[1] = mso.message;
                    newCfg.executeFunction("OnMSO", currInfoPlayer, par);
                }
            }
        }
        void managePacket(DateTime datRecvPacket, InSim.Decoder.MCI mci)
        {
            infoPlayer currInfoPlayer;
			bool LastCar = false;

            #region Update stat of each player
            double xm = 0;
            double ym = 0;
            double zm = 0;
            int x = 0;
            int y = 0;
            int z = 0;

            for (int i = 0; i < System.Math.Min(8, mci.numOfPlayers); i++)
            {
                xm = mci.compCar[i].x / 65536d;
                ym = mci.compCar[i].y / 65536d;
                zm = mci.compCar[i].z / 65536d;

                x = (int)xm;
                y = (int)ym;
                z = (int)zm;

                double spm = mci.compCar[i].speed / 327.68 * 3.6; // kmh
                double dirm = mci.compCar[i].direction * 180 / 32768d;
                double headm = mci.compCar[i].heading * 180 / 32768d;
                double angm = mci.compCar[i].angvel * 180 / 8192d;
                int currPos = mci.compCar[i].Position;

                currInfoPlayer = listOfPlayers.getPlayerByPLID(mci.compCar[i].PLID);
                if (currInfoPlayer != null)
                {
                    double accStart;
                    double accStop;


                    if (currInfoPlayer.unitSpeedKmh)
                    {
                        accStart = newCfg.varsLapper.AccelerationStartSpeed;
                        accStop = newCfg.varsLapper.AccelerationEndSpeed;
                    }
                    else
                    {
                        accStart = newCfg.varsLapper.AccelerationStartSpeedMph * 1.609344;
                        accStop = newCfg.varsLapper.AccelerationEndSpeedMph * 1.609344;
                    }
                    {
                        int zone = currInfoPlayer.LastZone;
						string idZone = currInfoPlayer.idCurrZone;
                        if (x != currInfoPlayer.x || y != currInfoPlayer.y)
                        {
                            zone = listOfZoneEvent.getMyZone(currState.ShortTrackName, x, y);
							idZone = listOfZoneEvent.idZoneByIdxZone(currState.ShortTrackName,zone);
                        }
                        currInfoPlayer.UpdateState( currPos, mci.compCar[i].node,zone,idZone, x, y, z, spm, dirm, headm, angm, newCfg.varsLapper.MinimumDriftSpeed, newCfg.varsLapper.MinimumDriftAngle, newCfg.varsLapper.MaximumDriftAngle, newCfg.varsLapper.MinAngleVelocity, accStart, accStop);

						if (currInfoPlayer.lastPos != currInfoPlayer.currPos)
                        {
                            PLIDChangePos.Add( currInfoPlayer );
                        }

                    }
                }
                {
                    if ((mci.compCar[i].Infos & 128) != 0) // iterator'sbyte the Last packet
                    {
						
                        for( int idx = 0; idx < PLIDChangePos.Count; idx++ ){
                            infoPlayer tmpCurrInfoPlayer = ( infoPlayer )PLIDChangePos[idx];
                            listOfPlayers.updateGap(tmpCurrInfoPlayer.PLID, tmpCurrInfoPlayer.lastIdxSplit, newCfg, true);
							string[] args = new string[3];
							args[0] = tmpCurrInfoPlayer.userName;
							args[1] = tmpCurrInfoPlayer.lastPos.ToString();
							args[2] = tmpCurrInfoPlayer.currPos.ToString();
							newCfg.executeFunction("OnChangePos", tmpCurrInfoPlayer, args);
                        }
                        PLIDChangePos.Clear();
						LastCar = true; // Reached the last car of a set of MCI
                    }
                }

            }
            #endregion
			# region Action to do when you receive the last packet for set car
			if (LastCar)
			{
				LastCar = false;
				
				foreach (System.Collections.DictionaryEntry de in listOfPlayers.playersUCID)
				{
					currInfoPlayer = (infoPlayer)de.Value;



					#region check for instunt time player
					if (currInfoPlayer.stuntticks == newCfg.varsLapper.MaxNbInStunt)
					{
						string[] args = new string[1];
						args[0] = currInfoPlayer.userName;
						newCfg.executeFunction("OnMaxNbInStunt", currInfoPlayer, args);
					}
					else if (currInfoPlayer.stuntticks == 1)
					{
						string[] args = new string[1];
						args[0] = currInfoPlayer.userName;
						newCfg.executeFunction("OnAngleVelocity", currInfoPlayer, args);
					}
					#endregion

					#region check for idle time
					{
						if (currRace.allowIdleOnTrack == true || currInfoPlayer.allowIdleOnTrack == true || currRace.inRace == false || currInfoPlayer.OnPit == true || currInfoPlayer.OnTrack == false)
						{ // if not in race, Force IdleTick for each player to 0
							currInfoPlayer.idleticks = 0;
						}
					}
					if (currInfoPlayer.idleticks > (newCfg.varsLapper.OnIdleTimeout2 * 10))
					{
						string[] args = new string[1];
						args[0] = currInfoPlayer.userName;
						newCfg.executeFunction("OnIdle2", currInfoPlayer, args);
						currInfoPlayer.idleticks = 0; //reset to zero afterwards, don't want flooding
						currInfoPlayer.OnIdleAction1Sended = false;
					}
					if ((currInfoPlayer.idleticks > (newCfg.varsLapper.OnIdleTimeout1 * 10)) && (currInfoPlayer.OnIdleAction1Sended == false))
					{
						string[] args = new string[1];
						args[0] = currInfoPlayer.userName;
						newCfg.executeFunction("OnIdle1", currInfoPlayer, args);
						currInfoPlayer.OnIdleAction1Sended = true;
						//                                    p.idleticks = 0; //reset to zero afterwards, don't want flooding
					}
					#endregion

					#region write current drift score && event on drift reset score
					if (currInfoPlayer.driftticks == 0 && currInfoPlayer.lastdriftscore > 0 && currInfoPlayer.totaldriftscore > 0)
					{
						{
							string[] args = new string[1];
							args[0] = currInfoPlayer.userName;
							newCfg.executeFunction("OnDriftScore", currInfoPlayer, args);
						}

						if (currInfoPlayer.lastdriftscore >= newCfg.varsLapper.GoodDriftScore)
						{
							string[] args = new string[1];
							args[0] = currInfoPlayer.userName;
							newCfg.executeFunction("OnGoodDrift", currInfoPlayer, args);
						}

						currInfoPlayer.lastdriftscore = 0;
					}
					if (currInfoPlayer.isDriftReset) // Drift score reset due to low speed
					{
						{
							string[] args = new string[1];
							args[0] = currInfoPlayer.userName;
							newCfg.executeFunction("OnDriftResetScore", currInfoPlayer, args);
						}
					}
					#endregion

					#region acceleration 0-100
					if (currInfoPlayer.accelerationTimeValid && currInfoPlayer.accelerationTime.TotalSeconds <= newCfg.varsLapper.AccelerationPrivateMaxTime)
					{
						currInfoPlayer.accelerationTimeValid = false;
						string[] args = new string[1];
						args[0] = currInfoPlayer.userName;
						newCfg.executeFunction("OnAcceleration", currInfoPlayer, args);
					}
					#endregion

					#region Action if player is to slow to complete a lap
					if (currRace.inRace && currInfoPlayer.OnTrack)
					{
						if (newCfg.varsLapper.MaxAllowedLapTime1 != -1)
						{
							long second = (long)(((double)newCfg.varsLapper.MaxAllowedLapTime1 / 100) * ((double)currInfoPlayer.MaxLapTime / 1000));
							if (!currInfoPlayer.MaxLapTime1Reached && currInfoPlayer.LastCrossedFinishLine.AddSeconds((double)second) < DateTime.Now)
							{
								string[] args = new string[1];
								args[0] = currInfoPlayer.userName;
								newCfg.executeFunction("OnMaxAllowedLapTime1", currInfoPlayer, args);
								currInfoPlayer.MaxLapTime1Reached = true;
							}
						}
						if (newCfg.varsLapper.MaxAllowedLapTime2 != -1)
						{
							long second = (long)(((double)newCfg.varsLapper.MaxAllowedLapTime2 / 100) * ((double)currInfoPlayer.MaxLapTime / 1000));
							if (!currInfoPlayer.MaxLapTime2Reached && currInfoPlayer.LastCrossedFinishLine.AddSeconds((double)second) < DateTime.Now)
							{
								string[] args = new string[1];
								args[0] = currInfoPlayer.userName;
								newCfg.executeFunction("OnMaxAllowedLapTime2", currInfoPlayer, args);
								currInfoPlayer.MaxLapTime2Reached = true;
							}
						}
					}
					#endregion

					#region speedTrap Node Event
					if (currInfoPlayer.CurrNode != currInfoPlayer.LastNode)
					{
						if (currInfoPlayer.LastNode != -1)
						{
							string cmdNode = listOfNodeEvent.getCommandNodeLeave(currState.ShortTrackName, currInfoPlayer.LastNode);
							if (cmdNode != "")
							{
								string[] args = new string[1];
								args[0] = currInfoPlayer.userName;
								newCfg.executeFunction(cmdNode, currInfoPlayer, args);
							}
						}
						currInfoPlayer.LastNode = currInfoPlayer.CurrNode;
						{
							string cmdNode = listOfNodeEvent.getCommandNodeEnter(currState.ShortTrackName, currInfoPlayer.CurrNode);
							if (cmdNode != "")
							{
								string[] args = new string[1];
								args[0] = currInfoPlayer.userName;
								newCfg.executeFunction(cmdNode, currInfoPlayer, args);
							}
						}
					}
					#endregion

					#region speedTrap Zone Event
					if (currInfoPlayer.CurrZone != currInfoPlayer.LastZone)
					{
						if (currInfoPlayer.LastZone != -1)
						{
							string cmdZone = listOfZoneEvent.getCommandZoneLeave(currState.ShortTrackName, currInfoPlayer.LastZone);
							if (cmdZone != "")
							{
								string[] args = new string[1];
								args[0] = currInfoPlayer.userName;
								newCfg.executeFunction(cmdZone, currInfoPlayer, args);
							}
						}
						currInfoPlayer.LastZone = currInfoPlayer.CurrZone;
						if (currInfoPlayer.CurrZone != -1)
						{
							string cmdZone = listOfZoneEvent.getCommandZoneEnter(currState.ShortTrackName, currInfoPlayer.CurrZone);
							if (cmdZone != "")
							{
								string[] args = new string[1];
								args[0] = currInfoPlayer.userName;
								newCfg.executeFunction(cmdZone, currInfoPlayer, args);
							}
						}
					}
					#endregion

					#region check for player have done disToDo
					if (newCfg.varsLapper.distToDo != -1 && currInfoPlayer.totDistMeter > currInfoPlayer.lastTotDistMeter + newCfg.varsLapper.distToDo)
					{
						currInfoPlayer.lastTotDistMeter += newCfg.varsLapper.distToDo;
						string[] args = new string[1];
						args[0] = currInfoPlayer.userName;
						newCfg.executeFunction("OnDistDone", currInfoPlayer, args);
					}
					#endregion
				}
			}
			#endregion

		}
        void managePacket(InSim.Decoder.ISM ism)
        {
            currentHName = ism.HName;
            if (newCfg.varsLapper.EnableRegisterWeb)
            {
                objregisterWeb.HName = ism.HName;
                objregisterWeb.Ver = LFSLapper.getShortVersion(4);
            }
            HName = ism.HName;
            // Get All Connections
            System.Threading.Thread.Sleep(100);
            byte[] ismncn = myEncoder.NCN();
            insimConnection.Send(ismncn, ismncn.Length);
            // Get All Players
            System.Threading.Thread.Sleep(100);
            byte[] ismnpl = myEncoder.NPL();
            insimConnection.Send(ismnpl, ismnpl.Length);

        }
        void managePacket(InSim.Decoder.PEN pen)
        {
            infoPlayer currInfoPlayer;
            currInfoPlayer = listOfPlayers.getPlayerByPLID(pen.PLID);
            if (currInfoPlayer == null)
                return;
			currInfoPlayer.PenaltyNew = pen.NewPen;
            OnPenalties(currInfoPlayer, pen);
        }
        void managePacket(InSim.Decoder.BFN bfn)
        {
            infoPlayer currInfoPlayer;

            currInfoPlayer = listOfPlayers.getPlayerByUCID(bfn.UCID);
            if (currInfoPlayer == null)
                return;
            if (currInfoPlayer.PLID == -1)
                return;
            if (bfn.SubT == (int)InSim.bfn.BFN_USER_CLEAR)
                currInfoPlayer.playerBox.deleteAllButton(currInfoPlayer, newCfg);
            {
                string[] args = new string[2];
                args[0] = currInfoPlayer.userName;
                args[1] = bfn.SubT.ToString();
                newCfg.executeFunction("OnButtonFunction", currInfoPlayer, args);
            }
        }
        void managePacket(InSim.Decoder.BTC btc)
        {
            infoPlayer currInfoPlayer;

            currInfoPlayer = listOfPlayers.getPlayerByUCID(btc.UCID);
            if (currInfoPlayer == null)
                return;
            string[] par = new string[2];
            par[0] = btc.CFlags.ToString();
            par[1] = currInfoPlayer.playerBox.getId(btc.ClickID, btc.UCID);
            string doAct = currInfoPlayer.playerBox.doAction(btc.ClickID, btc.UCID);
            ISMB.executeButtonFunction exeFunc = currInfoPlayer.playerBox.doActionBC(btc.ClickID, btc.UCID);
            if( doAct != "" )
                newCfg.executeFunction(doAct, currInfoPlayer, par);
            if (exeFunc != null)
                exeFunc(ISMB.typExeFunc.TYPE_BUTTON,currInfoPlayer, btc.CFlags, par[1].ToString());
        }
        void managePacket(InSim.Decoder.BTT btt)
        {
            infoPlayer currInfoPlayer;

            currInfoPlayer = listOfPlayers.getPlayerByUCID(btt.UCID);
            if (currInfoPlayer == null)
                return;
			string[] par = new string[2];
			par[0] = currInfoPlayer.playerBox.getId(btt.ClickID, btt.UCID);
			par[1] = btt.Text.Trim();
			string doAct = currInfoPlayer.playerBox.doAction(btt.ClickID, btt.UCID);
            ISMB.executeButtonFunction exeFunc = currInfoPlayer.playerBox.doActionBC(btt.ClickID, btt.UCID);
            if (doAct != "")
                newCfg.executeFunction(doAct, currInfoPlayer, par);
            if (exeFunc != null)
                exeFunc(ISMB.typExeFunc.TYPE_TEXT, currInfoPlayer, 0,par[0].ToString());
        }
        void managePacket(InSim.Decoder.TOC toc)
        {
            infoPlayer currInfoPlayer;

            infoPlayer oldCurrInfoPlayer = listOfPlayers.getPlayerByPLID(toc.PLID);
            if (oldCurrInfoPlayer == null)
                return;

            listOfPlayers.removeIndexPLID(toc.PLID); // Remove PLID OF oldPlayer
            listOfPlayers.majIndexPLID(toc.NewUCID, toc.PLID); // Attach PLID to new player

            currInfoPlayer = listOfPlayers.getPlayerByPLID(toc.PLID);
            currInfoPlayer.P_Mass = oldCurrInfoPlayer.P_Mass;
            currInfoPlayer.P_TRes = oldCurrInfoPlayer.P_TRes;
            currInfoPlayer.CName = oldCurrInfoPlayer.CName;
            // Retreive Info of this Player in DBS

            gripSqlDbs.retreiveRow(currInfoPlayer, currState.ShortTrackName);
            driftSqlDbs.retreiveRow(currInfoPlayer, currState.ShortTrackName);

            currInfoPlayer.OnTrack = true;
            
            currInfoPlayer.LastCrossedFinishLine = DateTime.Now;
            currInfoPlayer.MaxLapTime1Reached = false;
            currInfoPlayer.MaxLapTime2Reached = false;
        }
        void managePacket(InSim.Decoder.PIT pit)
        {
            infoPlayer currInfoPlayer;
            int FL_Changed = 0;
            int FR_Changed = 0;
            int RL_Changed = 0;
            int RR_Changed = 0;


            currInfoPlayer = listOfPlayers.getPlayerByPLID(pit.PLID);
            if (currInfoPlayer == null)
                return;
            currInfoPlayer.OnPit = true;
            currInfoPlayer.pitWork = pit.sWork;
            if ((pit.Work & (long)InSim.PIT_work.PSE_LE_FR_WHL) != 0)
                FL_Changed = 1;
            if ((pit.Work & (long)InSim.PIT_work.PSE_RI_FR_WHL) != 0)
                FR_Changed = 1;
            if ((pit.Work & (long)InSim.PIT_work.PSE_LE_RE_WHL) != 0)
                RL_Changed = 1;
            if ((pit.Work & (long)InSim.PIT_work.PSE_RI_RE_WHL) != 0)
                RR_Changed = 1;
            currInfoPlayer.tyreFrontLeft = currInfoPlayer.oldTyreFrontLeft;
            currInfoPlayer.tyreFrontRight = currInfoPlayer.oldTyreFrontRight;
            currInfoPlayer.tyreRearLeft = currInfoPlayer.oldTyreRearLeft;
            currInfoPlayer.tyreRearRight = currInfoPlayer.oldTyreRearRight;

            if( (int)pit.TyreRearLeft != 255 )
                currInfoPlayer.tyreRearLeft = pit.TyreRearLeft;
            if ((int)pit.TyreRearRight != 255)
                currInfoPlayer.tyreRearRight = pit.TyreRearRight;
            if ((int)pit.TyreFrontLeft != 255)
                currInfoPlayer.tyreFrontLeft = pit.TyreFrontLeft;
            if ((int)pit.TyreFrontRight != 255)
                currInfoPlayer.tyreFrontRight = pit.TyreFrontRight;
            {
                string[] args = new string[1];
                args[0] = currInfoPlayer.userName;
                newCfg.executeFunction("OnBeginPit", currInfoPlayer, args);
            }

            if (newCfg.varsLapper.PitWindowStop != 0 && currRace.inRace)
            {
                if ((currInfoPlayer.lapsDone + 1) < newCfg.varsLapper.PitWindowStart || (currInfoPlayer.lapsDone + 1) > newCfg.varsLapper.PitWindowStop)
                {
                    string[] args = new string[1];
                    args[0] = currInfoPlayer.userName;
                    newCfg.executeFunction("OnNotPitWindow", currInfoPlayer, args);
                }
            }
            {
                if (FL_Changed != 0 || FR_Changed != 0 || RL_Changed != 0 || RR_Changed != 0)
                {
                    string[] args = new string[5];
                    args[0] = currInfoPlayer.userName;
                    args[1] = FL_Changed.ToString();
                    args[2] = FR_Changed.ToString();
                    args[3] = RL_Changed.ToString();
                    args[4] = RR_Changed.ToString();
                    newCfg.executeFunction("OnChangeTyres", currInfoPlayer,args);
                }
            }


        }
        void managePacket(InSim.Decoder.PSF psf)
        {
            infoPlayer currInfoPlayer;
            currInfoPlayer = listOfPlayers.getPlayerByPLID(psf.PLID);
            if (currInfoPlayer == null)
                return;
            currInfoPlayer.OnPit = false;
            currInfoPlayer.totalPitTime += psf.STime;
            currInfoPlayer.pitTime = psf.STime;
            currInfoPlayer.pitStops++;

            string[] args = new string[1];
            args[0] = currInfoPlayer.userName;
            newCfg.executeFunction("OnEndPit", currInfoPlayer, args);

        }

        void managePacket(InSim.Decoder.SMALL small)
        {
//            Console.WriteLine("ReqI " + small.ReqI);
            switch (small.SubT)
            {
                case "SMALL_VTA":
                    if (small.uval == (long)InSim.vtn.VOTE_RESTART ){
                        if (currVotation.allowRestart)
                        {
                            currVotation.allowRestart = false;
//                            Console.WriteLine("Send a new ISP_REO"); 
                            setReorder(newCfg.varsLapper.ReorderGrid); // Set the reorder relative TO PB
                        }
                        else
                        {
                            if (newCfg.varsLapper.VoteRestart != -1)
                            {
                                byte[] vtc = myEncoder.VTC();
                                insimConnection.Send(vtc, vtc.Length);
                                return;
                            }
                        }
                    }
                    else if (small.uval == (long)InSim.vtn.VOTE_QUALIFY)
                    {
                        if (currVotation.allowQualify)
                        {
                            currVotation.allowQualify = false;
                        }
                        else
                        {
                            if (newCfg.varsLapper.VoteQualify != -1)
                            {
                                byte[] vtc = myEncoder.VTC();
                                insimConnection.Send(vtc, vtc.Length);
                                return;
                            }
                        }
                    }
                    else if (small.uval == (long)InSim.vtn.VOTE_END)
                    {
                        if (currVotation.allowEnd)
                        {
                            currVotation.allowEnd = false;
                        }
                        else
                        {
                            if (newCfg.varsLapper.VoteEnd != -1)
                            {
                                byte[] vtc = myEncoder.VTC();
                                insimConnection.Send(vtc, vtc.Length);
                                return;
                            }
                        }
                    }
                    listOfPlayers.voteClear();
                    break;
            }
        }

        void managePacket(InSim.Decoder.VTN vtn)
        {
            infoPlayer currInfoPlayer;
            currInfoPlayer = listOfPlayers.getPlayerByUCID(vtn.UCID);
            if (currInfoPlayer == null)
                return;
//            Console.WriteLine("Votation FROM " + currInfoPlayer.nickName);
// Ignore votation from admin, but do action
            if (vtn.UCID == 0)
            {
                if( vtn.Action == (int)InSim.vtn.VOTE_END )
                    currVotation.allowEnd = true;
                if (vtn.Action == (int)InSim.vtn.VOTE_RESTART)
                    currVotation.allowRestart = true;
                if (vtn.Action == (int)InSim.vtn.VOTE_QUALIFY)
                    currVotation.allowQualify = true;
            }
            else
            {
                if (currRace.inRace == false || (currRace.inRace && currRace.lapsDone >= InRaceLapsVoteMin && currRace.lapsDone <= InRaceLapsVoteMax))
                {
                    currInfoPlayer.currVote = vtn.Action;
                    currInfoPlayer.expireVote = DateTime.Now.AddSeconds(newCfg.varsLapper.VoteLifeSec);
                }
                else
                {
                    currInfoPlayer.currVote = (int)InSim.vtn.VOTE_NONE;
                    currInfoPlayer.expireVote = DateTime.Now.AddSeconds(newCfg.varsLapper.VoteLifeSec);
                }
            }

        }
        void managePacket(InSim.Decoder.PFL pfl)
        {
            infoPlayer currInfoPlayer;
            currInfoPlayer = listOfPlayers.getPlayerByPLID(pfl.PLID);
            if (currInfoPlayer == null)
                return;
            if (currInfoPlayer.OnTrack)
                actionPlayerFlags(pfl.Flags, currInfoPlayer, true);

        }
        void managePacket(InSim.Decoder.PLA pla)
        {
            infoPlayer currInfoPlayer;
            currInfoPlayer = listOfPlayers.getPlayerByPLID(pla.PLID);
            if (currInfoPlayer == null)
                return;
            if (pla.fact == InSim.Decoder.fact.PITLANE_EXIT)
            {
                string[] args = new string[1];
                args[0] = currInfoPlayer.userName;
                newCfg.executeFunction("OnExitPitLane", currInfoPlayer, args);
            }
            else
            {
                string[] args = new string[2];
                string fact = "";
                try
                {
                    fact = Enum.GetName(typeof(InSim.Decoder.fact), pla.fact);
                }
                catch
                {
                }
                if (fact == "")
                    fact = "UKNOWN";
                fact = fact.Remove(0, 8);

                args[0] = currInfoPlayer.userName;
                args[1] = fact;
                newCfg.executeFunction("OnEnterPitLane", currInfoPlayer, args);
            }


        }
    }

}
