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

namespace everdrive3x.Misc
{
    class HexFile
    {

        HexData hex;

        public struct HexData
        {
            public byte[][] bin;
            public int[] address;

            public HexData(int len)
            {
                this.bin = new byte[len][];
                address = new int[len];
            }

            public HexData(HexData hex, int offset, int len)
            {
                this.bin = new byte[len][];
                address = new int[len];
                for (int i = 0; i < len; i++)
                {
                    this.bin[i] = hex.bin[i + offset];
                    this.address[i] = hex.address[i + offset];
                }
            }

            public void setData(byte[] src,int idx, int len, int addr)
            {
                setData(src, 0, idx, len, addr);
            }

            public void setData(byte[] src, int offset, int idx, int len, int addr)
            {
                bin[idx] = new byte[len];
                System.Array.Copy(src, offset, bin[idx], 0, len);
                address[idx] = addr;
            }
        }

        private HexFile()
        {
        }

        private HexFile(byte[] src, int offset, int len)
        {
            hex = new HexData(len / 16);
            for (int i = 0; i < len / 16; i++)
            {
                hex.setData(src, offset + i * 16, i, 16, i * 16);
            }
        }

        public static HexFile makeFromBunary(byte[] src, int offset, int len)
        {
            return new HexFile(src, offset, len);
        }

        public static HexFile makeFromHexFile(String filename)
        {
            return new HexFile(filename);
        }

        private HexFile(String filename)
        {
            FileStream file = File.OpenRead(filename);
            int len = 0;
            int addr = 0;
            int cmd = 0;
            byte []data = new byte[16];
            byte calc_crc = 0;
            byte read_crc = 0;
            hex = new HexData(1024);
            int counter = 0;
            

            while (true)
            {
                while (file.ReadByte() != ':') ;
                calc_crc = 0;
                len = DataTool.readHexByte(file);
                addr = DataTool.readHexByte(file) << 8;
                addr |= DataTool.readHexByte(file);
                cmd = DataTool.readHexByte(file);
                calc_crc -= (byte)len;
                calc_crc -= (byte)(addr >> 8);
                calc_crc -= (byte)(addr & 0xff);
                calc_crc -= (byte)cmd;
                for (int i = 0; i < len; i++)
                {
                    data[i] = (byte)DataTool.readHexByte(file);
                    calc_crc -= data[i];
                }
                read_crc = (byte)DataTool.readHexByte(file);
                if (calc_crc != read_crc) throw new Exception("bad hex crc: calc = " + calc_crc + ", read = " + read_crc);

                if (cmd == 1) break;
                if (cmd == 0)
                {
                    hex.setData(data, counter++, len, addr);
                }
            }

            hex = new HexData(hex, 0, counter);
            file.Close();
        }

        

        public HexData Data
        {
            get { return hex; }
        }

        public byte[] toBinary()
        {
            int max_addr = 0;
            foreach (int i in hex.address) if (max_addr < i) max_addr = i;
            byte[] buff = new byte[max_addr + 16];
            for (int i = 0; i < hex.address.Length; i++)
            {
                System.Array.Copy(hex.bin[i], 0, buff, hex.address[i], hex.bin[i].Length);
            }
            return buff;
        }

        

        public byte[] toHex()
        {
            String buff = "";

            for (int i = 0; i < hex.address.Length; i++)
            {
                buff += ":";
                buff += DataTool.byteToHex(hex.bin[i].Length);
                buff += DataTool.byteToHex(hex.address[i] >> 8);
                buff += DataTool.byteToHex(hex.address[i]);
                buff += DataTool.byteToHex(0);
                foreach (int val in hex.bin[i]) buff += DataTool.byteToHex(val);
                int crc = 0;
                crc -=(hex.bin[i].Length + (hex.address[i] >> 8) + (hex.address[i] & 0xff) + 0);
                foreach (byte val in hex.bin[i]) crc -= val;

                buff += DataTool.byteToHex(crc);
                buff += "\r\n";
            }

            buff += ":00000001FF\r\n";


            byte[] data = new byte[buff.Length];
            int ctr = 0;

            foreach (char i in buff) data[ctr++] = (byte)i;

            return data;
        }

    }
}
