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

#define MONO

using System;
using System.Collections;
using System.Threading;

namespace InSim
{
    public enum tyre
    {
        TYRE_R1,            // 0
        TYRE_R2,            // 1
        TYRE_R3,            // 2
        TYRE_R4,            // 3
        TYRE_ROAD_SUPER,    // 4
        TYRE_ROAD_NORMAL,   // 5
        TYRE_HYBRID,        // 6
        TYRE_KNOBBLY,       // 7
        TYRE_NUM,
        TYRE_NO_CHANGE,
    };
    enum vtn
    {
        VOTE_NONE,          // 0 - no vote
        VOTE_END,           // 1 - end race
        VOTE_RESTART,       // 2 - restart
        VOTE_QUALIFY,       // 3 - qualify
        VOTE_NUM
    };

    enum bfn
    {
        BFN_DEL_BTN,        //  0 - instruction     : delete one button (must set ClickID)
        BFN_CLEAR,          //  1 - instruction		: clear all buttons made by this insim instance
        BFN_USER_CLEAR,     //  2 - info            : user cleared this insim instance's buttons
        BFN_REQUEST,        //  3 - user request    : SHIFT+B or SHIFT+I - request for buttons
    };

    enum pen
    {
        PENALTY_NONE,		// 0		
        PENALTY_DT,			// 1
        PENALTY_DT_VALID,	// 2
        PENALTY_SG,			// 3
        PENALTY_SG_VALID,	// 4
        PENALTY_30,			// 5
        PENALTY_45,			// 6
        PENALTY_NUM
    }
    enum penr
    {
        PENR_UNKNOWN,       // 0 - unknown or cleared penalty
        PENR_ADMIN,         // 1 - penalty given by admin
        PENR_WRONG_WAY,     // 2 - wrong way driving
        PENR_FALSE_START,   // 3 - starting before green light
        PENR_SPEEDING,      // 4 - speeding in pit lane
        PENR_STOP_SHORT,    // 5 - stop-go pit stop too short
        PENR_STOP_LATE,     // 6 - compulsory stop is too late
        PENR_NUM
    };

    enum obh
    {
        OBH_LAYOUT,		//1		// an added object
        OBH_CAN_MOVE,	//2		// a movable object
        OBH_WAS_MOVING,	//4		// was moving before this hit
        OBH_ON_SPOT		//8		// object in original position
    };
    enum uco
    {
        UCO_CIRCLE_ENTER,   //1   entered a circle
        UCO_CIRCLE_LEAVE,   //2   left a circle
        UCO_CP_FWD,         //4   crossed cp in forward direction
        UCO_CP_REV,         //8   crossed cp in reverse direction
    };

    enum PMOAction
    {
        PMO_LOADING_FILE,   // 0 - sent by the layout loading system only
        PMO_ADD_OBJECTS,    // 1 - adding objects (from InSim or editor)
        PMO_DEL_OBJECTS,    // 2 - delete objects (from InSim or editor)
        PMO_CLEAR_ALL,      // 3 - clear all objects (NumO must be zero)
        PMO_TINY_AXM,       // 4 - a reply to a TINY_AXM request
        PMO_TTC_SEL,        // 5 - a reply to a TTC_SEL request
        PMO_SELECTION,      // 6 - set a connection's layout editor selection
        PMO_POSITION,       // 7 - user pressed O without anything selected
        PMO_GET_Z,          // 8 - request Z values / reply with Z values
        PMO_NUM
    };

    enum mode
    {
        CIM_NORMAL,         // 0 - not in a special mode
        CIM_OPTIONS,        // 1
        CIM_HOST_OPTIONS,   // 2
        CIM_GARAGE,         // 3
        CIM_CAR_SELECT,     // 4
        CIM_TRACK_SELECT,   // 5
        CIM_SHIFTU,         // 6 - free view mode
        CIM_NUM
    };

    enum SubMode
    {
        FVM_PLAIN,          // 1no buttons displayed
        FVM_BUTTONS,        // 2buttons displayed (not editing)
        FVM_SP2,            // 3reserved
        FVM_SP3,            // 4reserved
        FVM_EDIT_CHALK,     // 5    
        FVM_EDIT_CONES,     // 6
        FVM_EDIT_TYRES,     // 7
        FVM_EDIT_MARKERS,   // 8
        FVM_EDIT_OTHER,     // 9
        FVM_EDIT_CONCRETE,  // 10
        FVM_EDIT_CONTROL,   // 11
        FVM_EDIT_MARSH,     // 12
        FVM_NUM             // 13
    };

    enum BTN_style
    {
        ISB_C1 = 1,
        ISB_C2 = 2,
        ISB_C4 = 4,
        ISB_CLICK = 8,
        ISB_LIGHT = 16,
        ISB_DARK = 32,
        ISB_LEFT = 64,
        ISB_RIGHT = 128
    };
    enum PIT_work
    {
        PSE_NOTHING = 0,        // bit 0 (1)
        PSE_STOP = 2,           // bit 1 (2)
        PSE_FR_DAM = 4,         // bit 2 (4)
        PSE_FR_WHL = 8,         // etc...
        PSE_LE_FR_DAM = 16,
        PSE_LE_FR_WHL = 32,
        PSE_RI_FR_DAM = 64,
        PSE_RI_FR_WHL = 128,
        PSE_RE_DAM = 256,
        PSE_RE_WHL = 512,
        PSE_LE_RE_DAM = 1024,
        PSE_LE_RE_WHL = 2048,
        PSE_RI_RE_DAM = 4096,
        PSE_RI_RE_WHL = 8192,
        PSE_BODY_MINOR = 16384,
        PSE_BODY_MAJOR = 32768,
        PSE_SETUP = 65536,
        PSE_REFUEL = 131072,
    };
    enum confirm
    {
        CONF_MENTIONED = 1,
        CONF_CONFIRMED = 2,
        CONF_PENALTY_DT = 4,
        CONF_PENALTY_SG = 8,
        CONF_PENALTY_30 = 16,
        CONF_PENALTY_45 = 32,
        CONF_DID_NOT_PIT = 64
    }

    enum ISF
    {
        RES_0 = 1,
        RES_1 = 2,	    // bit 1 : spare
        LOCAL = 4,	    // bit 2 : spare
        MSO_COLS = 8,	// bit 3 : spare 
        NLP = 16,	    // bit 4 : set to receive NLP packets
        MCI = 32,	    // bit 5 : set to receive MCI packets
        CON = 64,	    // bit 6 : set to receive CON packets
        OBH = 128,      // bit 7 : set to receive OBH packets
        HLV = 256,      // bit  8 : receive HLV packets
        AXM_LOAD = 512, // bit  9 : receive AXM when loading a layout
        AXM_EDIT = 1024, // bit 10 : receive AXM when changing objects
        REQ_JOIN = 2048	// bit 11 : process join requests
    }
    enum TypePack
    {
        ISP_NONE,       //  0					: not used
        ISP_ISI,        //  1 - instruction		: insim initialise
        ISP_VER,        //  2 - info			: version info
        ISP_TINY,       //  3 - both ways		: multi purpose
        ISP_SMALL,      //  4 - both ways		: multi purpose
        ISP_STA,        //  5 - info			: state info
        ISP_SCH,        //  6 - instruction		: single character
        ISP_SFP,        //  7 - instruction		: state flags pack
        ISP_SCC,        //  8 - instruction		: set car camera
        ISP_CPP,        //  9 - both ways		: cam pos pack
        ISP_ISM,        // 10 - info			: start multiplayer
        ISP_MSO,        // 11 - info			: message out
        ISP_III,        // 12 - info			: hidden /i message
        ISP_MST,        // 13 - instruction		: type message or /command
        ISP_MTC,        // 14 - instruction		: message to a connection
        ISP_MOD,        // 15 - instruction		: set screen mode
        ISP_VTN,        // 16 - info			: vote notification
        ISP_RST,        // 17 - info			: race start
        ISP_NCN,        // 18 - info			: new connection
        ISP_CNL,        // 19 - info			: connection left
        ISP_CPR,        // 20 - info			: connection renamed
        ISP_NPL,        // 21 - info			: new player (joined race)
        ISP_PLP,        // 22 - info			: player pit (keeps slot in race)
        ISP_PLL,        // 23 - info			: player leave (spectate - loses slot)
        ISP_LAP,        // 24 - info			: lap time
        ISP_SPX,        // 25 - info			: split x time
        ISP_PIT,        // 26 - info			: pit stop start
        ISP_PSF,        // 27 - info			: pit stop finish
        ISP_PLA,        // 28 - info			: pit lane enter / leave
        ISP_CCH,        // 29 - info			: camera changed
        ISP_PEN,        // 30 - info			: penalty given or cleared
        ISP_TOC,        // 31 - info			: take over car
        ISP_FLG,        // 32 - info			: flag (yellow or blue)
        ISP_PFL,        // 33 - info			: player flags (help flags)
        ISP_FIN,        // 34 - info			: finished race
        ISP_RES,        // 35 - info			: result confirmed
        ISP_REO,        // 36 - both ways		: reorder (info or instruction)
        ISP_NLP,        // 37 - info			: node and lap packet
        ISP_MCI,        // 38 - info			: multi car info
        ISP_MSX,        // 39 - instruction		: type message
        ISP_MSL,        // 40 - instruction		: message to local computer
        ISP_CRS,        // 41 - info			: car reset
        ISP_BFN,        // 42 - both ways		: delete buttons / receive button requests
        ISP_AXI,        // 43 - info			: autocross layout information
        ISP_AXO,        // 44 - info			: hit an autocross object
        ISP_BTN,        // 45 - instruction		: show a button on local or remote screen
        ISP_BTC,        // 46 - info			: sent when a user clicks a button
        ISP_BTT,        // 47 - info			: sent after typing into a button
        ISP_RIP,        // 48 - both ways		: replay information packet
        ISP_SSH,        // 49 - both ways		: screenshot
        ISP_CON,        // 50 - info			: contact between cars (collision report)
        ISP_OBH,        // 51 - info			: contact car + object (collision report)
        ISP_HLV,        // 52 - info			: report incidents that would violate HLVC
        ISP_PLC,        // 53 - instruction		: player cars
        ISP_AXM,        // 54 - both ways		: autocross multiple objects
        ISP_ACR,        // 55 - info			: admin command report
        ISP_HCP,        // 56 - instruction		: car handicaps
        ISP_NCI,        // 57 - info			: new connection - extra info for host
        ISP_JRR,        // 58 - instruction		: reply to a join request (allow / disallow)
        ISP_UCO,        // 59 - info			: report InSim checkpoint / InSim circle
        ISP_OCO,        // 60 - instruction		: object control (currently used for lights)
        ISP_TTC,        // 61 - instruction		: multi purpose - target to connection
        ISP_SLC,        // 62 - info			: connection selected a car
        ISP_CSC,        // 63 - info			: car state changed
        ISP_CIM,		// 64 - info			: connection's interface mode
    };
    enum TypeTiny
    {
        TINY_NONE,      //  0 - keep alive		: see "maintaining the connection"
        TINY_VER,       //  1 - info request	: get version
        TINY_CLOSE,     //  2 - instruction		: close insim
        TINY_PING,      //  3 - ping request	: external progam requesting a reply
        TINY_REPLY,     //  4 - ping reply		: reply to a ping request
        TINY_VTC,       //  5 - both ways		: game vote cancel (info or request)
        TINY_SCP,       //  6 - info request	: send camera pos
        TINY_SST,       //  7 - info request	: send state info
        TINY_GTH,       //  8 - info request	: get time in hundredths (i.e. SMALL_RTP)
        TINY_MPE,       //  9 - info			: multi player end
        TINY_ISM,       // 10 - info request	: get multiplayer info (i.e. ISP_ISM)
        TINY_REN,       // 11 - info			: race end (return to race setup screen)
        TINY_CLR,       // 12 - info			: all players cleared from race
        TINY_NCN,       // 13 - info request	: get NCN for all connections
        TINY_NPL,       // 14 - info request	: get all players
        TINY_RES,       // 15 - info request	: get all results
        TINY_NLP,       // 16 - info request	: send an IS_NLP
        TINY_MCI,       // 17 - info request	: send an IS_MCI
        TINY_REO,       // 18 - info request	: send an IS_REO
        TINY_RST,       // 19 - info request	: send an IS_RST
        TINY_AXI,       // 20 - info request	: send an IS_AXI - AutoX Info
        TINY_AXC,       // 21 - info			: autocross cleared
        TINY_RIP,       // 22 - info request	: send an IS_RIP - Replay Information Packet
        TINY_NCI,       // 23 - info request	: get NCI for all guests (on host only)
        TINY_ALC,       // 24 - info request	: send a SMALL_ALC (allowed cars)
        TINY_AXM,       // 25 - info request	: send IS_AXM packets for the entire layout
        TINY_SLC, 		// 26 - info request	: send IS_SLC packets for all connections
    };
    enum TypeSmall // the fourth byte of IS_SMALL packets is one of these
    {
        SMALL_NONE,     //  0					: not used
        SMALL_SSP,      //  1 - instruction		: start sending positions
        SMALL_SSG,      //  2 - instruction		: start sending gauges
        SMALL_VTA,      //  3 - report			: vote action
        SMALL_TMS,      //  4 - instruction		: time stop
        SMALL_STP,      //  5 - instruction		: time step
        SMALL_RTP,      //  6 - info			: race time packet (reply to GTH)
        SMALL_NLI,      //  7 - instruction		: set node lap interval
        SMALL_ALC,      //  8 - both ways		: set or get allowed cars (TINY_ALC)
        SMALL_LCS,		//  9 - instruction		: set local car switches (lights, horn, siren)
    };
    enum TypeTTC // the fourth byte of IS_SMALL packets is one of these
    {
        TTC_NONE,       //  0					: not used
        TTC_SEL,        //  1 - info request	: send IS_AXM for a layout editor selection
        TTC_SEL_START,  //  2 - info request	: send IS_AXM every time the selection changes
        TTC_SEL_STOP,	//  3 - instruction		: switch off IS_AXM requested by TTC_SEL_START
    }

