using System;
using System.Collections.Generic;
using System.Text;
using everdrive3x.Misc;

namespace everdrive3x.Hardware
{
    class DeviceControl
    {

        Communication com;

        private DeviceControl()
        {
        }

        public void close()
        {
            try
            {
                com.Port.Close();
            }
            catch (Exception) { }
            
        }

        public static DeviceControl getDeviceControl(String port_name)
        {
            DeviceControl dc = new DeviceControl();
            dc.com = new Communication(port_name);
            dc.com.Port.BaudRate = Communication.BAUDRATE_BOOTLOADER;
            dc.com.Port.Open();
            dc.exitBootLoader();
            dc.com.Port.Close();
            dc.com.Port.BaudRate = Communication.BAUDRATE_WORK;
            dc.com.Port.Open();
            try
            {
                dc.test();
            }
            catch (Exception x)
            {
                dc.close();
                throw x;
            }

            return dc;
        }

        public static DeviceControl getBootLoaderControl(String port_name)
        {
            DeviceControl dc = new DeviceControl();

            dc.com = new Communication(port_name);
            dc.com.Port.BaudRate = Communication.BAUDRATE_BOOTLOADER;
            dc.com.Port.Open();

            try
            {
                dc.test();
            }
            catch (Exception x)
            {
                dc.close();
                throw x;
            }
            return dc;
        }


        void exitBootLoader()
        {
            try
            {
                com.Port.ReadTimeout = 10;
                com.txCmd(CmdTable.CMD_BOOT_EXIT_BOOT);
                com.rxCmd();
            }
            catch (Exception) { }
        }

        public void test()
        {
            com.Port.ReadTimeout = 50;
            com.txCmd(CmdTable.CMD_TEST_DEVICE);
            com.rxOk();
        }

        public void eraseAuto(int data_len)
        {
            com.Port.ReadTimeout = 1000;
            com.write32(data_len);
            com.txData(CmdTable.CMD_ERASE_AUTO);
            for (int i = 0; i < 80; i++)
            {
                try
                {
                    com.rxOk();
                    return;
                }
                catch (TimeoutException) { }
            }

            throw new TimeoutException();
        }

        public void erase()
        {
            com.Port.ReadTimeout = 1000;
            com.txCmd(CmdTable.CMD_ERASE);
            for (int i = 0; i < 80; i++)
            {
                try
                {
                    com.rxOk();
                    return;
                }
                catch (TimeoutException) { }
            }

            throw new TimeoutException();
        }

        public void eraseBlocks(int data_len)
        {
            com.Port.ReadTimeout = 1000;
            com.write32(data_len);
            com.txData(CmdTable.CMD_ERASE_BLOCKS);
            for (int i = 0; i < 80; i++)
            {
                try
                {
                    com.rxOk();
                    return;
                }
                catch (TimeoutException) { }
            }

            throw new TimeoutException();
        }

        public byte[] readBlock(int len)
        {
            com.Port.ReadTimeout = 1000;
            com.write16(len);
            com.txData(CmdTable.CMD_DUMP_BLOCK);
            com.rxData(len);
            return com.read(len);
        }

        public void writeBlock(byte[] src, int offset, int len)
        {
            com.Port.ReadTimeout = 2000;
            com.write16(len);
            com.write(src, offset, len);
            com.txData(CmdTable.CMD_PROG_BLOCK);
            com.rxOk();
        }

        public void unlockByPass()
        {
            com.Port.ReadTimeout = 100;
            com.txCmd(CmdTable.CMD_UNLOCK_BYPASS);
            com.rxOk();
        }
        public void resetByPass()
        {
            com.Port.ReadTimeout = 100;
            com.txCmd(CmdTable.CMD_RESET_BYPASS);
            com.rxOk();
        }

        public void setAddr(int addr)
        {
            com.Port.ReadTimeout = 100;
            com.write32(addr);
            com.txData(CmdTable.CMD_SET_ADDR);
            com.rxOk();
        }

        public void writeEeprom(int addr, int val)
        {
            com.Port.ReadTimeout = 100;
            com.write8(addr);
            com.write8(val);
            com.txData(CmdTable.CMD_WRITE_EEPROM);
            com.rxOk();
        }

        public int readEeprom(int addr)
        {
            com.Port.ReadTimeout = 100;
            com.write8(addr);
            com.txData(CmdTable.CMD_READ_EEPROM);
            com.rxOk();
            com.rxData(1);
            return com.read8();
        }

        public void reset()
        {
            com.txCmd(CmdTable.CMD_RESET);
            com.rxOk();
        }

        public int getDeviceID()
        {
            com.Port.ReadTimeout = 100;
            com.txCmd(CmdTable.CMD_DEVICE_ID);
            com.rxOk();
            com.rxData(2);
            return com.read16();
        }

        public int getFirmwareVersion()
        {
            com.Port.ReadTimeout = 100;
            com.txCmd(CmdTable.CMD_FIRM_VERSION);
            com.rxOk();
            com.rxData(2);
            return com.read16();
        }

        public Communication DirectCommunication
        {
            get { return com; }
        }


        public void bootEnterProgMode()
        {
            com.Port.ReadTimeout = 100;
            com.txCmd(CmdTable.CMD_BOOT_ENTER_PROG_MODE);
            com.rxOk();
        }

        public void bootErase()
        {
            com.Port.ReadTimeout = 6000;
            com.txCmd(CmdTable.CMD_BOOT_ERASE);
            com.rxOk();

        }

        
        public void bootReadPage(byte[] buff, int offset, int page_len)
        {
            com.Port.ReadTimeout = 5000;
            com.txCmd(CmdTable.CMD_BOOT_READ);
            com.rxOk();
            com.rxData(page_len);

            com.read(buff, offset, page_len);
        }

        public void bootWritePage(byte[] bin, int offset, int page_len)
        {
            com.Port.ReadTimeout = 2000;
            com.write(bin, offset, page_len);
            com.txData(CmdTable.CMD_BOOT_WRITE_PAGE);
            com.rxOk();
          
        }

        public int bootPageSize()
        {
            com.Port.ReadTimeout = 500;
            com.txCmd(CmdTable.CMD_BOOT_PAGE_SIZE);
            com.rxOk();
            com.rxData(2);
            return com.read16();
        }

        public int bootAppEnd()
        {
            com.Port.ReadTimeout = 500;
            com.txCmd(CmdTable.CMD_BOOT_APP_END);
            com.rxOk();
            com.rxData(2);
            return com.read16();
        }

        public UInt16 calcCrcForArea(int offset, int len)
        {
            com.Port.ReadTimeout = 100;
            com.write32(offset);
            com.write32(len);
            com.txData(CmdTable.CMD_CRC_FOR_AREA);
            com.rxOk();
            com.Port.ReadTimeout = 2000;
            for (int i = 0; i < 60; i++)
            {
                try
                {
                    com.rxData(2);
                    return (UInt16)com.read16();
                }
                catch (Exception) { }

            }

            throw new TimeoutException();
            
        }
       

    }
}
