using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Runtime.InteropServices;
using FTD2XX_NET;

namespace A103_SDK
{
    class jClass_FT4222
    {
        //**************************************************************************
        //
        // FUNCTION IMPORTS FROM FTD2XX DLL
        //
        //**************************************************************************

        [DllImport("ftd2xx.dll")]
        static extern FTDI.FT_STATUS FT_CreateDeviceInfoList(ref UInt32 numdevs);

        [DllImport("ftd2xx.dll")]
        static extern FTDI.FT_STATUS FT_GetDeviceInfoDetail(UInt32 index, ref UInt32 flags, ref FTDI.FT_DEVICE chiptype, ref UInt32 id, ref UInt32 locid, byte[] serialnumber, byte[] description, ref IntPtr ftHandle);

        //[DllImportAttribute("ftd2xx.dll", CallingConvention = CallingConvention.Cdecl)]
        [DllImport("ftd2xx.dll")]
        static extern FTDI.FT_STATUS FT_OpenEx(uint pvArg1, int dwFlags, ref IntPtr ftHandle);

        [DllImport("ftd2xx.dll")]
        static extern FTDI.FT_STATUS FT_Open(int iDevice, ref IntPtr ftHandle);

        //[DllImportAttribute("ftd2xx.dll", CallingConvention = CallingConvention.Cdecl)]
        [DllImport("ftd2xx.dll")]
        static extern FTDI.FT_STATUS FT_Close(IntPtr ftHandle);


        //**************************************************************************
        //
        // Public FUNCTION IMPORTS FROM LIBFT4222 DLL
        //
        //**************************************************************************

        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_ChipReset(IntPtr ftHandle);

        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_SetClock(IntPtr ftHandle, FT4222_ClockRate clk);

        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_GetClock(IntPtr ftHandle, ref FT4222_ClockRate clk);

        //FT4222_STATUS FT4222_UnInitialize(FT_HANDLE ftHandle)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_UnInitialize(IntPtr ftHandle);

        //FT4222_STATUS FT4222_GetVersion(FT_HANDLE ftHandle, FT4222_Version* pVersion)
        public struct FT4222_Version
        {
            public UInt32 chipVersion;
            public UInt32 dllVersion;
        }
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_GetVersion(IntPtr ftHandle, ref FT4222_Version pVersion);

        //**************************************************************************
        //
        // FT4222 public define
        //
        //**************************************************************************
        // FT4222 Device status
        public enum FT4222_STATUS
        {
            FT4222_OK,
            FT4222_INVALID_HANDLE,
            FT4222_DEVICE_NOT_FOUND,
            FT4222_DEVICE_NOT_OPENED,
            FT4222_IO_ERROR,
            FT4222_INSUFFICIENT_RESOURCES,
            FT4222_INVALID_PARAMETER,
            FT4222_INVALID_BAUD_RATE,
            FT4222_DEVICE_NOT_OPENED_FOR_ERASE,
            FT4222_DEVICE_NOT_OPENED_FOR_WRITE,
            FT4222_FAILED_TO_WRITE_DEVICE,
            FT4222_EEPROM_READ_FAILED,
            FT4222_EEPROM_WRITE_FAILED,
            FT4222_EEPROM_ERASE_FAILED,
            FT4222_EEPROM_NOT_PRESENT,
            FT4222_EEPROM_NOT_PROGRAMMED,
            FT4222_INVALID_ARGS,
            FT4222_NOT_SUPPORTED,
            FT4222_OTHER_ERROR,
            FT4222_DEVICE_LIST_NOT_READY,