    public class Connect
    {
        public class ConnectException : Exception
        {
            public ConnectException(string message) : base(message) { }
            public ConnectException(string message, Exception innerException) : base(message, innerException) { }
        }
        public class objPacket
        {
            public DateTime dateReceived;
            public byte[] recvPacket;
            public objPacket(byte[] precvPacket)
            {
                recvPacket = precvPacket;
                dateReceived = DateTime.Now;
            }
        }

        public bool connected = false;
        public string Product;
        public string Version;
        public string HostName;
        public int InSimVersion;
        private System.Net.Sockets.UdpClient uc;
        private TcpConnection.Connection tc;
        private bool TCPmode;
        bool waitReceiveLow = true;
        public bool waitReceive = true;
        private Encoder myEncoder = new Encoder();
        private bool Local;
        private string Host;
        private int Port;
        private GLDebug.Debug myDebug;
        public Queue InsimPacks = new Queue(500);
        public Thread TInsimReceive = null;


        System.Net.IPEndPoint remoteEP = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);

        public Connect(GLDebug.Debug pmyDebug)
        {
            this.myDebug = pmyDebug;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        /// <param name="adminPassword"></param>
        /// <param name="mode"></param>
        /// <param name="nameApp"></param>
        /// <param name="isLocal"></param>
        /// <param name="TCPmode"></param>
        /// <param name="receiveJoinReq"></param>
        /// <param name="disableAIcars"></param>
        public void insimConnect(string host, int port, string adminPassword, string mode, string nameApp, bool isLocal, bool TCPmode, int receiveJoinReq, int disableAIcars)
        {
            this.TCPmode = TCPmode;
            this.Host = host;
            this.Port = port;
            this.Local = isLocal;
                if (this.TCPmode)
                    insimConnectTCP(host, port, adminPassword, mode, nameApp, isLocal, receiveJoinReq, disableAIcars);
                else
                    insimConnectUDP(host, port, adminPassword, mode, nameApp, isLocal, receiveJoinReq, disableAIcars);
            
            // Start Thread Receiving packet
            // Why a thread, because of Lagging option
            TInsimReceive = new Thread(new ThreadStart(this.TInSimReceive));
            TInsimReceive.Start();
        }

        public void insimConnectTCP(string host, int port, string adminPassword, string mode, string nameApp, bool isLocal, int receiveJoinReq, int disableAIcars)
        {
            int nbTry = 0;
            int maxTry = 2;

            tc = new TcpConnection.Connection(host, port);
            myDebug.Write("mss", "Connecting to:  [" + host + " / " + port + "]");
        retryConnect:
            try
            {
                tc.Connect();
            }
            catch
            {
                if (nbTry > maxTry)
                {
                    myDebug.printDateOnEachLine = false;
                    myDebug.WriteLine("mss", "Ko");
                    myDebug.printDateOnEachLine = true;
                    throw new ConnectException("Lapper Instance  [" + host + "/" + port + "] failed connecting with TCP connection!");

                }
                else
                {
                    Console.WriteLine("Lapper Instance  [" + host + "/" + port + "] failed connecting!");
                    System.Threading.Thread.Sleep(200);
                    myDebug.printDateOnEachLine = false;
                    myDebug.Write("mss", ".");
                    myDebug.printDateOnEachLine = true;
                    goto retryConnect;
                }

            }
            if (adminPassword.Length > 16)
            {
                Console.WriteLine("Wrong length adminPassword! :'" + adminPassword + "' max 16 characters");
                myDebug.WriteLine("mss", "[" + host + " / " + port + "] Connection failed: Wrong length adminpass in Lapper!!");
            }

            myDebug.printDateOnEachLine = false;
            myDebug.WriteLine("mss", "");

          
            myDebug.printDateOnEachLine = true;
            byte[] inSimInit = myEncoder.ISI(adminPassword, 0, 0, nameApp, isLocal, receiveJoinReq, disableAIcars);
            try
            {
                this.Send(inSimInit, inSimInit.Length);
            }
            catch (Exception e)
            {
                throw e;
            }
            byte[] recvPacket;
            this.waitReceiveLow = true;
            recvPacket = this.ReceivePack();
            myDebug.WriteLine("mss", "Instance [" + host + " / " + port + "] successfully connected ");

            try
            {
                InSim.Decoder.VER ver = new InSim.Decoder.VER(recvPacket);
                this.connected = true;
                this.Product = ver.Product;
                this.Version = ver.Version;
                this.InSimVersion = ver.InSimVersion;
            }
            catch
            {
                myDebug.WriteLine("mss", "[" + host + " / " + port + "] Connection failed: Lapper Password does not match with the server admin password!!");
                Console.WriteLine("Lapper password does not match with the server admin password!! restart LFSLapper");
                Console.ReadLine();
            }
        }
        public void insimConnectUDP(string host, int port, string adminPassword, string mode, string nameApp, bool isLocal, int receiveJoinReq, int disableAIcars)
        {
            int nbTry = 0;
            int maxTry = 2;

            myDebug.Write("mss", "Connecting to:  [" + host + " / " + port + "]");
            

        retryConnect:
            try
            {
                uc = new System.Net.Sockets.UdpClient(host, port);
            }
            catch (Exception e)
            {
                myDebug.printDateOnEachLine = false;
                myDebug.WriteLine("mss", "");
                myDebug.printDateOnEachLine = true;
                throw e;
            }
#if MONO
            int localport = 0;  //for mono
#else
            int localport = ((System.Net.IPEndPoint)uc.Client.LocalEndPoint).Port;//for .NET
#endif
            byte[] inSimInit = myEncoder.ISI(adminPassword, localport, 0, nameApp, isLocal, receiveJoinReq, disableAIcars);
            try
            {
                this.Send(inSimInit, inSimInit.Length);
            }
            catch (Exception e)
            {
                myDebug.printDateOnEachLine = false;
                myDebug.WriteLine("mss", "");
                myDebug.printDateOnEachLine = true;
                throw e;
            }

            byte[] recvPacket;
            try
            {
                this.waitReceiveLow = true;
                recvPacket = this.ReceivePack();
            }
            catch
            {
                if (nbTry++ > maxTry)
                {
                    myDebug.printDateOnEachLine = false;
                    myDebug.WriteLine("mss", "");
                    myDebug.printDateOnEachLine = true;
                    throw (new Exception("Can't Receive reply of ISI Packet"));
                }
                else
                {
                    System.Threading.Thread.Sleep(200);
                    myDebug.printDateOnEachLine = false;
                    myDebug.Write("mss", ".");
                    myDebug.printDateOnEachLine = true;
                    goto retryConnect;
                }
            }
            myDebug.printDateOnEachLine = false;
            myDebug.WriteLine("mss", "");
            myDebug.WriteLine("mss", "Instance [" + host + " / " + port + "] successfully connected");
            myDebug.printDateOnEachLine = true;
            InSim.Decoder.VER ver = new InSim.Decoder.VER(recvPacket);
            this.connected = true;
            this.Product = ver.Product;
            this.Version = ver.Version;
            this.InSimVersion = ver.InSimVersion;
        }

        public void TInSimReceive() // Thread of reception
        {
            myDebug.WriteLine("mss", "InSImReceive Thread Started...");
            while (true)
            {
                byte[] recvPacket;
                this.waitReceiveLow = true;
                try
                {
                    recvPacket = this.ReceivePack();
                }
                catch
                {
                    System.Threading.Thread.Sleep(500);
                    continue;
                }
                Monitor.Enter(InsimPacks);
                InsimPacks.Enqueue(new objPacket(recvPacket));
                Monitor.Exit(InsimPacks);
            }
        }
        public objPacket Receive()
        {
            objPacket recvPacket;
            while (true)
            {
                Monitor.Enter(InsimPacks);
                if (InsimPacks.Count != 0)
                {
                    //                    Console.WriteLine(InsimPacks.Count);
                    recvPacket = (objPacket)InsimPacks.Dequeue();
                }
                else
                {

                    recvPacket = null;
                }
                Monitor.Exit(InsimPacks);
                if (waitReceive == false)
                    break;
            }
            return recvPacket;
        }

        public void Send(byte[] outMsg, int Length)
        {
            if (this.TCPmode)
            {
                this.SendTCP(outMsg, Length);
            }
            else
                this.SendUDP(outMsg, Length);
        }
        public void SendTCP(byte[] outMsg, int Length)
        {
            try
            {
                tc.SendToServer(outMsg, Length);
            }
            catch
            {
                throw new ConnectException("Insim TCP Connection Lost on " + Host + " / " + Port);
            }
        }
        public void SendUDP(byte[] outMsg, int Length)
        {
            try
            {
                uc.Send(outMsg, Length);
            }
            catch
            {
                throw new ConnectException("Insim UDP Connection Lost on " + Host + " / " + Port);
            }
        }
        public byte[] ReceivePack()
        {
            if (this.TCPmode)
                return ReceiveTCP();
            else
                return ReceiveUDP();
        }
        public byte[] ReceiveTCP()
        {
            if (tc.Available || waitReceiveLow)
                return tc.GetPackFromInsimServer();
            else
                return null;
        }
        public byte[] ReceiveUDP()
        {
            try
            {
                if (uc.Available != 0 || waitReceiveLow)
                {
                    return uc.Receive(ref remoteEP);
                }
                else
                    return null;
            }
            catch
            {
                throw new ConnectException("Insim UDP Connection Lost on " + Host + " / " + Port);
            }
        }
        public void Close()
        {
            if (this.TCPmode)
                CloseTCP();
            else
                CloseUDP();

            if (TInsimReceive != null)
            {
                TInsimReceive.Abort();
                TInsimReceive.Join();
            }

        }
        public void CloseTCP()
        {
            tc.Disconnect();
        }
        public void CloseUDP()
        {
            uc.Close();
        }
        public string packetHead(byte[] packet)
        {
            string packetHead;
            packetHead = Enum.GetName(typeof(TypePack), packet[1]);
            if (packetHead == null)
                packetHead = "ISP_" + packet[1].ToString();
            packetHead = packetHead.Remove(0, 4);
            return packetHead;
        }
        public uint verifyID(byte[] packet)
        {
            return (uint)0;
        }
    }
    public class Encoder
    {

