using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Linq;
using System.Windows.Forms;
using DevExpress.XtraEditors;

using System.Runtime.InteropServices;
using System.IO;

using LFP_Manager.Utils;

namespace LFP_Manager
{
    public partial class fmxFwImageConverter : DevExpress.XtraEditors.XtraForm
    {
        #region CONST
        
        //const
        //  mFileMaxSize  : DWORD = 262144;   // 256KByte
        //  sFileMaxSize  : DWORD = 131072;   // 128KByte

        //  ImageCode     : DWORD = $53454355;
        //  ImageEmpty    : DWORD = $00000000;
        //  MasterOnly    : DWORD = $00000001;
        //  SlaveOnly     : DWORD = $00000002;
        //  MasterSlave   : DWORD = $00000003; 
        const UInt32 mFileMaxSize = 262144;         // 256KBytes
        const UInt32 ImageCode    = 0x6D424D53;     // mBMS
        const UInt32 ImageEmpty   = 0x00000000;     // 

        const UInt32 POLYNOMIAL   = 0x04C11DB7;

        #endregion

        #region STRUCTURE
        //TImageHeaderStructure = packed record
        //    ImageCode       : DWORD;
        //    ImageType       : DWORD;
        //    mImageSize      : DWORD;
        //    mImageName      : array[0..31]of Byte;
        //    mImageVersion   : TImageVersionStructure;
        //    mImageCRC       : DWORD;
        //    sImageSize      : DWORD;
        //    sImageName      : array[0..31]of Byte;
        //    sImageVersion   : TImageVersionStructure;
        //    sImageCRC       : DWORD;
        //    reserved        : array[0..927]of Byte;
        //end;
        struct TImageHeaderStructure                                // Total 64bytes
        {
            public UInt32 ImageCode;                                        // 4
            public UInt32 ImageType;                                        // 4
            public UInt32 ImageSize;                                        // 4
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
            public byte[] ImageName;                                        // 32
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
            public byte[] ImageVersion;                                     // 4
            public UInt32 ImageCRC;                                         // 4
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
            public byte[] reserved;                                         // 12
        }
        #endregion

        #region VARIABLES

        TImageHeaderStructure fwHeader;
        byte[] fwdata;

        UInt32[] crc_table;

        #endregion

        #region CONSTRUCTORS

        public fmxFwImageConverter()
        {
            InitializeComponent();

            crc_table = new UInt32[256];

            gen_crc_table();
        }

        #endregion

        /* generate the table of CRC remainders for all possible bytes */
        void gen_crc_table()
        {
            int i, j;
            UInt32 crc_accum;
    
            for (i = 0; i < 256; i++) 
            {
                crc_accum = ((UInt32)i << 24);
        
                for (j = 0; j < 8; j++) {
                    if ((crc_accum & 0x80000000) != 0)
                        crc_accum = (crc_accum << 1) ^ POLYNOMIAL;
                    else
                        crc_accum = (crc_accum << 1);
                    crc_table[i] = crc_accum;
                }
            }
        }

        /* update the CRC on the data block one byte at a time */
        UInt32 update_crc(UInt32 crc_accum, byte[] data_blk_ptr, UInt32 data_blk_size)
        {
            int i, j;
    
            for (j = 0; j < data_blk_size; j++) 
            {
                i = ((int)(crc_accum >> 24) ^ data_blk_ptr[j]) & 0xff;
                crc_accum = (crc_accum << 8) ^ crc_table[i];
            }
            return crc_accum;
        }

        byte[] getBytes(TImageHeaderStructure header)
        {
            int size = Marshal.SizeOf(header);
            byte[] arr = new byte[size];

            IntPtr ptr = Marshal.AllocHGlobal(size);
            Marshal.StructureToPtr(header, ptr, true);
            Marshal.Copy(ptr, arr, 0, size);
            Marshal.FreeHGlobal(ptr);
            return arr;
        }

        bool MakeFwImageFile(byte[] fData, string AppPath, string FileName)
        {
            FileStream aFile = null;
            string path = System.IO.Path.GetDirectoryName(AppPath);
            string aFileName = String.Format("{0}\\{1}", path, FileName);

            bool result = false;

            if (File.Exists(aFileName) == false)
            {
                try
                {
                    aFile = new FileStream(aFileName, FileMode.CreateNew, FileAccess.ReadWrite);
                    aFile.Write(fData, 0, fData.Length);
                    aFile.Close();

                    result = true;
                }
                catch (Exception)
                {
                }
            }
            return result;
        }

        #region BUNTTON EVENT