            FT4222_DEVICE_NOT_SUPPORTED = 1000,        // FT_STATUS extending message
            FT4222_CLK_NOT_SUPPORTED,     // spi master do not support 80MHz/CLK_2
            FT4222_VENDER_CMD_NOT_SUPPORTED,
            FT4222_IS_NOT_SPI_MODE,
            FT4222_IS_NOT_I2C_MODE,
            FT4222_IS_NOT_SPI_SINGLE_MODE,
            FT4222_IS_NOT_SPI_MULTI_MODE,
            FT4222_WRONG_I2C_ADDR,
            FT4222_INVAILD_FUNCTION,
            FT4222_INVALID_POINTER,
            FT4222_EXCEEDED_MAX_TRANSFER_SIZE,
            FT4222_FAILED_TO_READ_DEVICE,
            FT4222_I2C_NOT_SUPPORTED_IN_THIS_MODE,
            FT4222_GPIO_NOT_SUPPORTED_IN_THIS_MODE,
            FT4222_GPIO_EXCEEDED_MAX_PORTNUM,
            FT4222_GPIO_WRITE_NOT_SUPPORTED,
            FT4222_GPIO_PULLUP_INVALID_IN_INPUTMODE,
            FT4222_GPIO_PULLDOWN_INVALID_IN_INPUTMODE,
            FT4222_GPIO_OPENDRAIN_INVALID_IN_OUTPUTMODE,
            FT4222_INTERRUPT_NOT_SUPPORTED,
            FT4222_GPIO_INPUT_NOT_SUPPORTED,
            FT4222_EVENT_NOT_SUPPORTED,
        };

        public enum FT4222_ClockRate
        {
            SYS_CLK_60 = 0,
            SYS_CLK_24,
            SYS_CLK_48,
            SYS_CLK_80,
        };

        const byte FT_OPEN_BY_SERIAL_NUMBER = 1;
        const byte FT_OPEN_BY_DESCRIPTION = 2;
        const byte FT_OPEN_BY_LOCATION = 4;

        //**************************************************************************
        //
        // I2C Master IMPORTS
        //
        //**************************************************************************
        //FT4222_STATUS FT4222_I2CMaster_Init(FT_HANDLE ftHandle, uint32 kbps)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CMaster_Init(IntPtr ftHandle, UInt32 kbps);

        //FT4222_STATUS FT4222_I2CMaster_Read(FT_HANDLE ftHandle, uint16 slaveAddress, uint8* buffer, uint16 bytesToRead, uint16* sizeTransferred)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CMaster_Read(IntPtr ftHandle, UInt16 slaveAddress, ref byte buffer, UInt16 bytesToRead, ref UInt16 sizeTransferred);

        //FT4222_STATUS FT4222_I2CMaster_Write(FT_HANDLE ftHandle, uint16 slaveAddress, uint8* buffer, uint16 bytesToWrite, uint16* sizeTransferred)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CMaster_Write(IntPtr ftHandle, UInt16 slaveAddress, ref byte buffer, UInt16 bytesToWrite, ref UInt16 sizeTransferred);

        //FT4222_STATUS FT4222_I2CMaster_WriteEx(FT_HANDLE ftHandle, uint16 deviceAddress, uint8 flag, uint8* buffer, uint16 bytesToWrite, uint16* sizeTransferred)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CMaster_WriteEx(IntPtr ftHandle, UInt16 slaveAddress, byte flag, ref byte buffer, UInt16 bytesToWrite, ref UInt16 sizeTransferred);

        //FT4222_STATUS FT4222_I2CMaster_ReadEx(FT_HANDLE ftHandle, uint16 deviceAddress, uint8 flag, uint8* buffer, uint16 bytesToRead, uint16* sizeTransferred)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CMaster_ReadEx(IntPtr ftHandle, UInt16 slaveAddress, byte flag, ref byte buffer, UInt16 bytesToRead, ref UInt16 sizeTransferred);

        //FT4222_STATUS FT4222_I2CMaster_GetStatus(FT_HANDLE ftHandle, uint8 *controllerStatus)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CMaster_GetStatus(IntPtr ftHandle, ref byte controllerStatus);

        //FT4222_STATUS FT4222_I2CMaster_Reset(FT_HANDLE ftHandle)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CMaster_Reset(IntPtr ftHandle);


        //**************************************************************************
        //
        // I2C Slave IMPORTS 181201
        //
        //**************************************************************************

        //FT4222_STATUS FT4222_I2CSlave_Init(FT_HANDLE ftHandle)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CSlave_Init(IntPtr ftHandle);