        #region Send TINY insimpackets
        /// <summary>
        /// Request Insimpacket to LFS for information
        /// </summary>
        /// 
        public byte[] TINY_NONE()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_NONE);
        }
        public byte[] TINY_PING()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_PING);
        }
        public byte[] VER()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_VER);
        }
        public byte[] VTC()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 0, (byte)TypeTiny.TINY_VTC);
        }
        public byte[] NCN()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_NCN);
        }
        public byte[] ISM()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_ISM);
        }
        public byte[] NPL()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_NPL);
        }
        public byte[] AXI()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_AXI);
        }
        public byte[] AXM()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_AXM);
        }
        public byte[] GTH() // Request RaceTimer
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_GTH);
        }
        public byte[] ALC()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_ALC);
        }
        public byte[] NCI()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_NCI);
        }
        public byte[] RST()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_RST);
        }
        public byte[] REO(int ReqI)
        {
            return IS_TINY((byte)TypePack.ISP_TINY, (byte)ReqI, (byte)TypeTiny.TINY_REO);
        }
        public byte[] SST()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 10, (byte)TypeTiny.TINY_SST);
        }
        public byte[] ISC()
        {
            return IS_TINY((byte)TypePack.ISP_TINY, 1, (byte)TypeTiny.TINY_CLOSE);
        }
        public byte[] NLI(int interval)
        {
            return IS_SMALL((byte)TypePack.ISP_SMALL, 0, (byte)TypeSmall.SMALL_NLI, (uint)interval);
        }
        public byte[] ALC(int allowedcars)
        {
            return IS_SMALL((byte)TypePack.ISP_SMALL, 0, (byte)TypeSmall.SMALL_ALC, (uint)allowedcars);
        }
        public byte[] LCS(int Switches)
        {
            return IS_SMALL((byte)TypePack.ISP_SMALL, 0, (byte)TypeSmall.SMALL_LCS, (uint)Switches);
        }
        #endregion
        public byte[] IS_TINY(byte Type, byte ReqI, byte SubT)
        {
            byte[] packet = new byte[4];
            packet[0] = 4;
            packet[1] = Type;
            packet[2] = ReqI;
            packet[3] = SubT;
            return packet;
        }
        public byte[] IS_SMALL(byte Type, byte ReqI, byte SubT, uint Uval)
        {
            byte[] packet = new byte[8];
            packet[0] = 8;
            packet[1] = Type;
            packet[2] = ReqI;
            packet[3] = SubT;
            packet[4] = (byte)(Uval & 0xff);
            packet[5] = (byte)((Uval >> 8) & 0xff);
            packet[6] = (byte)((Uval >> 16) & 0xff);
            packet[7] = (byte)((Uval >> 24) & 0xff);

            return packet;
        }

        /// <summary>
        /// Sent TTC (target to connection) info to LFS
        /// </summary>
        public byte[] TTC(byte ReqI, byte UCID)
        {
            byte[] packet = new byte[8];
            packet[0] = 8;
            packet[1] = (byte)TypePack.ISP_TTC;
            packet[2] = ReqI;                  // ReqI - 0 unless it is an info request or a reply to an info request
            packet[3] = 1;                  // subtype, from TTC_ enumeration (e.g. TTC_SEL) >>     1 = TTC_SEL
            packet[4] = UCID;               // connection's unique id (0 = local)
            packet[5] = 0;                  // B1, B2, B3 may be used in various ways depending on SubT
            packet[6] = 0;                  //
            packet[7] = 0;                  //
            return packet; // send packet to lfs
        }

        /// <summary>
        /// Sent MST (command messages) to LFS
        /// </summary>
        public byte[] MST(string msg)
        {
            int msgLen = msg.Length > 63 ? 63 : msg.Length;
            byte[] buffer = new byte[63];
            //Encode string 
            int length = LfsEncoding.Current.GetBytes(msg, buffer, 0, 63);
            // Get the packet size

            byte[] packet = new byte[68];
            packet[0] = 68;
            packet[1] = (byte)TypePack.ISP_MST;
            packet[2] = 0;
            packet[3] = 0;

            for (int i = 0; i < 63; i++)
            {
                packet[i + 4] = buffer[i];
            }
            return packet;
        }
        
        public byte[] MST(byte[] msg)
        {
            byte[] packet = new byte[68];
            packet[0] = 68;
            packet[1] = (byte)TypePack.ISP_MST;
            packet[2] = 0;
            packet[3] = 0;
            System.Array.Copy(msg, 0, packet, 4, System.Math.Min(63, msg.Length));
            return packet;
        }

        /// <summary>
        /// Sent MSX (Player messages) to LFS
        /// </summary>
        public byte[] MSX(string msg)
        {
            //int msgLen = msg.Length > 96 ? 96 : msg.Length;
             byte[] buffer = new byte[96];
            //Encode string 
            int length = LfsEncoding.Current.GetBytes(msg, buffer, 0, 96);

            // Get the packet size
            byte[] packet = new byte[100];
            packet[0] = (byte)(100);
            packet[1] = (byte)TypePack.ISP_MSX;
            packet[2] = 0;
            packet[3] = 0;
            //InSim.CodePage.GetBytes(msg, 0, msgLen, packet, 4);
            //Encode string
            for (int i = 0; i < length; i++)
            {
                packet[i + 4] = buffer[i];
            }
            return packet;
        }
        
        public byte[] MSX(byte[] msg)
        {
            byte[] packet = new byte[100];
            packet[0] = (byte)100;
            packet[1] = (byte)TypePack.ISP_MSX;
            packet[2] = 0;
            packet[3] = 0;
            System.Array.Copy(msg, 0, packet, 4, System.Math.Min(96, msg.Length));
            return packet;
        }

        public byte[] MTC(int UCID, int PLID, string msg, int Sound)
        {

            byte[] buffer = new byte[128];
            //Encode string 
            int length = LfsEncoding.Current.GetBytes(msg, buffer, 0, 128);
            // Get the packet size (MTC needs trailing zero).
            int PacketSize = (byte)(Math.Min(length + (4 - (length % 4)), 128));
            if (PacketSize > 128)
            {
                PacketSize = 128;
            }
            //MTC LAPPER INSIM PACKET
            byte[] packet = new byte[8 + PacketSize];
            packet[0] = (byte)(8 + PacketSize);
            packet[1] = (byte)TypePack.ISP_MTC;
            packet[2] = 0;
            packet[3] = (byte)Sound;
            packet[4] = (byte)UCID;
            packet[5] = (byte)PLID;
            packet[6] = 0;
            packet[7] = 0;

            for (int i = 0; i < length; i++)
            {
                packet[i + 8] = buffer[i];
            }
            //Test
            //Console.WriteLine("ISP_MTC: TEXTLENGTH::" + length + "SIZE::" + (8 + PacketSize) + " bytes  UCID::" + UCID + " TEXT:: " + msg);
            return packet;
        }

        /// <summary>
        /// InSim Initialization Request Encoder
        /// </summary>
        /// <param name="adminPass">Administrator password.</param>
        /// <param name="portNum">Port number. 0 for same port as output.</param>
        /// <param name="mciSeconds">Seconds between MCI packets. 0 to disable.</param>
        /// <returns></returns>
        // OK in VERSION 4
        public byte[] ISI(string adminPass, int portNum, byte mciSeconds, string nameApp, bool local, int receiveJoinReq, int disableAIcars)
        {
            byte[] packet = new byte[44]; //byte[] Each Packet is 1 Byte!!
            packet[0] = 44;
            packet[1] = (byte)TypePack.ISP_ISI;
            packet[2] = 1;
            packet[3] = 0;
            // Port
            packet[4] = (byte)(portNum % 256);  // LSB
            packet[5] = (byte)(portNum / 256);  // MSB


            if (local)
            {
                if (receiveJoinReq == 1 || disableAIcars == 1) // if ReceiveJoinRequest or DisableAI is set to 1 in main LPR script - set REQ_JOIN flag
                {
                    packet[6] = (byte)ISF.MCI | (byte)ISF.LOCAL | (byte)ISF.OBH | (byte)ISF.CON | (UInt16)ISF.HLV & 255 | (UInt16)ISF.AXM_EDIT & 255 | (UInt16)ISF.AXM_LOAD & 255;    // LSB
                    packet[7] = (UInt16)ISF.HLV >> 8 | (UInt16)ISF.AXM_EDIT >> 8 | (UInt16)ISF.AXM_LOAD >> 8 | (UInt16)ISF.REQ_JOIN >> 8; // MSB

                }
                else
                {
                    packet[6] = (byte)ISF.MCI | (byte)ISF.LOCAL | (byte)ISF.OBH | (byte)ISF.CON | (UInt16)ISF.HLV & 255 | (UInt16)ISF.AXM_EDIT & 255 | (UInt16)ISF.AXM_LOAD & 255;    // LSB
                    packet[7] = (UInt16)ISF.HLV >> 8 | (UInt16)ISF.AXM_EDIT >> 8 | (UInt16)ISF.AXM_LOAD >> 8; // MSB 
                }
            }
            else
            {
                if (receiveJoinReq == 1 || disableAIcars == 1) // if ReceiveJoinRequest or DisableAI is set to 1 in main LPR script - set REQ_JOIN flag
                {
                    packet[6] = (byte)ISF.MCI | (byte)ISF.OBH | (byte)ISF.CON | (UInt16)ISF.HLV & 255 | (UInt16)ISF.AXM_EDIT & 255 | (UInt16)ISF.AXM_LOAD & 255 | (UInt16)ISF.REQ_JOIN & 255;    // LSB
                    packet[7] = (UInt16)ISF.HLV >> 8 | (UInt16)ISF.AXM_EDIT >> 8 | (UInt16)ISF.AXM_LOAD >> 8 | (UInt16)ISF.REQ_JOIN >> 8; // MSB

                }
                else
                {
                    packet[6] = (byte)ISF.MCI | (byte)ISF.OBH | (byte)ISF.CON | (UInt16)ISF.HLV & 255 | (UInt16)ISF.AXM_EDIT & 255 | (UInt16)ISF.AXM_LOAD & 255 | (UInt16)ISF.REQ_JOIN & 255;    // LSB
                    packet[7] = (UInt16)ISF.HLV >> 8 | (UInt16)ISF.AXM_EDIT >> 8 | (UInt16)ISF.AXM_LOAD >> 8; // MSB

                }
            }

            packet[8] = 8; //Insim Version
            packet[9] = (byte)'!';

            // Number of milli seconds between NLP or MCI packets (0=none) 
            packet[10] = 50;   // LSB
            packet[11] = 0;     // MSB

            System.Text.Encoding.ASCII.GetBytes(adminPass, 0, Math.Min(adminPass.Length, 16), packet, 12);
            System.Text.Encoding.ASCII.GetBytes(nameApp, 0, Math.Min(nameApp.Length, 16), packet, 28);

            return packet;
        }

        /// <summary>
        /// ISP_BFN ( Clicked Button)
        /// </summary>
        public byte[] BFN(byte ReqI, int SubT, int UCID, int ClickID)
        {
            byte[] packet = new byte[8];
            packet[0] = 8;
            packet[1] = (byte)TypePack.ISP_BFN;
            packet[2] = ReqI;
            packet[3] = (byte)SubT;
            packet[4] = (byte)UCID;
            packet[5] = (byte)ClickID;
            packet[6] = 0;
            packet[7] = 0;
            return packet;
        }


        /// <summary>
        /// Sent BTN (Button) info to LFS
        /// </summary>
        /// 
        public byte[] BTN(byte ReqI, int L, int T, int W, int H, int UCID, int ClickID, int style, int TypeIn, string Caption, string Text)
        {
            //Calculate packetsize
            int PacketSize = 0;
            byte[] buffer = new byte[240];

            int poke = Caption.Length;
            string allText;
            if (poke > 0)
                allText = " " + Caption + " " + Text;
            else
                allText = Text;

            while (allText.Length % 4 != 0)
                allText += " ";

            if (allText.Length > 240)
                allText = allText.Substring(0, 240);
            
            //Calculate packetsize (Text)
            int length = LfsEncoding.Current.GetBytes(allText, buffer, 0, 240);
            if (length > 0)
            {
                PacketSize = (byte)(Math.Min(length + (4 - (length % 4)), 240));
            }
            else
            {
                PacketSize = 0;
            }

            byte[] packet = new byte[12 + PacketSize];
            packet[0] = (byte)(12 + PacketSize);
            packet[1] = (byte)TypePack.ISP_BTN;
            packet[2] = ReqI;              // ReqI
            packet[3] = (byte)UCID;     // UCID
            packet[4] = (byte)ClickID;  // ClickID Of Button
            packet[5] = 0;              // Inst
            packet[6] = (byte)style;              // BUtton Style
            packet[7] = (byte)TypeIn;              // TypeIn
            packet[8] = (byte)L; // Left
            packet[9] = (byte)T; // Top
            packet[10] = (byte)W; // Largeur
            packet[11] = (byte)H; // Largeur

            for (int i = 0; i < length; i++)
            {
                packet[i + 12] = (byte)buffer[i];

                if (poke != 0)
                {
                    packet[12] = 0;
                    packet[12 + poke + 1] = 0;
                }
                int l = allText.Length;
                for (int a = 12 + allText.Length - 1; a > 12; a--)
                {
                    if (packet[a] == ' ')
                        packet[a] = 0;
                    else
                        break;
                }
            }
            //DEBUG
            //Console.WriteLine("ISP_BTN: TEXTLENGTH::"+ length + "SIZE::" + (12 + PacketSize) + " bytes  UCID::" + UCID + " TEXT:: "+ Text);
            return packet;


        }

        /// <summary>
        /// Sent REO (Grid reorder) request to LFS
        /// </summary>
        public byte[] REO(byte ReqI, int nbPlayer, byte[] arPLID)
        {
            byte[] packet = new byte[36];
            packet[0] = 36;
            packet[1] = (byte)TypePack.ISP_REO;
            packet[2] = ReqI;
            packet[3] = (byte)nbPlayer;
            for (int i = 0; i < 32; i++)
                packet[i + 4] = (byte)arPLID[i];
            return packet;
        }

        /// <summary>
        /// Sent OCO (StartLight Control) info to LFS
        /// </summary>
        public byte[] OCO(byte OCOAction, byte Index, byte Identifier, byte Data)
        {
            byte[] packet = new byte[8];
            packet[0] = 8;
            packet[1] = (byte)TypePack.ISP_OCO;
            packet[2] = 0;
            packet[3] = 0;
            packet[4] = OCOAction;
            packet[5] = Index;
            packet[6] = Identifier;
            packet[7] = Data;

            return packet;
        }
        
        /// <summary>
        /// Sent HCP Car Handicap info to LFS
        /// </summary>
        public byte[] HCP(byte Car,byte H_Mass, byte H_Tres)
        {
            byte[] packet = new byte[68];
            packet[0] = (byte)68;
            packet[1] = (byte)TypePack.ISP_HCP;
            packet[2] = 0;
            packet[3] = 0;
            //HCP car info
            packet[4 + (Car * 2)] = H_Mass;
            packet[5 + (Car * 2)] = H_Tres;
            return packet;
        }
        //4 + (1  1)

        /// <summary>
        /// Sent AXM (Objects) info to LFS
        /// </summary> 
        public byte[] AXM(byte NumO, byte UCID, byte PMOAction, byte PMOFlags, short[] X, short[] Y, byte[] Z, byte[] Flags, byte[] Index, byte[] Heading)
        {
            byte[] packet = new byte[(8 + (NumO * 8))]; // max is 248 bytes
            packet[0] = (byte)(8 + (NumO * 8));         // max is 248 bytes
            packet[1] = (byte)TypePack.ISP_AXM;         //Packet 54
            packet[2] = 0;                              //Request = 0
            packet[3] = NumO;                           //Number of objects
            packet[4] = UCID;                           //Player ID who sent the objects
            packet[5] = PMOAction;                      //Remove/Add object
            packet[6] = PMOFlags;                       //128
            packet[7] = 0;                              //Spare 3
            byte a = 0;
            for (byte i = 0; i < NumO; i++)
            {
                //Convert Short X into 2 bytes
                packet[8 + (i * 8)] = (byte)(X[a] & 255);
                packet[9 + (i * 8)] = (byte)(X[a] >> 8); //Shift by 8 bits
                //Convert Short Y into 2 bytes
                packet[10 + (i * 8)] = (byte)(Y[a] & 255);
                packet[11 + (i * 8)] = (byte)(Y[a] >> 8); //Shift by 8 bits

                packet[12 + (i * 8)] = Z[a];
                packet[13 + (i * 8)] = Flags[a];
                packet[14 + (i * 8)] = Index[a];
                packet[15 + (i * 8)] = Heading[a];
                a++;
            }
            return packet;
        }

        /// <summary>
        /// Sent PLC (Objects) Playerallowed cars info to LFS
        /// </summary>  
        public byte[] PLC(byte UCID, int Cars)
        {
            byte[] packet = new byte[12];
            packet[0] = 12;
            packet[1] = (byte)TypePack.ISP_PLC;
            packet[2] = 0;
            packet[3] = 0;
            packet[4] = UCID; //Connection's unique id (0 = host)(255 = all)
            packet[5] = 0; //Spare 1
            packet[6] = 0; //Spare 2
            packet[7] = 0; //Spare 3
            //CARS
            packet[8] = (byte)((Cars) & 255);
            packet[9] = (byte)((Cars) >> 8); // Shift by 8 Bits
            packet[10] = (byte)((Cars) >> 16); // Shift by 16 Bits 2*8
            packet[11] = (byte)((Cars) >> 24); // Shift by 24 Bits 3*8
            return packet;
        }


        /// <summary>
        /// Sent JRR (join Request) info to LFS
        /// Join Request Reply - send one of these back to LFS in response to a join request
        /// </summary>
        public byte[] JRR(short X, short Y, sbyte Zbyte, byte Flags, byte Heading, byte UCID, byte PLID, byte JRRAction)
        {

            byte[] packet = new byte[16];
            packet[0] = 16;
            packet[1] = (byte)TypePack.ISP_JRR;
            packet[2] = 0;
            packet[3] = PLID;               //Player's unique id (if zero, use UCID) // ZERO when this is a reply to a join request - SET to move a car
            packet[4] = UCID;               //Connection's unique id (0 = host) // set when this is a reply to a join request - ignored when moving a car
            packet[5] = JRRAction;          //JRR Action
            packet[6] = 0;                  //Spare
            packet[7] = 0;                  //Spare

            //Convert Short X into 2 bytes
            packet[8] = (byte)(X & 255);
            packet[9] = (byte)(X >> 8); //Shift by 8 bits
            //Convert Short Y into 2 bytes
            packet[10] = (byte)(Y & 255);
            packet[11] = (byte)(Y >> 8); //Shift by 8 bits

            packet[12] = (byte)Zbyte;             //Z Pos
            packet[13] = Flags;             //Flags
            packet[14] = 0;                 //Index
            packet[15] = Heading;           //Heading

            return packet; // send packet to lfs
        }
    }
    /// <summary>
    /// Decoder Convert datatypes
    /// Version packet decoder
    /// </summary>
    public class Decoder
    {
        static int pakGetByte(byte[] pak, int first)
        {
            return (int)pak[first];
        }
        static string pakGetString(byte[] pak, int first, int len)
        {
            // return InSim.CodePage.GetString(pak, first, len);
            return LfsEncoding.Current.GetString(pak, first, len);
        }
        static int pakGetWord(byte[] pak, int first)
        {
            return (int)System.BitConverter.ToUInt16(pak, first);
        }
        static int pakGetShort(byte[] pak, int first)
        {
            return (int)System.BitConverter.ToInt16(pak, first);
        }
        static long pakGetUnsigned(byte[] pak, int first)
        {
            return (long)System.BitConverter.ToUInt32(pak, first);
        }
        static int pakGetInt(byte[] pak, int first)
        {
            return (int)System.BitConverter.ToInt32(pak, first);
        }
        static float pakGetFloat(byte[] pak, int first)
        {
            return (float)System.BitConverter.ToSingle(pak, first);
        }
        static bool pakGetBoolean(byte[] pak, int first)
        {
            return (Boolean)System.BitConverter.ToBoolean(pak, first);
        }


        public class TINY
        {
            public readonly int ReqI;
            public readonly string SubT;

            public TINY(byte[] packet)
            {
                ReqI = pakGetByte(packet, 2);
                SubT = Enum.GetName(typeof(TypeTiny), packet[3]);
            }
        }
        public class SMALL
        {
            public readonly int ReqI;
            public readonly string SubT;
            public long uval;

            public SMALL(byte[] packet)
            {
                ReqI = pakGetByte(packet, 2);
                SubT = Enum.GetName(typeof(TypeSmall), packet[3]);
                uval = pakGetUnsigned(packet, 4);
            }
        }
        public class VER
        {
            public readonly string Version;
            public readonly string Product;
            public readonly int InSimVersion;

            public VER(byte[] packet)
            {
                Version = pakGetString(packet, 4, 8);
                Product = pakGetString(packet, 12, 6);
                InSimVersion = pakGetWord(packet, 18);
            }
        }
        public class ISM
        {
            public readonly int Host;
            public readonly string HName;

            public ISM(byte[] packet)
            {
                Host = pakGetByte(packet, 4);
                HName = pakGetString(packet, 8, 32);
            }
        }
        // Ok for Insim 4
        public class DefCompCar
        {
            public int node;
            public int lap;
            public int PLID;
            public int Position;
            public int Infos;
            public int x;
            public int y;
            public int z;
            public int speed;
            public int direction;
            public int heading;
            public int angvel;

        }
        public class MCI
        {
            public int numOfPlayers;
            public DefCompCar[] compCar = new DefCompCar[8];

            public MCI(byte[] packet)
            {
                numOfPlayers = pakGetByte(packet, 3);
                int offsetStartPlayer = 4;
                int lengthPlayer = 28;

                for (int i = 0; i < System.Math.Min(8, numOfPlayers); i++)
                {
                    compCar[i] = new DefCompCar();

                    compCar[i].node = pakGetWord(packet, offsetStartPlayer + i * lengthPlayer + 0);
                    compCar[i].lap = pakGetWord(packet, offsetStartPlayer + i * lengthPlayer + 2);
                    compCar[i].PLID = pakGetByte(packet, offsetStartPlayer + i * lengthPlayer + 4);
                    compCar[i].Position = pakGetByte(packet, offsetStartPlayer + i * lengthPlayer + 5);
                    compCar[i].Infos = pakGetByte(packet, offsetStartPlayer + i * lengthPlayer + 6);
                    compCar[i].x = pakGetInt(packet, offsetStartPlayer + i * lengthPlayer + 8);
                    compCar[i].y = pakGetInt(packet, offsetStartPlayer + i * lengthPlayer + 12);
                    compCar[i].z = pakGetInt(packet, offsetStartPlayer + i * lengthPlayer + 16);
                    compCar[i].speed = pakGetWord(packet, offsetStartPlayer + i * lengthPlayer + 20);
                    compCar[i].direction = pakGetWord(packet, offsetStartPlayer + i * lengthPlayer + 22);
                    compCar[i].heading = pakGetWord(packet, offsetStartPlayer + i * lengthPlayer + 24);
                    compCar[i].angvel = pakGetShort(packet, offsetStartPlayer + i * lengthPlayer + 26);
                }
            }
        }
        // Ok for Insim 4
        public class NLP
        {
            public NLP(byte[] packet)
            {
            }
        }

        // OK For Insim 7
        //Admin Command Report ( added @ 16-10-2017)
        public class ACR // Size 12, 16, 20... 72 depending on Text
        {
            public readonly int PacketSize;     // 12, 16, 20... 72 depending on Text
            public readonly int ReqI;           // 0
            public readonly int UCID;           // Unique Connection ID
            public readonly int Admin;          // set if user is an admin
            public readonly int Result;         // 1 - processed / 2 - rejected / 3 - unknown command
            public readonly int Sp3;            // Spare 3
            public readonly string Text;        // 4, 8, 12... 64 characters - last byte is zero

            public ACR(byte[] packet)
            {
                PacketSize = pakGetByte(packet, 1);             //1 Byte
                ReqI = pakGetByte(packet, 2);                   //1 Byte
                UCID = pakGetByte(packet, 4);                   //1 Byte
                Admin = pakGetByte(packet, 5);                  //1 Byte
                Result = pakGetByte(packet, 6);                 //1 Byte
                Sp3 = pakGetByte(packet, 7);                    //1 Byte
                Text = pakGetString(packet, 8, 64);             // 4, 8, 12... 64 characters - last byte is zero
            }
        }

        public class SLC // Player Select a car in garage
        {
            public readonly int PacketSize;             // 8 Bytes
            public readonly int ReqI;                   // 0
            public readonly int UCID;                   // Unique Connection ID
            public readonly string Car;                 // Selected Car
            public SLC(byte[] packet)
            {
                PacketSize = pakGetByte(packet, 1);             //1 Byte
                ReqI = pakGetByte(packet, 2);                   //1 Byte
                UCID = pakGetByte(packet, 4);                   //1 Byte
                Car = pakGetString(packet, 5, 4);               //4 Byte
            }
        }

        public class NCI
        {
            public readonly int ReqI;           // 0 unless this is a reply to a TINY_NCI request
            public readonly int UCID;           // Spare
            public readonly int Language;       // Player's Language
            public readonly int Sp1;            // Spare 1
            public readonly int Sp2;            // Spare 2
            public readonly int Sp3;            // Spare 3
            public readonly long UserID;         // LFS UserID
            public readonly long IP;       // Player's IPAdress
            public string IPAddress;       // Converted IP Adress

            public NCI(byte[] packet) //Packetsize 16 Bytes
            {
                ReqI = pakGetByte(packet, 2);               //1 Byte
                UCID = pakGetByte(packet, 3);               //1 Byte
                Language = pakGetByte(packet, 4);            //1 Byte
                Sp1 = pakGetByte(packet, 4);                //1 Byte
                Sp2 = pakGetByte(packet, 5);                //1 Byte
                Sp3 = pakGetByte(packet, 6);                //1 Byte

                UserID = pakGetUnsigned(packet, 7);         //4 Bytes
                IP = pakGetUnsigned(packet, 12);            //4 Bytes

                //Convert Decimal number into dotted decimal IPadress
                byte[] IPPacket = new byte[4];
                IPPacket[0] = (byte)((IP) & 255);
                IPPacket[1] = (byte)((IP) >> 8);
                IPPacket[2] = (byte)((IP) >> 16);
                IPPacket[3] = (byte)((IP) >> 24);
                IPAddress = "" + IPPacket[0] + "." + IPPacket[1] + "." + IPPacket[2] + "." + IPPacket[3];
            }
        }

        // OK For Insim 7
        public class CarContact         // 8 bytes : car in a contact with an object
        {
            public int PLID;
            public int Info;            // like Info byte in CompCar (CCI_BLUE / CCI_YELLOW / CCI_LAG)
            public int Sp2;             // spare
            public int Steer;           // front wheel steer in degrees (right positive)

            public int ThrBrk;          // high 4 bits : throttle    / low 4 bits : brake (0 to 15)
            public int CluHan;          // high 4 bits : clutch      / low 4 bits : handbrake (0 to 15)
            public int GearSp;          // high 4 bits : gear (15=R) / low 4 bits : spare
            public int Speed;           // m/s

            public int Direction;       // car's motion if Speed > 0 : 0 = world y direction, 128 = 180 deg
            public int Heading;         // direction of forward axis : 0 = world y direction, 128 = 180 deg
            public int AccelF;          // m/s^2 longitudinal acceleration (forward positive)
            public int AccelR;          // m/s^2 lateral acceleration (right positive)

            public int X;               // position (1 metre = 16)
            public int Y;			    // position (1 metre = 16)
        }
        // OK For Insim 7
        public class CON
        {
            public readonly int Zero;
            public readonly int SpClose;                    // high 4 bits : reserved / low 12 bits : closing speed (10 = 1 m/s)
            public readonly int Time;                       // looping time stamp (hundredths - time since reset - like TINY_GTH)
            public CarContact PlayerA = new CarContact();
            public CarContact PlayerB = new CarContact();
            public CON(byte[] packet)                       // Size 40 bytes
            {
                Zero = pakGetByte(packet, 3);              //1 Byte
                SpClose = pakGetWord(packet, 4);           //2 Bytes 
                Time = pakGetWord(packet, 6);              //2 Bytes
                                                           //Player A
                PlayerA.PLID = pakGetByte(packet, 8);       //1 Byte        // player's unique id
                PlayerA.Info = pakGetByte(packet, 9);       //1 Byte        // like Info byte in CompCar (CCI_BLUE / CCI_YELLOW / CCI_LAG)
                PlayerA.Sp2 = pakGetByte(packet, 10);       //1 Byte        // spare
                PlayerA.Steer = pakGetByte(packet, 11);     //1 Byte        // front wheel steer in degrees (right positive)
                PlayerA.ThrBrk = pakGetByte(packet, 12);    //1 Byte        // high 4 bits : throttle    / low 4 bits : brake (0 to 15)
                PlayerA.CluHan = pakGetByte(packet, 13);    //1 Byte        // high 4 bits : clutch      / low 4 bits : handbrake (0 to 15)
                PlayerA.GearSp = pakGetByte(packet, 14);    //1 Byte        // high 4 bits : gear (15=R) / low 4 bits : spare
                PlayerA.Speed = pakGetByte(packet, 15);     //1 Byte        // m/s
                PlayerA.Direction = pakGetByte(packet, 16); //1 Byte        // car's motion if Speed > 0 : 0 = world y direction, 128 = 180 deg
                PlayerA.Heading = pakGetByte(packet, 17);   //1 Byte        // direction of forward axis : 0 = world y direction, 128 = 180 deg
                PlayerA.AccelF = pakGetByte(packet, 18);    //1 Byte        // m/s^2 longitudinal acceleration (forward positive)
                PlayerA.AccelR = pakGetByte(packet, 19);    //1 Byte        // m/s^2 lateral acceleration (right positive)
                PlayerA.X = pakGetShort(packet, 20);         //2 Byte        // position (1 metre = 16)
                PlayerA.Y = pakGetShort(packet, 22);        //2 Byte		// position (1 metre = 16)
                //Player B
                PlayerB.PLID = pakGetByte(packet, 24);      //1 Byte
                PlayerB.Info = pakGetByte(packet, 25);      //1 Byte        // like Info byte in CompCar (CCI_BLUE / CCI_YELLOW / CCI_LAG)
                PlayerB.Sp2 = pakGetByte(packet, 26);       //1 Byte        // spare
                PlayerB.Steer = pakGetByte(packet, 27);     //1 Byte        // front wheel steer in degrees (right positive)
                PlayerB.ThrBrk = pakGetByte(packet, 28);    //1 Byte        // high 4 bits : throttle    / low 4 bits : brake (0 to 15)
                PlayerB.CluHan = pakGetByte(packet, 29);    //1 Byte        // high 4 bits : clutch      / low 4 bits : handbrake (0 to 15)
                PlayerB.GearSp = pakGetByte(packet, 30);    //1 Byte        // high 4 bits : gear (15=R) / low 4 bits : spare
                PlayerB.Speed = pakGetByte(packet, 31);     //1 Byte        // m/s
                PlayerB.Direction = pakGetByte(packet, 32); //1 Byte        // car's motion if Speed > 0 : 0 = world y direction, 128 = 180 deg
                PlayerB.Heading = pakGetByte(packet, 33);   //1 Byte        // direction of forward axis : 0 = world y direction, 128 = 180 deg
                PlayerB.AccelF = pakGetByte(packet, 34);    //1 Byte        // m/s^2 longitudinal acceleration (forward positive)
                PlayerB.AccelR = pakGetByte(packet, 35);    //1 Byte        // m/s^2 lateral acceleration (right positive)
                PlayerB.X = pakGetShort(packet, 36);        //2 Bytes       // position (1 metre = 16)
                PlayerB.Y = pakGetShort(packet, 38);        //2 Bytes		// position (1 metre = 16)
            }
        }

        // OK For Insim 7
        public class ObjectInfo // Info about a single object - explained in the layout file format
        {
            public int X;
            public int Y;

            public int Z;
            public int Flags;
            public int Index;
            public int Heading;
        }
        // OK For Insim 7
        public class CarContOBJ // 8 bytes : car in a contact with an object
        {
            public int Direction;               // car's motion if Speed > 0 : 0 = world y direction, 128 = 180 deg
            public int Heading;                 // direction of forward axis : 0 = world y direction, 128 = 180 deg
            public int Speed;                   // m/s
            public int PlayerPosZ;              // position (1 metre = 4)
            public int PlayerPosX;              // position (1 metre = 16)
            public int PlayerPosY;              // position (1 metre = 16)
        }
        // OK For Insim 7
        public class OBH // 24 Byte size Object Collision between Car > Object
        {
            public CarContOBJ CarCont = new CarContOBJ();

            public readonly int PLID;               // player's unique id
            public readonly int SpClose;            // closing speed (10 = 1 m/s)
            public readonly int ObjectHitTime;      // looping time stamp
            public readonly int ObjectPosX;         // as in ObjectInfo
            public readonly int ObjectPosY;         // as in ObjectInfo
            public readonly int ObjectPosZ;         // as in ObjectInfo
            public readonly int Sp1;                //spare1
            public readonly int Index;              // AXO_x as in ObjectInfo or zero if it is an unknown object
            public readonly int OBHFlags;

            public OBH(byte[] packet)
            {

                PLID = pakGetByte(packet, 3);                   //1 Byte
                SpClose = pakGetWord(packet, 4);                //2 Bytes 
                ObjectHitTime = pakGetWord(packet, 6);          //2 Bytes

                CarCont.Direction = pakGetByte(packet, 8);      //1 Byte
                CarCont.Heading = pakGetByte(packet, 9);        //1 Byte
                CarCont.Speed = pakGetByte(packet, 10);         //1 Byte
                CarCont.PlayerPosZ = pakGetByte(packet, 11);    //1 Byte
                CarCont.PlayerPosX = pakGetShort(packet, 12);   //2 Bytes
                CarCont.PlayerPosY = pakGetShort(packet, 14);   //2 Bytes

                ObjectPosX = pakGetShort(packet, 16);           //2 Bytes
                ObjectPosY = pakGetShort(packet, 18);           //2 Bytes
                ObjectPosZ = pakGetByte(packet, 20);            //1 Byte
                Sp1 = pakGetByte(packet, 21);                   //1 Byte
                Index = pakGetByte(packet, 22);                 //1 Byte
                OBHFlags = pakGetByte(packet, 23);              //1 Byte
            }
        }

        // OK For Insim 7
        public class HLV // Size 16 Bytes: Hot Lap Validity - off track / hit wall / speeding in pits / out of bounds
        {
            public CarContOBJ CarCont = new CarContOBJ();

            public readonly int PLID;               // player's unique id
            public readonly int HVLC;               // 0 : ground / 1 : wall / 4 : speeding in pitlane / 5 : out of bounds
            public readonly int Spare01;            // Spare
            public readonly int Time;               //looping time stamp(hundredths - time since reset - like TINY_GTH)

            public HLV(byte[] packet)
            {
                PLID = pakGetByte(packet, 3);                   //1 Byte
                HVLC = pakGetByte(packet, 4);                   //1 Byte
                Spare01 = pakGetWord(packet, 5);                //2 Bytes 
                Time = pakGetWord(packet, 6);                   //2 Bytes

                CarCont.Direction = pakGetByte(packet, 8);      //1 Byte
                CarCont.Heading = pakGetByte(packet, 9);        //1 Byte
                CarCont.Speed = pakGetByte(packet, 10);         //1 Byte
                CarCont.PlayerPosZ = pakGetByte(packet, 11);    //1 Byte
                CarCont.PlayerPosX = pakGetShort(packet, 12);   //2 Bytes
                CarCont.PlayerPosY = pakGetShort(packet, 14);   //2 Bytes
            }
        }


        // OK For Insim 7
        public class UCO // Size 28 Bytes: report InSim checkpoint / InSim circle
        {
            public CarContOBJ C = new CarContOBJ();
            public ObjectInfo OBJInfo = new ObjectInfo();

            public readonly int PLID;                           // player's unique id
            public readonly int Sp0;                            //Spare 0
            public readonly int UCOAction;
            public readonly int Sp2;                            //Spare 2
            public readonly int Sp3;                            //Spare 3
            public readonly int Time;                           //hundredths of a second since start (as in SMALL_RTP)

            public UCO(byte[] packet)
            {
                PLID = pakGetByte(packet, 3);                   //1 Byte
                Sp0 = pakGetByte(packet, 4);                    //1 Byte
                UCOAction = pakGetByte(packet, 5);              //1 Byte
                Sp2 = pakGetByte(packet, 6);                    //1 Byte
                Sp3 = pakGetByte(packet, 7);                    //1 Byte
                Time = pakGetInt(packet, 8);                    //4 Byte

                C.Direction = pakGetByte(packet, 12);           //1 Byte
                C.Heading = pakGetByte(packet, 13);             //1 Byte
                C.Speed = pakGetByte(packet, 14);               //1 Byte
                C.PlayerPosZ = pakGetByte(packet, 15);          //1 Byte
                C.PlayerPosX = pakGetShort(packet, 16);         //2 Bytes
                C.PlayerPosY = pakGetShort(packet, 18);         //2 Bytes

                OBJInfo.X = pakGetShort(packet, 20);            //2 Bytes
                OBJInfo.Y = pakGetShort(packet, 22);            //2 Bytes
                OBJInfo.Z = pakGetByte(packet, 24);             //1 Byte
                OBJInfo.Flags = pakGetByte(packet, 25);         //1 Byte
                OBJInfo.Index = pakGetByte(packet, 26);         //1 Byte
                OBJInfo.Heading = pakGetByte(packet, 27);       //1 Byte
            }
        }

        // OK For Insim 7
        public class AXM // Size 16 Bytes: Info about objects
        {
            public ObjectInfo[] OBJInfo = new ObjectInfo[30];
            public readonly int PacketSize;
            public readonly int ReqI;
            public readonly int NumO;           //Number of objects
            public readonly int UCID;           //Spare 0
            public readonly int PMOAction;      //Add/Remove object
            public readonly int PMOFlags;       //128
            public readonly int Sp3;            //Spare 3

            public AXM(byte[] packet)
            {
                PacketSize = pakGetByte(packet, 1);
                ReqI = pakGetByte(packet, 2);                   //1 Byte
                NumO = pakGetByte(packet, 3);                   //1 Byte
                UCID = pakGetByte(packet, 4);                   //1 Byte
                PMOAction = pakGetByte(packet, 5);              //1 Byte
                PMOFlags = pakGetByte(packet, 6);               //1 Byte
                Sp3 = pakGetByte(packet, 7);                    //1 Byte

                for (byte i = 0; i < NumO; i++)
                {
                    OBJInfo[i] = new ObjectInfo();

                    OBJInfo[i].X = pakGetShort(packet, 8 + (i * 8));             //2 Bytes
                    OBJInfo[i].Y = pakGetShort(packet, 10 + (i * 8));            //2 Bytes
                    OBJInfo[i].Z = pakGetByte(packet, 12 + (i * 8));             //1 Byte
                    OBJInfo[i].Flags = pakGetByte(packet, 13 + (i * 8));         //1 Byte
                    OBJInfo[i].Index = pakGetByte(packet, 14 + (i * 8));         //1 Byte
                    OBJInfo[i].Heading = pakGetByte(packet, 15 + (i * 8));       //1 Byte
                }
            }
        }

        public class CSC // Cars State Changed
        {
            public CarContOBJ CarCont = new CarContOBJ();
            public int PLID;                    //player's unique id
            public int Sp0;                     //Spare 0
            public int CSCAction;               //Change in a car's state 
            public int Sp2;                     //Spare 2
            public int Sp3;                     //Spare 3
            public int Time;                    //Hundredths of a second since start (as in SMALL_RTP)
            public CSC(byte[] packet)
            {
                PLID = pakGetByte(packet, 3);                   //1 Byte
                Sp0 = pakGetByte(packet, 4);                    //1 Byte 
                CSCAction = pakGetByte(packet, 5);              //1 Byte
                Sp2 = pakGetByte(packet, 6);                    //1 Byte
                Sp3 = pakGetByte(packet, 7);                    //1 Byte
                Time = pakGetInt(packet, 8);                    //4 Byte

                CarCont.Direction = pakGetByte(packet, 12);      //1 Byte
                CarCont.Heading = pakGetByte(packet, 13);        //1 Byte
                CarCont.Speed = pakGetByte(packet, 14);         //1 Byte
                CarCont.PlayerPosZ = pakGetByte(packet, 15);    //1 Byte
                CarCont.PlayerPosX = pakGetShort(packet, 16);   //2 Bytes
                CarCont.PlayerPosY = pakGetShort(packet, 18);   //2 Bytes
            }
        }

        public class AXI  //40 bytes// AutoX Info
        {
            public readonly int ReqI;
            public readonly int Zero;       //Spare
            public readonly int AXStart;    // autocross start position
            public readonly int NumCP;      // number of checkpoints
            public readonly int NumO;       // number of objects
            public string LayoutName;       // the name of the layout last loaded (if loaded locally)

            public AXI(byte[] packet)
            {
                ReqI = pakGetByte(packet, 2);               //1 Byte
                Zero = pakGetByte(packet, 3);               //1 Byte
                AXStart = pakGetByte(packet, 4);            //1 Byte
                NumCP = pakGetByte(packet, 5);              //1 Bytes
                NumO = pakGetWord(packet, 6);               //2 Bytes
                LayoutName = pakGetString(packet, 8, 32);   //4 Bytes  
            }
        }


        /// <summary>
        /// State pack decoder
        /// LFS will send a StatePack any time the info in the StatePack changes.
        /// </summary>
        // OK For Insim 4
        public class STA
        {
            /// <summary>
            /// Short Track Name.
            /// </summary>
            public readonly float ReplaySpeed = 0;
            public readonly int Flags = 0;
            public readonly int InGameCam = 0;
            public readonly int ViewPlayer = 0;
            public readonly int NumP = 0;
            public readonly int NumConns = 0;
            public readonly int NumFinished = 0;
            public readonly int RaceInProg = 0;
            public readonly int QualMins = 0;
            public readonly int RaceLaps = 0;
            public string ShortTrackName = "NOT SET";
            public readonly int Weather = 0;
            public readonly int Wind = 0;

            public STA()
            {
            }
            public STA(byte[] packet)
            {
                ReplaySpeed = pakGetFloat(packet, 4);
                Flags = pakGetWord(packet, 8);
                InGameCam = pakGetByte(packet, 10);
                ViewPlayer = pakGetByte(packet, 11);
                NumP = pakGetByte(packet, 12);
                NumConns = pakGetByte(packet, 13);
                NumFinished = pakGetByte(packet, 14);
                RaceInProg = pakGetByte(packet, 15);
                QualMins = pakGetByte(packet, 16);
                RaceLaps = pakGetByte(packet, 17);
                ShortTrackName = pakGetString(packet, 20, 6);
                Weather = pakGetByte(packet, 26);
                Wind = pakGetByte(packet, 27);
            }
        }
        // Ok for InSim 4
        public class REO
        {
            /// <summary>
            /// Short Track Name.
            /// </summary>
            public readonly int ReqI;
            public readonly int NumP;
            public byte[] PLID = new byte[32];
            public REO(byte[] packet)
            {
                ReqI = pakGetByte(packet, 2);
                NumP = pakGetByte(packet, 3);
                Array.ConstrainedCopy(packet, 4, PLID, 0, 32);
            }

        }
        public class RES
        {
            /// <summary>
            /// Short Track Name.
            /// </summary>
            public readonly int PLID;
            public readonly string userName;
            public readonly string nickName;
            public readonly string Plate;
            public readonly string CName;
            public readonly long TTime;
            public readonly long BTime;
            public readonly int NumStops;
            public readonly int Confirm;
            public readonly int LapDone;
            public readonly int Flags;
            public readonly int ResultNum;
            public readonly int NumRes;


            public RES(byte[] packet)
            {

                PLID = pakGetByte(packet, 3);
                userName = pakGetString(packet, 4, 24);
                nickName = pakGetString(packet, 28, 24);
                Plate = pakGetString(packet, 52, 8);
                CName = pakGetString(packet, 60, 4);
                TTime = pakGetUnsigned(packet, 64);
                BTime = pakGetUnsigned(packet, 68);
                NumStops = pakGetByte(packet, 73);
                Confirm = pakGetByte(packet, 74);
                LapDone = pakGetWord(packet, 76);
                Flags = pakGetWord(packet, 78);
                ResultNum = pakGetByte(packet, 80);
                NumRes = pakGetByte(packet, 81);
            }
        }
        public class FIN
        {
            /// <summary>
            /// Short Track Name.
            /// </summary>
            public readonly int PLID;
            public readonly long TTime;
            public readonly long BTime;
            public readonly int NumStops;
            public readonly int Confirm;
            public readonly int LapDone;
            public readonly int Flags;


            public FIN(byte[] packet)
            {

                PLID = pakGetByte(packet, 3);
                TTime = pakGetUnsigned(packet, 4);
                BTime = pakGetUnsigned(packet, 8);
                NumStops = pakGetByte(packet, 13);
                Confirm = pakGetByte(packet, 14);
                LapDone = pakGetWord(packet, 16);
                Flags = pakGetWord(packet, 18);
            }
        }
        public enum fact
        {
            PITLANE_EXIT,		// 0 - left pit lane
            PITLANE_ENTER,		// 1 - entered pit lane
            PITLANE_NO_PURPOSE,	// 2 - entered for no purpose
            PITLANE_DT,			// 3 - entered for drive-through
            PITLANE_SG,			// 4 - entered for stop-go
            PITLANE_NUM,
        }
        public class PLA
        {
            public readonly int NumP;
            public int PLID;
            public fact fact;
            public PLA(byte[] packet)
            {
                PLID = pakGetByte(packet, 3);
                fact = (fact)pakGetByte(packet, 4);
            }

        }

        // MSO OK FOR insim 4
        public class MSO
        {
            public readonly int UCID;
            public readonly int PLID;
            public readonly int UserType;
            public readonly int TextStart;
            public readonly string message;
            public readonly string completeMessage;

            public MSO(byte[] packet)
            {
                UCID = pakGetByte(packet, 4);
                PLID = pakGetByte(packet, 5);
                UserType = pakGetByte(packet, 6);
                TextStart = pakGetByte(packet, 7);
                message = pakGetString(packet, 8 + TextStart, 128 - TextStart);
                completeMessage = pakGetString(packet, 8, 128);
            }
        }

        // Ok For Insim 4
        public class NPL
        {
            public readonly int PLID;
            public readonly int UCID;
            public readonly int PType;
            public readonly int Flags;
            public readonly string nickName;
            public readonly string Plate;
            public readonly string CName;
            public readonly string SName;
            public readonly tyre TyreRearLeft;
            public readonly tyre TyreRearRight;
            public readonly tyre TyreFrontLeft;
            public readonly tyre TyreFrontRight;
            public readonly int H_Mass;
            public readonly int H_TRes;
            public readonly int Pass;
            public readonly int SetF;
            public readonly int NumP;

            //			public readonly PlayerFlags flags;

            public enum PlayerFlags : ushort
            {
                SwapSide = 1,               // If Swapside -> pilote à gauche
                Reserved_2 = 2,
                Reserved_4 = 4,
                AutoGears = 8,
                Shifter = 16,
                Reserved_32 = 32,
                HelpBrake = 64,
                AxisClutch = 128,
                InPits = 256,
                AutoClutch = 512,
                Mouse = 1024,
                KbNoHelp = 2048,
                KbStabilised = 4096,
                CustomView = 8192
            }

            public NPL(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
                UCID = pakGetByte(pak, 4);
                PType = pakGetByte(pak, 5);
                Flags = pakGetWord(pak, 6);
                nickName = pakGetString(pak, 8, 24);
                Plate = pakGetString(pak, 32, 8);
                CName = pakGetString(pak, 40, 4);
                SName = pakGetString(pak, 44, 16);
                TyreRearLeft = (tyre)pakGetByte(pak, 60);
                TyreRearRight = (tyre)pakGetByte(pak, 61);
                TyreFrontLeft = (tyre)pakGetByte(pak, 62);
                TyreFrontRight = (tyre)pakGetByte(pak, 63);
                H_Mass = pakGetByte(pak, 64);
                H_TRes = pakGetByte(pak, 65);
                Pass = pakGetByte(pak, 67);
                SetF = pakGetByte(pak, 72);
                NumP = pakGetByte(pak, 73);
                //                flags = (PlayerFlags)(ushort)((pak[6]) | (pak[7] << 8));

            }
        }
        // Ok For insim 4
        public class PIT
        {
            public readonly int PLID;
            public readonly int LapsDone;
            public readonly int Flags;
            public readonly int Penalty;
            public readonly int NumStop;
            public readonly tyre TyreRearLeft;
            public readonly tyre TyreRearRight;
            public readonly tyre TyreFrontLeft;
            public readonly tyre TyreFrontRight;
            public readonly long Work;
            public readonly string sWork;

            public PIT(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
                LapsDone = pakGetWord(pak, 4);
                Flags = pakGetWord(pak, 6);
                Penalty = pakGetByte(pak, 9);
                NumStop = pakGetByte(pak, 10);
                TyreRearLeft = (tyre)pakGetByte(pak, 12);
                TyreRearRight = (tyre)pakGetByte(pak, 13);
                TyreFrontLeft = (tyre)pakGetByte(pak, 14);
                TyreFrontRight = (tyre)pakGetByte(pak, 15);
                Work = pakGetUnsigned(pak, 16);
                sWork = "";
                string virg = "";

                if (
                    (Work & (long)PIT_work.PSE_FR_DAM) != 0
                    || (Work & (long)PIT_work.PSE_RE_DAM) != 0
                    || (Work & (long)PIT_work.PSE_LE_FR_DAM) != 0
                    || (Work & (long)PIT_work.PSE_RI_FR_DAM) != 0
                    || (Work & (long)PIT_work.PSE_LE_RE_DAM) != 0
                    || (Work & (long)PIT_work.PSE_RI_RE_DAM) != 0
                    )
                {
                    sWork = sWork + virg + "Mechanical Damage";
                    virg = ", ";
                }
                if ((Work & (long)PIT_work.PSE_BODY_MINOR) != 0)
                {
                    sWork = sWork + virg + "Minor Damage";
                    virg = ", ";
                }
                if ((Work & (long)PIT_work.PSE_BODY_MAJOR) != 0)
                {
                    sWork = sWork + virg + "Major Damage";
                    virg = ", ";
                }
                if ((Work & (long)PIT_work.PSE_REFUEL) != 0)
                {
                    sWork = sWork + virg + "Refuelling";
                    virg = ", ";
                }
                if ((Work & (long)PIT_work.PSE_SETUP) != 0)
                {
                    sWork = sWork + virg + "Setup";
                    virg = ", ";
                }
                if (
                    (Work & (long)PIT_work.PSE_FR_WHL) != 0
                    || (Work & (long)PIT_work.PSE_LE_FR_WHL) != 0
                    || (Work & (long)PIT_work.PSE_RI_FR_WHL) != 0
                    || (Work & (long)PIT_work.PSE_RE_WHL) != 0
                    || (Work & (long)PIT_work.PSE_LE_RE_WHL) != 0
                    || (Work & (long)PIT_work.PSE_RI_RE_WHL) != 0
                    )
                {
                    sWork = sWork + virg + "Wheels";
                    virg = ", ";
                }

            }
        }
        // Ok For insim 4
        public class PSF
        {
            public readonly int PLID;
            public readonly long STime;

            public PSF(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
                STime = pakGetUnsigned(pak, 4);
            }
        }
        // LAP OK for insim 4
        public class LAP
        {
            public readonly int PLID;
            public readonly long LTime;
            public readonly long ETime;
            public readonly int LapsDone;
            public readonly int Flags;
            public readonly int Penalty;
            public readonly int NumStop;


            public LAP(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
                LTime = pakGetUnsigned(pak, 4);
                ETime = pakGetUnsigned(pak, 8);
                LapsDone = pakGetWord(pak, 12);
                Flags = pakGetWord(pak, 14);
                Penalty = pakGetByte(pak, 17);
                NumStop = pakGetByte(pak, 18);
            }
        }

        /// <summary>
        /// Split X time decoder
        /// </summary>
        // LAP OK for insim 4
        public class SPX
        {
            public readonly int PLID;
            public readonly long STime;
            public readonly long ETime;
            public readonly int Split;
            public readonly int Penalty;
            public readonly int NumStop;

            public SPX(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
                STime = pakGetUnsigned(pak, 4);
                ETime = pakGetUnsigned(pak, 8);
                Split = pakGetByte(pak, 12);
                Penalty = pakGetByte(pak, 13);
                NumStop = pakGetByte(pak, 14);
            }
        }
        // Race Restart Ok for Insim 7
        public class RST
        {
            /// <summary>
            /// Gets time for qualifications in minutes. If zero, race started.
            /// </summary>
            public readonly int RaceLaps;
            public readonly int QualMins;
            public readonly int NumP;
            public readonly int Timing;
            public readonly string Track;
            public readonly int Weather;
            public readonly int Wind;
            public readonly int Flags;
            public readonly int NumNodes;
            public readonly int Finish;
            public readonly int Split1;
            public readonly int Split2;
            public readonly int Split3;

            public RST(byte[] pak)
            {
                RaceLaps = pakGetByte(pak, 4);
                QualMins = pakGetByte(pak, 5);
                NumP = pakGetByte(pak, 6);
                Timing = pakGetByte(pak, 7);
                Track = pakGetString(pak, 8, 6);
                Weather = pakGetByte(pak, 14);
                Wind = pakGetByte(pak, 15);
                Flags = pakGetWord(pak, 16);
                NumNodes = pakGetWord(pak, 18);
                Finish = pakGetWord(pak, 20);
                Split1 = pakGetWord(pak, 22);
                Split2 = pakGetWord(pak, 24);
                Split3 = pakGetWord(pak, 26);
            }
        }

        // Ok for Insim 7
        public class NCN
        {
            public readonly int UCID;
            public readonly string userName;
            public readonly string nickName;
            public readonly int Admin;
            public readonly int Total;
            public readonly int Flags;


            public NCN(byte[] pak)
            {
                UCID = pakGetByte(pak, 3);
                userName = pakGetString(pak, 4, 24);
                nickName = pakGetString(pak, 28, 24);
                Admin = pakGetByte(pak, 52);
                Total = pakGetByte(pak, 53);
                Flags = pakGetByte(pak, 54);
            }
        }
        // Ok For insim 4
        public class CNL
        {
            public readonly int UCID;
            public readonly int Reason;
            public readonly int Total;

            public CNL(byte[] pak)
            {
                UCID = pakGetByte(pak, 3);
                Reason = pakGetByte(pak, 4);
                Total = pakGetByte(pak, 5);
            }
        }
        // Ok For insim 4
        public class CPR
        {
            public readonly int UCID;
            public readonly string newNickName;
            public readonly string Plate;

            public CPR(byte[] pak)
            {
                UCID = pakGetByte(pak, 3);
                newNickName = pakGetString(pak, 4, 24);
                Plate = pakGetString(pak, 28, 8);
            }
        }
        // Ok for Insim 4
        /// <summary>
        /// PLayer Pits
        /// </summary>
        public class PLP
        {
            public readonly int PLID;

            public PLP(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
            }
        }
        // Ok for Insim 4
        /// <summary>
        /// PLayer Leave race
        /// </summary>
        public class PLL
        {
            public readonly int PLID;

            public PLL(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
            }
        }
        // Ok For insim 4
        public class FLG
        {
            public readonly int PLID;
            public readonly int OffOn;
            public readonly int Flag;
            public readonly int CarBehind;

            public FLG(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
                OffOn = pakGetByte(pak, 4);
                Flag = pakGetByte(pak, 5);
                CarBehind = pakGetByte(pak, 6);
            }
        }
        public class PEN
        {
            public readonly int PLID;
            public readonly int OldPen;
            public readonly int NewPen;
            public readonly int Reason;

            public PEN(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
                OldPen = pakGetByte(pak, 4);
                NewPen = pakGetByte(pak, 5);
                Reason = pakGetByte(pak, 6);
            }
        }
        public class BFN
        {
            public readonly int SubT;
            public readonly int UCID;
            public readonly int ClickID;

            public BFN(byte[] pak)
            {
                SubT = pakGetByte(pak, 3);
                UCID = pakGetByte(pak, 4);
                ClickID = pakGetByte(pak, 5);
            }
        }
        public class BTC
        {
            public readonly int UCID;
            public readonly int ClickID;
            public readonly int CFlags;

            public BTC(byte[] pak)
            {
                UCID = pakGetByte(pak, 3);
                ClickID = pakGetByte(pak, 4);
                CFlags = pakGetByte(pak, 6);
            }
        }
        public class BTT
        {
            public readonly int UCID;
            public readonly int ClickID;
            public readonly int TypIn;
            public string Text;

            public BTT(byte[] pak)
            {
                UCID = pakGetByte(pak, 3);
                ClickID = pakGetByte(pak, 4);
                TypIn = pakGetByte(pak, 6);
                Text = pakGetString(pak, 8, 96);
            }
        }
        public class TOC
        {
            public readonly int PLID;
            public readonly int OldUCID;
            public readonly int NewUCID;


            public TOC(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
                OldUCID = pakGetByte(pak, 4);
                NewUCID = pakGetByte(pak, 5);
            }
        }
        public class VTN
        {
            public readonly int UCID;
            public readonly int Action;


            public VTN(byte[] pak)
            {
                UCID = pakGetByte(pak, 4);
                Action = pakGetByte(pak, 5);
            }
        }
        public class PFL
        {
            public readonly int PLID;
            public readonly int Flags;



            public PFL(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
                Flags = pakGetWord(pak, 4);
            }
        }
        public class CRS
        {
            public readonly int PLID;

            public CRS(byte[] pak)
            {
                PLID = pakGetByte(pak, 3);
            }
        }

        // OK For Insim 7
        //Admin Command Report ( added @ 16-10-2017)
        public class CIM // Size 8 Bytes
        {
            public readonly int PacketSize;         // 8 Bytes
            public readonly int ReqI;               // 0
            public readonly int UCID;               // Unique Connection ID
            public readonly int Mode;               // mode identifier
            public readonly int SubMode;            // submode identifier
            public readonly int SelType;            // selected object type
            public readonly int Sp3;             // Spare

            public CIM(byte[] packet)
            {
                PacketSize = pakGetByte(packet, 1);       //1 Byte
                ReqI = pakGetByte(packet, 2);             //1 Byte
                UCID = pakGetByte(packet, 3);             //1 Byte
                Mode = pakGetByte(packet, 4);             //1 Byte
                SubMode = pakGetByte(packet, 5);          //1 Byte
                SelType = pakGetByte(packet, 6);          //1 Byte
                Sp3 = pakGetByte(packet, 7);              //1 Byte
            }
        }
    }
}