        private void btnFileFind_Click(object sender, EventArgs e)
        {
            OpenFileDialog oDialog;

            oDialog = new OpenFileDialog();
            oDialog.DefaultExt = "*.*";
            oDialog.Filter = "bin files (*.bin)|*.bin|All files (*.*)|*.*";

            if (oDialog.ShowDialog() == DialogResult.OK)
            {
                teFileName.Text = oDialog.FileName;

                FileStream fwFile = new FileStream(teFileName.Text, FileMode.Open, FileAccess.Read);
                long fsize = fwFile.Length;

                if (fsize > 0)
                {
                    fwdata = new byte[fsize];
                    fwFile.Seek(0, SeekOrigin.Begin);
                    fwFile.Read(fwdata, 0, (int)fsize);
                }
                fwFile.Close();

                byte[] bName = csUtils.StringToByte(Path.GetFileName(teFileName.Text));

                fwHeader = new TImageHeaderStructure();
                fwHeader.ImageCode = ImageCode;
                fwHeader.ImageType = 0x00000001;
                fwHeader.ImageSize = (UInt32)fsize;
                fwHeader.ImageName = new byte[32];
                Buffer.BlockCopy(bName, 0, fwHeader.ImageName, 0, bName.Length);
                fwHeader.ImageCRC = update_crc(fwHeader.ImageCRC, fwdata, fwHeader.ImageSize);
                fwHeader.reserved = new byte[12];

                if (bName.Length > 8)
                {
                    fwHeader.ImageVersion = new byte[4];

                    UInt32 nPC = (UInt32)((fwdata[0] << 0) | (fwdata[1] << 8) | (fwdata[2] << 16) | (fwdata[3] << 24));
                    UInt32 nSP = (UInt32)((fwdata[4] << 0) | (fwdata[5] << 8) | (fwdata[6] << 16) | (fwdata[7] << 24));

                    for (int i = 0; i < 4; i++)
                        fwHeader.ImageVersion[i] = (byte)(bName[bName.Length - 1 - 3 - 4 + i] - 0x30);

                    lbFileInfo.Text = String.Format("File Information");
                    lbFileInfo.Text += String.Format("\r\n  - Filename: {0}", Encoding.UTF8.GetString(fwHeader.ImageName).Trim('\0'));
                    lbFileInfo.Text += String.Format("\r\n  - File Version: V{0}.{1}.{2}.{3}", fwHeader.ImageVersion[0], fwHeader.ImageVersion[1], fwHeader.ImageVersion[2], fwHeader.ImageVersion[3]);
                    lbFileInfo.Text += String.Format("\r\n  - File Size: {0:#,##0} bytes", fwHeader.ImageSize);
                    lbFileInfo.Text += String.Format("\r\n  - File CRC32: 0x{0:X8}", fwHeader.ImageCRC);
                    lbFileInfo.Text += String.Format("\r\n  - Packet Size: {0:#,##0} packets", fwHeader.ImageSize / 2048);
                    lbFileInfo.Text += String.Format("\r\n  - Last packet: {0:#,##0}", fwHeader.ImageSize % 2048);

                    lbFileInfo.Text += String.Format("\r\n  - PC: 0x{0:X8}", nPC);
                    lbFileInfo.Text += String.Format("\r\n  - SP: 0x{0:X8}", nSP);
                }
            }
        }

        private void btnConvert_Click(object sender, EventArgs e)
        {
            if (fwHeader.ImageSize > 0)
            {
                byte[] bHeader = getBytes(fwHeader);
                byte[] cFwData = new byte[bHeader.Length + fwdata.Length];

                Buffer.BlockCopy(bHeader, 0, cFwData, 0, 64);
                Buffer.BlockCopy(fwdata, 0, cFwData, 64, fwdata.Length);

                string nFileName = String.Format("mBMS_APP_DL_V{0}{1}{2}{3}.bin"
                                                , fwHeader.ImageVersion[0]
                                                , fwHeader.ImageVersion[1]
                                                , fwHeader.ImageVersion[2]
                                                , fwHeader.ImageVersion[3]
                                                );

                if (MakeFwImageFile(cFwData, teFileName.Text, nFileName))
                {
                    MessageBox.Show("Firmware Image Convert Success !!", "FwUpdate", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else
                {
                    MessageBox.Show("Firmware Image Convert Fail !!", "FwUpdate", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else
            {
                MessageBox.Show("Please select firmware file !!", "FwUpdate", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        #endregion

        private void btnClose_Click(object sender, EventArgs e)
        {
            Close();
        }
    }
}