        //FT4222_STATUS FT4222_I2CSlave_GetAddress(FT_HANDLE ftHandle, uint8* pAddr)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CSlave_GetAddress(IntPtr ftHandle, ref byte pAddr);

        //FT4222_STATUS FT4222_I2CSlave_SetAddress(FT_HANDLE ftHandle, uint8 addr)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CSlave_SetAddress(IntPtr ftHandle, byte addr);

        //FT4222_STATUS FT4222_I2CSlave_GetRxStatus(FT_HANDLE ftHandle, uint16* pRxSize)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CSlave_GetRxStatus(IntPtr ftHandle, ref UInt16 pRxSize);

        //FT4222_STATUS FT4222_I2CSlave_Read(FT_HANDLE ftHandle, uint8* buffer, uint16 bytesToRead, uint16* sizeTransferred)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CSlave_Read(IntPtr ftHandle, ref byte buffer, UInt16 bytesToRead, ref UInt16 sizeTransferred);

        //FT4222_STATUS FT4222_I2CSlave_Write(FT_HANDLE ftHandle, uint8* buffer, uint16 bytesToWrite, uint16* sizeTransferred)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CSlave_Write(IntPtr ftHandle, ref byte buffer, UInt16 bytesToWrite, ref UInt16 sizeTransferred);

        //FT4222_STATUS FT4222_I2CSlave_Reset(FT_HANDLE ftHandle)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CSlave_Reset(IntPtr ftHandle);

        //FT4222_STATUS FT4222_I2CSlave_SetClockStretch(FT_HANDLE ftHandle, BOOL enable)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CSlave_SetClockStretch(IntPtr ftHandle, bool enable);

        //FT4222_STATUS FT4222_I2CSlave_SetRespWord (FT_HANDLE ftHandle, uint8 responseWord)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_I2CSlave_SetRespWord(IntPtr ftHandle, byte responseWord);

        //**************************************************************************
        //
        // SPI Public Func IMPORTS
        //
        //**************************************************************************
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        //FT4222_STATUS FT4222_SPI_Reset (FT_HANDLE ftHandle)
        public static extern FT4222_STATUS FT4222_SPI_Reset(IntPtr ftHandle);

        //FT4222_STATUS FT4222_SPI_ResetTransaction(FT_HANDLE ftHandle, uint8 spiIdx)
        //Now is useless

        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_SPIMaster_Init(IntPtr ftHandle, FT4222_SPIMode ioLine, FT4222_SPIClock clock, FT4222_SPICPOL cpol, FT4222_SPICPHA cpha, Byte ssoMap);

        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_SPI_SetDrivingStrength(IntPtr ftHandle, SPI_DrivingStrength clkStrength, SPI_DrivingStrength ioStrength, SPI_DrivingStrength ssoStregth);



        //**************************************************************************
        //
        // SPI Master IMPORTS
        //
        //**************************************************************************
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_SPIMaster_SingleReadWrite(IntPtr ftHandle, ref byte readBuffer, ref byte writeBuffer, ushort bufferSize, ref ushort sizeTransferred, bool isEndTransaction);

        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_SPIMaster_SingleRead(IntPtr ftHandle, ref byte readBuffer, ushort bytesToRead, ref ushort sizeOfRead, bool isEndTransaction);

        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_SPIMaster_SingleWrite(IntPtr ftHandle, ref byte writeBuffer, ushort bytesToWrite, ref ushort sizeTransferred, bool isEndTransaction);

        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        //FT4222_STATUS FT4222_SPIMaster_MultiReadWrite(FT_HANDLE ftHandle, uint8* readBuffer, uint8* writeBuffer, uint8 singleWriteBytes,uint16 multiWriteBytes,uint16 multiReadBytes, uint32* sizeOfRead)
        public static extern FT4222_STATUS FT4222_SPIMaster_MultiReadWrite(IntPtr ftHandle, ref byte readBuffer, ref byte writeBuffer, byte singleWriteBytes, UInt16 multiWriteBytes, UInt16 multiReadBytes, ref UInt32 sizeOfRead);

