using System;
using System.IO.Ports;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.SPOT.Hardware;
using SWModules;

namespace HWModules
{
    public class I2CUartBridge : I2CPortBase
    {

        // create write buffer (we need one byte)
        private byte[] RegisterNum = new byte[1] {0x12};
        // create read buffer to read the register
        private byte[] RegisterValue = new byte[1] {0x00};
        private byte[] str = new byte[256];
        private StringBuilder strLine = new StringBuilder(128);
        //number of bytes in read Fifo
        private int nb;


        public int UartBaud {
            get { return UartBaud; }
            protected set
            {
                switch (value)
                {
                    case 1200:
                        _baudIndex = 0;
                        break;
                    case 2400:
                        _baudIndex = 1;
                        break;
                    case 4800:
                        _baudIndex = 2;
                        break;
                    case 9600:
                        _baudIndex = 3;
                        break;
                    case 19200:
                        _baudIndex = 4;
                        break;
                    case 28800:
                        _baudIndex = 5;
                        break;
                    case 38400:
                        _baudIndex = 6;
                        break;
                    case 57600:
                        _baudIndex = 7;
                        break;
                    default:
                        _baudIndex = 4; //19200
                        break;
                }
            }
        }

        private int _baudIndex;

        public System.IO.Ports.Parity UartParity { get; protected set; }
        public int UartDataBits { get; protected set; }
        public System.IO.Ports.StopBits UartStopBits { get; protected set; }

        public I2CUartBridge(I2CDevice.Configuration config, 
            int slotnumber = 3, 
            int baudrate = 19200, 
            System.IO.Ports.Parity parity = Parity.None, 
            int dataBits = 8, 
            System.IO.Ports.StopBits stopBits = StopBits.One )
            : base(config, slotnumber)
        {
            UartBaud = baudrate;
            UartParity = parity;
            UartDataBits = dataBits;
            UartStopBits = stopBits;

            TimeOut = 50;
            Init();
        }

        //************************************************************************************************************************************
        private int Init()
        {
            //initialiaze an EXAR1280 I2C to UART device ONLY.  sets up registers in the proper order.
            //TODO P2 hook up error checking and return value

            int ret = 0;

            RegisterNum[0] = LCR;
            RegisterValue[0] = 0xBF; //LCR=BF enables access to enhanced reg
            RegWrite(RegisterNum, RegisterValue);

            RegisterNum[0] = FCTR;
            RegisterValue[0] = (byte) (RegFCTR.ScratchpadSwap);
                //FCTR=0x41 enables SPR as fifo counter FCTR[6]=1).  FCTR[0] = no sleeping!
            //RegisterValue[0] = 0x41; //FCTR=0x41 enables SPR as fifo counter FCTR[6]=1).  FCTR[0] = no sleeping!
            RegWrite(RegisterNum, RegisterValue);

            RegisterNum[0] = EFR;
            RegisterValue[0] = (byte) RegEFR.EnhFuncEn; //EFR[4] = 1 enables shaded bits, access to DLD and flow control
            RegWrite(RegisterNum, RegisterValue);

            RegisterNum[0] = LCR;
            RegisterValue[0] = (byte) RegLCR.BrDivLatch;
            //LCR[7] = 1 enables access to divisors.  LCR[1,2] = 0 selects 8N1
            RegWrite(RegisterNum, RegisterValue);

            SetBrgValues();

            SetLcrRegister();

            RegisterNum[0] = MCR;
            RegisterValue[0] = 0x00; //MCR
            RegWrite(RegisterNum, RegisterValue);

            RegisterNum[0] = FCR;
            RegisterValue[0] = (byte) RegFCR.FifoEnable; //FCR
            RegWrite(RegisterNum, RegisterValue);

            RegisterNum[0] = SFR;
            RegisterValue[0] = 0x00; //SFR
            RegWrite(RegisterNum, RegisterValue);

            RegisterNum[0] = EMSR;
            RegisterValue[0] = 0x00; //EMSR[0] makes spr FIFO rx count
            RegWrite(RegisterNum, RegisterValue);

            RegisterNum[0] = IER;
            RegisterValue[0] = (byte) RegIER.RHRInterrupt; //IER, only if LCR[7]=0
            RegWrite(RegisterNum, RegisterValue);

            //clear read fifo
            FifoRead(str);

            //clear interrupt
            ClearIRQ();

            return (ret); //TODO P2 hook this up
        }

