using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using MTK;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WinISP.BaseClass
{

    public class GraphicCardI2CCtrl : ISimI2CCtrl
    {
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        delegate string PCallBack(string strParam);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern void SetCallBack([MarshalAs(UnmanagedType.FunctionPtr)] PCallBack pCallBack);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        static public extern IntPtr CreateGraphicCardCtrl();
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        static public extern IntPtr CreateGraphicCardCtrl(int GraphicCardType);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        static public extern void DisposeGraphicCardCtrl();
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.I1)]
        static public extern bool InitGraphicCardCtrl(IntPtr pGraphicCardCtrlObj);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        static private extern int DllGetGraphicCardType(IntPtr pGraphicCardCtrlObj);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        static public extern int GetPortType(IntPtr pGraphicCardCtrlObj);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        static public extern int GetPortType(IntPtr pGraphicCardCtrlObj, byte idx);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.I1)]
        static public extern bool DllGetMonitorName(IntPtr pGraphicCardCtrlObj, byte[] name);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.I1)]
        static public extern bool DDCRead(IntPtr pGraphicCardCtrlObj, byte slaveAddr, uint rdLen, byte[] rdBuf);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.I1)]
        static public extern bool DDCWrite(IntPtr pGraphicCardCtrlObj, byte slaveAddr, uint wrLen, byte[] wrBuf);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.I1)]
        static public extern byte InitConnectedMTKMonitor(IntPtr pGraphicCardCtrlObj);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.I1)]
        static public extern void SetConnectedMonitorIndex(IntPtr pGraphicCardCtrlObj, byte idx);
        [DllImport("GraphicsCardCtrl.dll", CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.I1)]
        static public extern void GetMonitorNameByIndex(IntPtr pGraphicCardCtrlObj, byte idx, byte[] pName);
        public enum GRAPHIC_CARD
        {
            INTEL = 0,
            AMD = 1,
            NVIDIA = 2,
            INTEL_IGCL = 3,
            NA = 4
        }
        public enum PortTypes
        {
            NA = 0,
            HDMI = 1,
            DISPALYPORT = 2,
            VGA = 3,
            DVI = 4
        }
        public GRAPHIC_CARD GraphicCardType;
        public PortTypes PortType;
        private static byte[] MonitorName = new byte[13];
        public enum GPUTypes { AUTO_Search, AMD, NVIDIA, INTEL };
        private GCHandle gch;
        private static bool _IsInit = false;
        private static IntPtr _hMod;
        private static IntPtr pGraphicCardCtrl;
        static string CallBackFunction(string strParam)
        {
            WinISP.frmISP.WriteLog("GraphicCard Msg = " + strParam, "debug");
            return strParam;
        }
        public void ReCreateGraphicCardCtrl()
        {
            FreeGraphicsCardCtrl();
            CreateGraphicsCardCtrl();
        }

        public void ReCreateGraphicCardCtrl(GPUTypes type)
        {
            FreeGraphicsCardCtrl();

            try
            {
                switch (type)
                {
                    case GPUTypes.AMD:
                        pGraphicCardCtrl = CreateGraphicCardCtrl((int)GPUTypes.AMD);
                        break;

                    case GPUTypes.NVIDIA:
                        pGraphicCardCtrl = CreateGraphicCardCtrl((int)GPUTypes.NVIDIA);
                        break;

                    case GPUTypes.INTEL:
                        pGraphicCardCtrl = CreateGraphicCardCtrl((int)GPUTypes.INTEL);
                        break;

                    default:
                        pGraphicCardCtrl = CreateGraphicCardCtrl();
                        break;
                }

                if (pGraphicCardCtrl != IntPtr.Zero)
                    _IsInit = true;
            }
            catch (Exception e)
            {
                frmISP.WriteLog("ReCreateGraphicCardCtrl(" + type.ToString() + ") Exception: " + e.Message, "debug");
            }
        }

        private void CreateGraphicsCardCtrl()
        {
            // Declare an instance of the CallBack delegate.
            PCallBack callback_delegate = new PCallBack(CallBackFunction);
            // Use GCHandle to hold the delegate object in memory.
            gch = GCHandle.Alloc(callback_delegate);
            // Invoke the managed callback from unmanaged code.
            try
            {
                SetCallBack(callback_delegate);
            }
            catch (Exception e)
            {
                frmISP.WriteLog("Exception: " + e.Message, "debug");
            }
            try
            {
                pGraphicCardCtrl = CreateGraphicCardCtrl();
                if (pGraphicCardCtrl != IntPtr.Zero)
                    _IsInit = true;
            }
            catch (Exception e)
            {
                frmISP.WriteLog("CreateGraphicCardCtrl Exception: " + e.Message, "debug");
            }
        }

        private void FreeGraphicsCardCtrl()
        {
            // Free the GCHandle.
            frmISP.WriteLog("Free the GCHandle.", "debug");
            if (gch.IsAllocated)
                gch.Free();
            DisposeGraphicCardCtrl();
            pGraphicCardCtrl = IntPtr.Zero;
            _IsInit = false;
        }

        public GraphicCardI2CCtrl()
        {
            if (_IsInit == true)
                return;
            CreateGraphicsCardCtrl();
            return;
        }
        ~GraphicCardI2CCtrl()
        {
            FreeGraphicsCardCtrl();
        }
        public GraphicCardI2CCtrl(GPUTypes type)
        {
            switch (type)
            {
                case GPUTypes.AUTO_Search:
                    pGraphicCardCtrl = CreateGraphicCardCtrl();
                    break;

                case GPUTypes.AMD:
                    pGraphicCardCtrl = CreateGraphicCardCtrl((int)GPUTypes.AMD);
                    break;

                case GPUTypes.NVIDIA:
                    pGraphicCardCtrl = CreateGraphicCardCtrl((int)GPUTypes.NVIDIA);
                    break;

                case GPUTypes.INTEL:
                    pGraphicCardCtrl = CreateGraphicCardCtrl((int)GPUTypes.INTEL);
                    break;
            }
        }

        public Levels SDA
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public Levels SCL
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public bool I2C_Start()
        {
            return true;
        }

        public void I2C_Stop()
        {
            return;
        }

        public byte I2C_RecvByte(bool IsAck)
        {
            throw new NotImplementedException();
        }

        public bool I2C_SendByte(byte data)
        {
            throw new NotImplementedException();
        }

        public bool WriteData(byte slvAddr, byte[] arr)
        {

            unsafe
            {
                fixed (Byte* pArr = arr)
                {
                    return DDCWrite(pGraphicCardCtrl, slvAddr, (uint)(arr.Length), arr);
                }
            }
        }

        public byte ReadData(byte slvAddr, byte[] arr)
        {
            throw new NotImplementedException();
        }

        public bool WriteData(byte[] data)
        {
            Byte slvAddr = data[0];
            Byte[] wrBuf = new Byte[data.Length - 1];
            Array.Copy(data, 1, wrBuf, 0, wrBuf.Length);

            bool ret = DDCWrite(pGraphicCardCtrl, slvAddr, (uint)wrBuf.Length, wrBuf);
            frmISP.WriteLog($"[DDCWrite] card={GraphicCardType} slv=0x{slvAddr:X2} len={wrBuf.Length} ret={ret}", ret ? "debug" : "error");
            return ret;
        }

        public bool ReadData(byte[] data, byte[] wrCmds = null)
        {
            if (wrCmds == null || wrCmds.Length == 0)
            {
                frmISP.WriteLog($"[DDCRead] card={GraphicCardType} wrCmds is NULL/empty!", "error");
                return false;
            }
            uint revlen = (uint)data.Length;
            bool ret = DDCRead(pGraphicCardCtrl, wrCmds[0], revlen, data);
            frmISP.WriteLog($"[DDCRead] card={GraphicCardType} slv=0x{wrCmds[0]:X2} rdLen={revlen} ret={ret} " +
                            $"first4={(data.Length >= 4 ? BitConverter.ToString(data, 0, 4) : "n/a")}", ret ? "debug" : "error");
            return ret;
        }

        public bool Init()
        {
            if (_IsInit == true)
                return true;
            if (pGraphicCardCtrl == IntPtr.Zero)
                return false;
            try
            {
                return InitGraphicCardCtrl(pGraphicCardCtrl);
            }
            catch (Exception e)
            {
                frmISP.WriteLog("Init() Exception: " + e.Message, "debug");
                return false;
            }
        }

        public GRAPHIC_CARD GetGraphicCardType
        {
            get
            {
                GraphicCardType = (GRAPHIC_CARD)DllGetGraphicCardType(pGraphicCardCtrl);
                return GraphicCardType;
            }
        }

        public PortTypes GetConnectedPortType
        {
            get
            {
                return PortType;
            }
        }

        public PortTypes GetConnectedPortTypeByIdx(Byte idx)
        {
            return (PortTypes)GetPortType(pGraphicCardCtrl, idx);
        }

        public String GetMonitorName
        {
            get
            {
                return ASCIIEncoding.ASCII.GetString(MonitorName);
            }
        }

        public List<String> InitConnectedMonitor
        {
            get
            {
                //if (Program.isManagedDll)
                {
                    List<String> list = new List<string>();
                    Byte[] buf = new Byte[13];
                    char[] charsToTrim = { ',', '/', ' ' };
                    String mntName;
                    Byte numOfMnt;
                    numOfMnt = InitConnectedMTKMonitor(pGraphicCardCtrl);
                    for (Byte idx = 0; idx < numOfMnt; idx++)
                    {
                        unsafe
                        {
                            fixed (byte* pBuf = buf)
                            {
                                GetMonitorNameByIndex(pGraphicCardCtrl, idx, buf);
                            }
                        }

                        mntName = ASCIIEncoding.ASCII.GetString(buf);
                        mntName.Replace("\n", "").Replace("\r", "").Replace("\0", "");
                        list.Add(mntName);
                    }
                    return list;
                }
                //else //TBD
                //    return null;
            }
        }

        public void SetConnectedMonitorIndex(Byte index)
        {
            SetConnectedMonitorIndex(pGraphicCardCtrl, index);
        }
    }
}