        public enum FT4222_SPIMode
        {
            SPI_IO_NONE = 0,
            SPI_IO_SINGLE = 1,
            SPI_IO_DUAL = 2,
            SPI_IO_QUAD = 4,

        };

        public enum FT4222_SPIClock
        {
            CLK_NONE = 0,
            CLK_DIV_2,      // 1/2   System Clock
            CLK_DIV_4,      // 1/4   System Clock
            CLK_DIV_8,      // 1/8   System Clock
            CLK_DIV_16,     // 1/16  System Clock
            CLK_DIV_32,     // 1/32  System Clock
            CLK_DIV_64,     // 1/64  System Clock
            CLK_DIV_128,    // 1/128 System Clock
            CLK_DIV_256,    // 1/256 System Clock
            CLK_DIV_512,    // 1/512 System Clock

        };

        public enum FT4222_SPICPOL
        {
            CLK_IDLE_LOW = 0,
            CLK_IDLE_HIGH = 1,
        };

        public enum FT4222_SPICPHA
        {
            CLK_LEADING = 0,
            CLK_TRAILING = 1,
        };

        public enum SPI_DrivingStrength
        {
            DS_4MA = 0,
            DS_8MA,
            DS_12MA,
            DS_16MA,
        };

        //**************************************************************************
        //
        // SPI Slave Function IMPORTS
        //
        //**************************************************************************
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_SPISlave_Init(IntPtr ftHandle);

        //FT4222_STATUS FT4222_SPISlave_InitEx(FT_HANDLE ftHandle , SPI_SlaveProtocol protocolOpt)
        //[DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        //public static extern FT4222_SPISlave_InitEx(IntPtr ftHandle

        //**************************************************************************
        //
        // IO Function IMPORTS
        //
        //**************************************************************************

        //FT4222_STATUS FT4222_SetSuspendOut(FT_HANDLE ftHandle, BOOL enable)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_SetSuspendOut(IntPtr ftHandle, bool enable);

        //FT4222_STATUS FT4222_SetWakeUpInterrupt(FT_HANDLE ftHandle, BOOL enable)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_SetWakeUpInterrupt(IntPtr ftHandle, bool enable);

        //FT4222_STATUS FT4222_GPIO_Init(FT_HANDLE ftHandle, GPIO_Dir gpioDir[4])
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_GPIO_Init(IntPtr ftHandle, ref GPIO_Dir gpioDir);

        //FT4222_STATUS FT4222_GPIO_Read(FT_HANDLE ftHandle, GPIO_Port portNum, BOOL* pValue)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_GPIO_Read(IntPtr ftHandle, GPIO_Port portNum, ref  bool pValue);