        private void SetLcrRegister()
        {
            RegisterNum[0] = LCR;
            RegLCR lcrFlags = 0;
            switch (UartDataBits)
            {
                case 5:
                    lcrFlags |= RegLCR.WordLen5;
                    break;
                case 6:
                    lcrFlags |= RegLCR.WordLen6;
                    break;
                case 7:
                    lcrFlags |= RegLCR.WordLen7;
                    break;
                case 8:
                    lcrFlags |= RegLCR.WordLen8;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("I2CUartBridge.DataBits", "Databits must be 5-8 for EXAR");
            }

            switch (UartStopBits)
            {
                case StopBits.OnePointFive:
                    lcrFlags |= RegLCR.Stop1p5;
                    break;
                case StopBits.Two:
                    lcrFlags |= RegLCR.Stop2;
                    break;
                case StopBits.None:
                    throw new ArgumentException("No stop bit configurations are not supported by the EXAR specification.");
                case StopBits.One:
                    lcrFlags |= RegLCR.Stop1;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            switch (UartParity)
            {
                case Parity.None:
                    lcrFlags |= RegLCR.ParityNone;
                    break;
                case Parity.Even:
                    lcrFlags |= RegLCR.ParityEven;
                    break;
                case Parity.Odd:
                    lcrFlags |= RegLCR.ParityOdd;
                    break;
                case Parity.Mark:
                    lcrFlags |= RegLCR.ParityForceMark;
                    break;
                case Parity.Space:
                    lcrFlags |= RegLCR.ParityForceSpace;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            RegisterValue[0] = (byte) lcrFlags;
            RegWrite(RegisterNum, RegisterValue);
        }

//end Init()

        private bool SetBrgValues()
        {
            
            RegisterNum[0] = DLD;
            RegisterValue[0] = DldBytes[_baudIndex]; //DLD
            RegWrite(RegisterNum, RegisterValue);

            RegisterNum[0] = DLM;
            RegisterValue[0] = DlmBytes[_baudIndex]; //DLM
            RegWrite(RegisterNum, RegisterValue);

            RegisterNum[0] = DLL;
            RegisterValue[0] = DllBytes[_baudIndex]; //DLL
            RegWrite(RegisterNum, RegisterValue);
            //--------------------------------------------------
            return true;

        }



        //************************************************************************************************************************************
        //CRITICAL, ADDING A DEBUG MESSAGE HERE DERAILS THE OPERATION AND LEADS TO SYSTEM
        //INSTABILITY, DO NOT TOUCH!!
        [MethodImpl(MethodImplOptions.Synchronized)]
        public int FifoRead(byte[] rtstr)
        {
            //reads all characters in read fifo and returns in rtstr.
            //TODO P2 hook up error

            int bytes_read = 0;
            int j = 0;
            int nb;

            do {
                RegisterNum[0] = SPR;
                nb = RegRead(RegisterNum);

                //bounds checking
                if (nb < 1) nb = 0;
                if (nb > 128) 
                    nb = 128;


                
                //get nb bytes
                for (int i = 0; i < nb; i++)
                {
                    RegisterNum[0] = (byte)0;

                    if (bytes_read + 1 == rtstr.Length)
                    {
                        RegRead(RegisterNum);
                    }
                    else
                    {
                        rtstr[bytes_read] = (byte) RegRead(RegisterNum);
                        bytes_read++;
                    }
                }

                if (nb <=0) j++;
            }
            while ((nb > 0) && (j < 3)) ;

            return bytes_read;

        } //end fiforead()
        //************************************************************************************************************************************
        public int i2cprint(byte[] sndstr, int nb, int offset = 0)
        {
            //TODO P2 hook up error condition
            for (int i = 0; i < nb; i++)
            {
                RegisterNum[0] = 0;
                RegisterValue[0] = sndstr[i+offset];
                RegWrite(RegisterNum, RegisterValue);
            }
            return nb; //send something reasonable back.  just using nb so 
            //the damn compiler stops complaining.
        } //end i2cprint
        //************************************************************************************************************************************
        [MethodImpl(MethodImplOptions.Synchronized)]
        public ushort RegRead(byte[] RegNum)
        {
            //TODO P2 hook up error condition
            I2CDevice.I2CTransaction[] xActions =
                new I2CDevice.I2CTransaction[2];

            RegNum[0] = (byte) ((int) RegNum[0] & 0x0F);
            RegNum[0] = (byte) ((int) RegNum[0] << 3);
            //RegNum[0] = (byte)((int)RegNum[0] | 0x01);
            xActions[0] = I2CDevice.CreateWriteTransaction(RegNum);
            xActions[1] = I2CDevice.CreateReadTransaction(RegNum);
            // Now we access the I2C bus using a timeout of one second 
            // if the execute command returns zero, the transaction failed (this 
            // is a good check to make sure that you are communicating with the device correctly
            // and don’t have a wiring issue or other problem with the I2C device)
            if (Execute(xActions, TimeOut) == 0)
            {
                //EngrLogger.Comment("RegRead:  Write Fail 1: " + RegNum[0].ToString());
                if (Execute(xActions, TimeOut) == 0)
                {
                    //EngrLogger.Comment("RegRead:  Write Fail 2: " + RegNum[0].ToString());
                }
            }

            return RegNum[0];
        } //end RegRead()
        //************************************************************************************************************************************
        public ushort RegWrite(byte[] RegNum, byte[] RegVal)
        {
            //to do, hook up error condition
            ushort ret = 0;
            I2CDevice.I2CTransaction[] xActions =
                new I2CDevice.I2CTransaction[1];


            RegNum[0] = (byte) ((int) RegNum[0] & 0x0F);
            RegNum[0] = (byte) ((int) RegNum[0] << 3);
            //RegNum[0] = (byte)((int)RegNum[0] | 0x01);
            byte[] snd = new byte[2] {RegNum[0], RegVal[0]};
            //Debug.Print("RegWrite:  Writing " + snd[1].ToString() + "to reg num "
            //     + snd[0].ToString());
            xActions[0] = I2CDevice.CreateWriteTransaction(snd);

            if (Execute(xActions, TimeOut) == 0)
            {
                //EngrLogger.Comment("RegWrite:  Write Fail 1: " + RegNum[0].ToString());
                if (Execute(xActions, TimeOut) == 0)
                {
                    //EngrLogger.Comment("RegWrite:  Write Fail 2: " + RegNum[0].ToString());
                    ret = 1;
                }
            }

            return (ret);
        } //end RegWrite
        //************************************************************************************************************************************
        public void ClearIRQ()
        {
            RegisterNum[0] = 2;
            RegRead(RegisterNum);
        } //end ClearIRQ()
        //************************************************************************************************************************************
        public StringBuilder get_i2c_buffer()
        {
            //StringBuilder returnField;
            int i = 0;
            nb = FifoRead(str);

            if (nb > 0)
            {
                //returnField.Append(System.Text.SafeEncoding.GetChars(str, 0, nb));
                for (i = 0; i < nb; i++) //non printable chars crash program
                {
                    if (str[i] > 126) str[i] = 0;
                }
                strLine.Clear();
                strLine.Append(SafeEncoding.GetChars(str, 0, nb));
                if (strLine.Length > 0)
                {
                    //Debug.Print(strLine.ToString());
                }
            }
            return (strLine);
        } //end
        //************************************************************************************************************************************
        public void clear_i2c_buffer(int x)
        {
            //Debug.Print("clear_buffer " + x.ToString());
            //Debug.Print("i2c_flag=" + i2c_flg.ToString());
            //Program.I2CBridge.Config = con;
            nb = FifoRead(str);
            //I2CInstrument.ClearIRQ(I2CBridge);
            //i2c_flg &= ~(1 << (x - 1)); //clear the bit
            //Debug.Print("i2c_flag=" + i2c_flg.ToString());
            //Debug.Print("Clearing B" + x.ToString() + ":" + nb.ToString());
        } //end
        //************************************************************************************************************************************

        /// <summary>
        /// Internal Register Addresses
        /// LCR != 0xBF
        /// </summary>
        const byte RHR = 0x0;

        const byte THR = 0x0;
        const byte IER = 0x1;
        const byte FCR = 0x2;
        const byte ISR = 0x2;
        const byte LCR = 0x3;
        const byte MCR = 0x4;
        const byte LSR = 0x5;
        const byte SHR = 0x5; // requires EFR bit-4 = 1
        const byte MSR = 0x6;
        const byte SFR = 0x6; // requires EFR bit-4 = 1
        const byte TCR = 0x6;
        const byte SPR = 0x7; // requires SFR bit-0 = 0, FCTR bit-6 = 0
        const byte GPIOLVL = 0x7; // requires SFR bit-0 = 1, FCTR bit-6 = 0
        const byte EMSR = 0x7; // requires SFR bit-0 = 0, FCTR bit-6 = 1
        const byte FC = 0x7; // requires SFR bit-0 = 0, FCTR bit-6 = 1
        const byte DREV = 0x0; //LCR[7] = 1 DLL = 0x00, DLM = 0x00
        const byte DLL = 0x0;
        const byte DLM = 0x1;
        const byte DLD = 0x2;

        /// <summary>
        /// Enhanced Registers, LCR must be 0xBF
        /// </summary>
        const byte TRIG = 0x0;

        const byte FCTR = 0x1;
        const byte EFR = 0x2;
        const byte XON1 = 0x4;
        const byte XON2 = 0x5;
        const byte XOFF1 = 0x6;
        const byte XOFF2 = 0x7;
        const byte GPIOINT = 0x4; // requires SFR bit-0 = 1
        const byte GPIO3T = 0x5; // requires SFR bit-0 = 1
        const byte GPIOINV = 0x6; // requires SFR bit-0 = 1
        const byte GPIOSEL = 0x7; // requires SFR bit-0 = 1

        /// <summary>
        /// Interrupt Enable Register - R/W
        /// </summary>
        [Flags]
        private enum RegIER
        {
            RHRInterrupt = 0x01, //IER[0]: RHR interrupt enable
            THRInterrupt = 0x02, //IER[1]: THR interrupt enable
            RxLineInterrupt = 0x04, //IER[2]: Receive Line Status Interrupt Enable
            ModemStatRegInterrupt = 0x08, //IER[3]: Modem Status Interrupt Enable
            SleepMode = 0x10, //IER[4]: Sleep Mode enable (requires EFR[4] == 1)
            SoftwareFlowControl = 0x20, //IER[5]: Xoff Interrupt Enable (requires EFR[4] == 1)
            RtsInterrupt = 0x40, //IER[6]: RTS# Output Interrupt Enable (requires EFR[4] == 1) 
            CtsInterrupt = 0x80 //IER[7]: CTS# Input Interrupt Enable (requires EFR[4] == 1)
        }

        /// <summary>
        /// Interrupt Status Register - Read Only
        /// </summary>
        [Flags]
        private enum RegISR
        {
            IntStatus = 0x01,

            //ISR[0:5]: Interrupt Source
            MaskIntSrc = 0x1F,
            IntSrcLSR = 0x06,
            IntSrcRxTimeout = 0x0C,
            IntSrcRxDataRdy = 0x04,
            IntSrcTxReady = 0x02,
            IntSrcMsr = 0x00,
            IntSrcXonXoff = 0x10,
            IntSrcCtsRts = 0x20,
            IntSrcNone = 0x01,

            //ISR[6]: GPIO interrupt status (when there is an interrupt, it will be the inverse of ISR[7]
            GpioIntStatus = 0x40,
            //ISR[7]: FIFO enable status
            FifoEnableStatus = 0x80
        }

        /// <summary>
        /// FIFO Control Register - Write Only
        /// </summary>
        [Flags]
        private enum RegFCR
        {
            FifoEnable = 0x01, //FCR[0]: Tx and Rx Fifo Enable
            RxFifoReset = 0x02, //FCR[1]: RX Fifo Reset
            TxFifoReset = 0x04, //FCR[2]: Tx Fifo Reset
            WakeUpIntEn = 0x08, //FCR[3]: Enable Wake Up interrupt
            MaskTxFifoTrig = 0x30, //FCR[5:4]: Tx Fifo Trigger select mask (requires EFR[4] == 1)
            TxFifoLvlOpt0 = 0x00,
            TxFifoLvlOpt1 = 0x10,
            TxFifoLvlOpt2 = 0x20,
            TxFifoLvlOpt3 = 0x30,

            MaskRxFifoTrig = 0xC0, //FCR[6:7]: Rx Fifo Trigger select mask 
            RxFifoLvlOpt0 = 0x00,
            RxFifoLvlOpt1 = 0x40,
            RxFifoLvlOpt2 = 0x80,
            RxFifoLvlOpt3 = 0xC0,

        }

        /// <summary>
        /// Line Control Register - Read/Write
        /// </summary>
        [Flags]
        private enum RegLCR
        {
            MaskWordLen = 0x03,
            WordLen5 = 0x00,
            WordLen6 = 0x01,
            WordLen7 = 0x02,
            WordLen8 = 0x03,

            //LCR[2] 
            MaskStop = 0x01 << 2,
            Stop1 = 0x00 << 2, //for all word lengths
            Stop1p5 = 0x01 << 2, //for word length 5
            Stop2 = 0x01 << 2, //for word lengths 6,7,8

            //LCR[3:5]: Parity Select
            MaskParity = 0x07 << 3, //mask for parity 
            ParityNone = 0x00 << 3, //no parity
            ParityOdd = 0x01 << 3, //odd parity 
            ParityEven = 0x03 << 3, //even parity 
            ParityForceMark = 0x05 << 3, //Force parity to mark, HIGH
            ParityForceSpace = 0x07 << 3, //Force partiy to space, LOW


            //LCR[6]: Transmit Break Enable
            TxBreakNone = 0x00 << 6,
            TxBreakSpace = 0x01 << 6,

            //LCR[7]: Baud Rate Divisors Enable
            BrDivData = 0x00 << 7, //data registers are selected.
            BrDivLatch = 0x01 << 7 //divisor latch registers selected.
        }

        /// <summary>
        /// Modem Control Register - Read/Write
        /// </summary>
        [Flags]
        private enum RegMCR
        {
            DtrOutputLow = 0x01, //MCR[0]: Force Dtr# output low.
            RtsOutputLow = 0x02, //MCR[1]: Force Rts# output low, required to start Auto RTS Flow Control
            ModemIoPins = 0x04, //MCR[2]: GPIO[3:0] behcable at RI#, CD#, DTR#, DSR#
            OP2OutputLow = 0x08, //MCR[3]: OP2# output set LOW
            LoopBackEn = 0x10, //MCR[4]: Enable local loopback mode.
            XonAnyEn = 0x20, //MCR[5]: Enable Xon-Any Function (requires EFR[4] == 1)
            IrdaEn = 0x40, //MCR[6]: Enable the IrDA rx and tx IO. (requires EFR[4] == 1)
            ClockDiv4 = 0x80, //MCR[7]: Clock Prescalar Divied by 4 (requires EFR[4] == 1) default div by 1
        }

        /// <summary>
        /// Line Status Register - Read Only
        /// </summary>
        [Flags]
        private enum RegLSR
        {
            RxDataReady = 0x01, //LSR[0]: Data received and in the holding registor or FIFO
            OverrunError = 0x02, //LSR[1]: Overrun error 
            ParityError = 0x04, //LSR[2]: Parity error
            FramingError = 0x08, //LSR[3]: Framing error
            BreakRx = 0x10, //LSR[4]: Receiver received a break signal
            TxHoldingRegEmpty = 0x20, //LSR[5]: Transmit Holding Register Empty Flag
            ThrTsrEmpty = 0x40, //LSR[6]: Transmitter idle
            RxFifoError = 0x80, //LSR[7]: Sum of all error bits in the RX FIFO
        }

        /// <summary>
        /// Setup/Hysteresis Register - Write Only
        /// </summary>
        [Flags]
        private enum RegSHR
        {
            Tad00 = 0x00,
            Tad04 = 0x01,
            Tad06 = 0x02,
            Tad08 = 0x03,
            Tad08b = 0x04,
            Tad16 = 0x05,
            Tad24 = 0x06,
            Tad32 = 0x07,
            Tad40 = 0x08,
            Tad44 = 0x09,
            Tad48 = 0x0A,
            Tad52 = 0x0B,
            Tad12 = 0x0C,
            Tad20 = 0x0D,
            Tad28 = 0x0E,
            Tad36 = 0x0F,

            RtsBitLen0 = 0x00,
            RtsBitLen1 = 0x10,
            RtsBitLen2 = 0x20,
            RtsBitLen3 = 0x30,
            RtsBitLen4 = 0x40,
            RtsBitLen5 = 0x50,
            RtsBitLen6 = 0x60,
            RtsBitLen7 = 0x70,
            RtsBitLen8 = 0x80,
            RtsBitLen9 = 0x90,
            RtsBitLen10 = 0xA0,
            RtsBitLen11 = 0xB0,
            RtsBitLen12 = 0xC0,
            RtsBitLen13 = 0xD0,
            RtsBitLen14 = 0xE0,
            RtsBitLen15 = 0xF0
        }

        /// <summary>
        /// Modem Status Register - Read Only
        /// </summary>
        [Flags]
        private enum RegMSR
        {
            CtsChanged = 0x01, //MSR[0]: Delta CTS# Input Flag
            DsrChanged = 0x02, //MSR[1]: Delta DSR# Input Flag
            RiChanged = 0x04, //MSR[2]: Delt RI# Input from LOW to HIGH ending of the ringing signal
            CdChanged = 0x08, //MSR[3]: Delta CD# Input Flag
            CtsStatus = 0x10, //MSR[4]: CTS# Input Status
            DsrStatus = 0x20, //MSR[5]: DSR# Input Status
            RiStatus = 0x40, //MSR[6]: RI# Input Status
            CdStatus = 0x80, //MSR[7]: CD# Inputer Status
        }

        /// <summary>
        /// Special Function Register - Write Only
        /// </summary>
        [Flags]
        private enum RegSFR
        {
            //ALL FLAGS REQUIRE EFR[4] == 1
            GpioEnable = 0x01, //SFR[0]: Enable GPIO Registers
            GpioSelectHigh = 0x02, //SFR[1]: GPIOLVL, GPIOINT, GPIO3T, GPIOINV and GPIOSEL 
            // registers will control and report GPIO[15:8] (default GPIO[7:0])
            GpioIntEnable = 0x04, //SFR[2]: GPIO interrupt enabled
            FastIrEnable = 0x08, //SFR[3]: IrDA version 1.1
            TxDisable = 0x10, //SFR[4]: Disable the Transmitter
            RxDisable = 0x20, //SFR[5]: Disable the Receiver
            NineBitEnable = 0x40, //SFR[6]: Enable the 9-bit or Multidrop Mode
            TxAddrBit = 0x80, //SFR[7]: Value of the 9th bit will be '1'
        }

        /// <summary>
        /// Enhanced Mode Select Register - Write Only
        /// 
        /// This register replaces SPR (during a write) 
        /// and is accessible only when FCTR[6] = 1
        /// </summary>
        [Flags]
        private enum RegESMR
        {
            MaskFifoLevel = 0x03, //EMSR[1:0]: Rx/Tx FIFO Level Count
            FifoLvlRx = 0x00, //RX fifoe level counter mode
            FifoLvlTx = 0x01, //TX fifo level counter mode
            FifoLvlAlt = 0x03, //Alternate RX/TX Fifo counter mode

            SendTxImmediately = 0x04, //ESMR[2]: Send TX immediately
            InvertRtsIn485 = 0x08, //ESMR[3]: Invert RTS in RS485 Mode
            ModemThreeState = 0x10, //ESMR[4]: TX, RTS# and DTR# are in three state mode
            LsrIntImmediate = 0x40, //ESMR[6]: LSR interrupts immediately
            XoffIntClrIsr = 0x80,
            //ESMR[7]: Xoff/Special Character interrupt can on be cleared by reading the ISR register.
        }

        /// <summary>
        /// Baud Rate Generator Divisor Register - Read / Write
        /// </summary>
        [Flags]
        private enum RegDLD
        {
            MaskValue = 0x0F, //DLD[3:0] Program value of the baud rate (fraction BR * 16)
            MaskSamp = 0x30, //DLD[5:4] Sample rate select 
            Samp16x = 0x00,
            Samp8x = 0x10,
            Samp4x = 0x30,

            MaskBrgSelect = 0xC0, //DLD[6] Enable independent baud rate generators. //DLD[7] Cofigures tx or rx
            BrgTxRxSame = 0x00,
            BrgDiffCfgTx = 0x40,
            BrgDiffCfgRx = 0xC0,
            BrgDiffCfgNone = 0x80,
        }

        /// <summary>
        /// Feature Control Register (FCTR) - Read / Write
        /// </summary>
        [Flags]
        private enum RegFCTR
        {
            PwrdnEn = 0x01, //FCTR[0]: Sleep/Pwrdn# Function ctl. logic 1 output is enable on pwrdn# pin.
            Reserved = 0x02,
            IrDaRxInvert = 0x04, //FCTR[2]: selects rx nput as invertede encoded irda data.
            AutoRs485En = 0x08, //FCTR[3]: Auto RS-485 Direction contrl enabled.

            MaskTxRxTable = 0x30, //FCTR[5:4] Transmit receive trigger table select.
            TxRxTableA = 0x00,
            TxRxTableB = 0x10,
            TxRxTableC = 0x20,
            TxRxTableD = 0x30,

            ScratchpadSwap = 0x40, //FCTR[6]: ScratchpadSwap
            TxTrigReg = 0x80, //FCTR[7]: Programmable trigger register select (1 registers TRG and FC for Tx, 0 for Rx)
        }

        /// <summary>
        /// Enhanced Feature Register - Read / Write
        /// </summary>
        [Flags]
        private enum RegEFR
        {
            MaskSoftFlowCtl = 0x0F, //EFR[3:0]: Software flow control select
            NoTxRxFlowCtl = 0x00, //No Tx and Rx flow control  (default and reset)
            //TODO P3 Do we need software flow control support???

            EnhFuncEn = 0x10, //EFR[4]: Enhance Function Bits Enable
            SpecCharDet = 0x20, //EFR[5]: Special Character Detect Enable
            AutoRtsEn = 0x40, //EFR[6]: Auto Rts Flow Control Enable
            AutoCtsEn = 0x80, //EFR[7]: Auto Cts Flow Control Enable

        }

        private enum BaudRate
        {
            B1200 = 0,
            B2400,
            B4800,
            B9600,
            B19200,
            B28800,
            B38400,
            B57600,
        }

        private static readonly byte[] DlmBytes = new byte[] {
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        };

        private static readonly byte[] DllBytes = new byte[] {
            0x5F, 0x2F, 0x17, 0x0B, 0x05, 0x03, 0x01, 0x01
        };

        private static readonly byte[] DldBytes = new byte[] {
            0x0D, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F
        };


        public int TimeOut { get; set; }
    }

}