        //FT4222_STATUS FT4222_GPIO_Write(FT_HANDLE ftHandle, GPIO_Port portNum, BOOL bValue)
        [DllImport("LibFT4222.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FT4222_STATUS FT4222_GPIO_Write(IntPtr ftHandle, GPIO_Port portNum, bool bValue);

        public enum GPIO_Dir
        {
            GPIO_OUTPUT = 0,
            GPIO_INPUT,
        };

        public enum GPIO_Port
        {
            GPIO_PORT0 = 0,
            GPIO_PORT1,
            GPIO_PORT2,
            GPIO_PORT3
        };

        //**************************************************************************
        //
        // Global var
        //
        //**************************************************************************
        static FTDI.FT_DEVICE_INFO_NODE devInfo = new FTDI.FT_DEVICE_INFO_NODE();
        public static IntPtr ftHandle = new IntPtr();  //for FT4222H_A
        public static IntPtr ftHandle_B = new IntPtr();//for FT4222H_B
        static FTDI.FT_STATUS ftStatus = 0;
        static FT4222_STATUS ft42Status = 0;


        public static bool DeviceOpen(string sDesc_In)
        {
            string sDesc = "FT4222 " + sDesc_In;

            UInt32 numOfDevices = 0;
            ftStatus = FT_CreateDeviceInfoList(ref numOfDevices);

            for (UInt32 iDev = 0; iDev < numOfDevices; iDev++)
            {
                byte[] sernum = new byte[16];
                byte[] desc = new byte[64];

                ftStatus = FT_GetDeviceInfoDetail(iDev, ref devInfo.Flags, ref devInfo.Type, ref devInfo.ID, ref devInfo.LocId, sernum, desc, ref devInfo.ftHandle);

                if (ftStatus == FTDI.FT_STATUS.FT_OK)
                {
                    devInfo.SerialNumber = Encoding.ASCII.GetString(sernum, 0, 16);
                    devInfo.Description = Encoding.ASCII.GetString(desc, 0, 64);
                    devInfo.SerialNumber = devInfo.SerialNumber.Substring(0, devInfo.SerialNumber.IndexOf("\0"));
                    devInfo.Description = devInfo.Description.Substring(0, devInfo.Description.IndexOf("\0"));

                    if (devInfo.Description == sDesc)
                    {
                        switch (sDesc_In)
                        {
                            case "A":
                                ftStatus = FT_Open((int)iDev, ref ftHandle);
                                break;

                            case "B":
                                ftStatus = FT_Open((int)iDev, ref ftHandle_B);
                                break;

                            default:
                                return (false);
                                break;
                        }
                        if (ftStatus == FTDI.FT_STATUS.FT_OK)
                            return (true);
                        else
                            return (false);
                    }
                }
            }//end for
            return (false);
        }//end func

        //sDesc_In= A B C or D
        public static bool DeviceClose(string sDesc_In)
        {
            try
            {
                switch (sDesc_In)
                {
                    case "A":
                        ft42Status = FT4222_UnInitialize(ftHandle);
                        ftStatus = FT_Close(ftHandle);
                        break;
                    case "B":
                        ft42Status = FT4222_UnInitialize(ftHandle_B);
                        ftStatus = FT_Close(ftHandle_B);
                        break;

                    default:
                        return (false);
                        break;
                }//end switch
            }
            catch
            {
                return (false);
            }

            if (ftStatus == FTDI.FT_STATUS.FT_OK)
                return (true);
            else
                return (false);
        }//end func    

        //user code
        public static bool DeviceCheck()
        {
            UInt32 numOfDevices = 0;
            ftStatus = FT_CreateDeviceInfoList(ref numOfDevices);

            if (numOfDevices > 0)
            {
                byte[] sernum = new byte[16];
                byte[] desc = new byte[64];

                ftStatus = FT_GetDeviceInfoDetail(0, ref devInfo.Flags, ref devInfo.Type, ref devInfo.ID, ref devInfo.LocId,
                                            sernum, desc, ref devInfo.ftHandle);

                devInfo.SerialNumber = Encoding.ASCII.GetString(sernum, 0, 16);
                devInfo.Description = Encoding.ASCII.GetString(desc, 0, 64);
                devInfo.SerialNumber = devInfo.SerialNumber.Substring(0, devInfo.SerialNumber.IndexOf("\0"));
                devInfo.Description = devInfo.Description.Substring(0, devInfo.Description.IndexOf("\0"));

                //Console.WriteLine("Device Number: {0}", numOfDevices);
                return (true);
            }
            else
            {
                return (false);
            }

        }//end func        

        public static bool SetClockRate(UInt32 iClockRate)
        {
            FT4222_ClockRate ft_ClockRate;
            switch (iClockRate)
            {
                case 24:
                    ft_ClockRate = FT4222_ClockRate.SYS_CLK_24;
                    break;

                case 48:
                    ft_ClockRate = FT4222_ClockRate.SYS_CLK_48;
                    break;

                case 60:
                    ft_ClockRate = FT4222_ClockRate.SYS_CLK_60;
                    break;

                case 80:
                    ft_ClockRate = FT4222_ClockRate.SYS_CLK_80;
                    break;

                default:
                    ft_ClockRate = FT4222_ClockRate.SYS_CLK_80;
                    break;
            }

            ft42Status = FT4222_SetClock(ftHandle, ft_ClockRate);
            if (ft42Status != FT4222_STATUS.FT4222_OK)
            {
                return (false);
            }
            else
            {
                return (true);
            }
        }//end func        


    }//end class
}
