using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.ServiceModel.Discovery;
using System.Text;
using System.Xml;

namespace YoseenPTZ.SDKDef
{
    public static class LibHK
    {
        #region hknet
        //
        [DllImport("LibHK.dll", EntryPoint = "hkStaticInit", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkStaticInit(string libDir);

        [DllImport("LibHK.dll", EntryPoint = "hkStaticFree", CallingConvention = CallingConvention.Cdecl)]
        public extern static void hkStaticFree();

        //
        [DllImport("LibHK.dll", EntryPoint = "hkCreate", CallingConvention = CallingConvention.Cdecl)]
        public extern static IntPtr hkCreate();

        [DllImport("LibHK.dll", EntryPoint = "hkFree", CallingConvention = CallingConvention.Cdecl)]
        public extern static void hkFree(ref IntPtr pp);

        //
        [DllImport("LibHK.dll", EntryPoint = "hkLogin", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkLogin(IntPtr context, string ip, string username, string password);

        [DllImport("LibHK.dll", EntryPoint = "hkLogout", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkLogout(IntPtr context);

        //
        [DllImport("LibHK.dll", EntryPoint = "hkPtzControl", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkPtzControl(IntPtr context, int act, int arg);

        [DllImport("LibHK.dll", EntryPoint = "hkPtzControl_Stop", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkPtzControl_Stop(IntPtr context, int act, int arg);

        [DllImport("LibHK.dll", EntryPoint = "hkPtzControl_GetPtz", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkPtzControl_GetPtz(IntPtr context, ref int pan, ref int tilt, ref int zoom, int timeout);

        [DllImport("LibHK.dll", EntryPoint = "hkPtzControl_SetPtz", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkPtzControl_SetPtz(IntPtr context, int pan, int tilt, int zoom);

        //
        [DllImport("LibHK.dll", EntryPoint = "hkGetDVRConfig", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkGetDVRConfig(IntPtr context, int cmd, int channel, IntPtr outBuffer, int outBufferSize, ref int bytesReturned);

        [DllImport("LibHK.dll", EntryPoint = "hkSetDVRConfig", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkSetDVRConfig(IntPtr context, int cmd, int channel, IntPtr inBuffer, int inBufferSize);

        [DllImport("LibHK.dll", EntryPoint = "hkSTDXMLConfig", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkSTDXMLConfig(IntPtr context, IntPtr lpInputParam, IntPtr lpOutputParam);

        //
        [DllImport("LibHK.dll", EntryPoint = "hkSerialStart", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkSerialStart(IntPtr context, HKSerialDataCallBack cb, IntPtr cbData);

        [DllImport("LibHK.dll", EntryPoint = "hkSerialStart", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkSerialStart2(IntPtr context, IntPtr cb, IntPtr cbData);

        [DllImport("LibHK.dll", EntryPoint = "hkSerialStop", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkSerialStop(IntPtr context);

        [DllImport("LibHK.dll", EntryPoint = "hkSerialSend", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkSerialSend(IntPtr context, IntPtr data, int dataSize);

        [DllImport("LibHK.dll", EntryPoint = "hkSerialSendWaitRecv", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkSerialSendWaitRecv(IntPtr context, IntPtr req, int reqSize, IntPtr resp, ref int respSize, int timeout);
        #endregion


        #region hkisapi
        [DllImport("LibHK.dll", EntryPoint = "hkisapiCreate", CallingConvention = CallingConvention.Cdecl)]
        public extern static IntPtr hkisapiCreate();

        [DllImport("LibHK.dll", EntryPoint = "hkisapiFree", CallingConvention = CallingConvention.Cdecl)]
        public extern static void hkisapiFree(ref IntPtr pp);

        [DllImport("LibHK.dll", EntryPoint = "hkisapiSetConfig", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkisapiSetConfig(IntPtr context, ref HKISAPIConfig config);

        [DllImport("LibHK.dll", EntryPoint = "hkisapiExecuteRaw", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkisapiExecuteRaw(IntPtr context, ref HKISAPIRaw raw);

        [DllImport("LibHK.dll", EntryPoint = "hkisapiExecuteRaw2", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkisapiExecuteRaw2(IntPtr context, ref HKISAPIRaw2 raw2);

        [DllImport("LibHK.dll", EntryPoint = "hkisapiExecuteBin", CallingConvention = CallingConvention.Cdecl)]
        public extern static int hkisapiExecuteBin(IntPtr context, IntPtr bin);
        #endregion

        //
        public static string[] DiscoverDevices()
        {
            FindCriteria findCriteria = new FindCriteria();
            string contractTypeName = "NetworkVideoTransmitter";
            string contractTypeNamespace = "http://www.onvif.org/ver10/network/wsdl";
            findCriteria.ContractTypeNames.Add(new XmlQualifiedName(contractTypeName, contractTypeNamespace));
            findCriteria.MaxResults = 16;
            findCriteria.Duration = TimeSpan.FromSeconds(2);
            UdpDiscoveryEndpoint endpoint = new UdpDiscoveryEndpoint(DiscoveryVersion.WSDiscoveryApril2005);
            DiscoveryClient discoveryClient = new DiscoveryClient(endpoint);
            FindResponse findResponse = discoveryClient.Find(findCriteria);

            HashSet<string> ipHashSet = new HashSet<string>();
            foreach (var a in findResponse.Endpoints)
            {
                foreach (var b in a.ListenUris)
                {
                    if (b.HostNameType == UriHostNameType.IPv4)
                    {
                        ipHashSet.Add(b.Host);
                    }
                }
            }
            return ipHashSet.ToArray();
        }
    }

    public delegate void HKSerialDataCallBack(IntPtr data, int dataSize, IntPtr userData);

    public class HKNETDevice : IDisposable
    {
        IntPtr _ptr;

        public HKNETDevice()
        {
            _ptr = LibHK.hkCreate();
            if (IntPtr.Zero == _ptr) throw new Exception("hkCreate, error");
        }

        public void Dispose()
        {
            if (IntPtr.Zero == _ptr) return;
            LibHK.hkFree(ref _ptr);
        }

        public int Login(string ip, string username, string password)
        {
            int ret;
            ret = LibHK.hkLogin(_ptr, ip, username, password);
            return ret;
        }

        public int Logout()
        {
            int ret;
            ret = LibHK.hkLogout(_ptr);
            return ret;
        }

        public int PtzControl(int act, int arg)
        {
            int ret;
            ret = LibHK.hkPtzControl(_ptr, act, arg);
            return ret;
        }

        public int PtzControl_Stop(int act, int arg)
        {
            int ret;
            ret = LibHK.hkPtzControl_Stop(_ptr, act, arg);
            return ret;
        }

        public int PtzControl_GetPtz(ref int p, ref int t, ref int z, int timeoutms)
        {
            int ret;
            ret = LibHK.hkPtzControl_GetPtz(_ptr, ref p, ref t, ref z, timeoutms);
            return ret;
        }

        public int PtzControl_SetPtz(int p, int t, int z, int speed)
        {
            int ret;
            ret = LibHK.hkPtzControl_SetPtz(_ptr, p, t, z);
            return ret;
        }

        public int EnableXPTZ(bool enable)
        {
            int ret;
            if (enable)
            {
                ret = LibHK.hkSerialStart2(_ptr, IntPtr.Zero, IntPtr.Zero);
            }
            else
            {
                ret = LibHK.hkSerialStop(_ptr);
            }
            return ret;
        }
    }

    public class HKISAPIDevice : IDisposable
    {
        IntPtr _ptr;

        readonly HKISAPIBin _bin;
        public void Dispose()
        {
            if (IntPtr.Zero == _ptr) return;
            LibHK.hkisapiFree(ref _ptr);
            _bin.Dispose();
        }

        public HKISAPIDevice()
        {
            _ptr = LibHK.hkisapiCreate();
            if (IntPtr.Zero == _ptr) throw new Exception("hkisapiCreate, error");
            _bin = new HKISAPIBin();
        }

        public int SetConfig(ref HKISAPIConfig config)
        {
            int ret;
            ret = LibHK.hkisapiSetConfig(_ptr, ref config);
            return ret;
        }

        //--------------------------------------------------------------------------------------------------
        public int Get_ZoomFocus(ref HKISAPI_ZoomFocus x)
        {
            int ret;
            _bin.WriteReq_ZoomFocus(0, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            if (0 == ret)
            {
                _bin.ReadResp<HKISAPI_ZoomFocus>(ref x);
            }
            return ret;
        }
        public int Set_ZoomFocus(ref HKISAPI_ZoomFocus x)
        {
            int ret;
            _bin.WriteReq_ZoomFocus(1, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            return ret;
        }

        //--------------------------------------------------------------------------------------------------
        public int Get_Color(ref HKISAPI_Color x)
        {
            int ret;
            _bin.WriteReq_Color(0, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            if (0 == ret)
            {
                _bin.ReadResp<HKISAPI_Color>(ref x);
            }
            return ret;
        }
        public int Set_Color(ref HKISAPI_Color x)
        {
            int ret;
            _bin.WriteReq_Color(1, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            return ret;
        }

        //--------------------------------------------------------------------------------------------------
        public int Get_Exposure(ref HKISAPI_Exposure x)
        {
            int ret;
            _bin.WriteReq_Exposure(0, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            if (0 == ret)
            {
                _bin.ReadResp<HKISAPI_Exposure>(ref x);
            }
            return ret;
        }
        public int Set_Exposure(ref HKISAPI_Exposure x)
        {
            int ret;
            _bin.WriteReq_Exposure(1, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            return ret;
        }

        //--------------------------------------------------------------------------------------------------
        public int Get_Iris(ref HKISAPI_Iris x)
        {
            int ret;
            _bin.WriteReq_Iris(0, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            if (0 == ret)
            {
                _bin.ReadResp<HKISAPI_Iris>(ref x);
            }
            return ret;
        }
        public int Set_Iris(ref HKISAPI_Iris x)
        {
            int ret;
            _bin.WriteReq_Iris(1, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            return ret;
        }

        //--------------------------------------------------------------------------------------------------
        public int Get_FocusConfiguration(ref HKISAPI_FocusConfiguration x)
        {
            int ret;
            _bin.WriteReq_FocusConfiguration(0, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            if (0 == ret)
            {
                _bin.ReadResp<HKISAPI_FocusConfiguration>(ref x);
            }
            return ret;
        }
        public int Set_FocusConfiguration(ref HKISAPI_FocusConfiguration x)
        {
            int ret;
            _bin.WriteReq_FocusConfiguration(1, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            return ret;
        }


        //--------------------------------------------------------------------------------------------------
        public int Get_SupplementLight(ref HKISAPI_SupplementLight x)
        {
            int ret;
            _bin.WriteReq_SupplementLight(0, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            if (0 == ret)
            {
                _bin.ReadResp<HKISAPI_SupplementLight>(ref x);
            }
            return ret;
        }
        public int Set_SupplementLight(ref HKISAPI_SupplementLight x)
        {
            int ret;
            _bin.WriteReq_SupplementLight(1, ref x);
            ret = LibHK.hkisapiExecuteBin(_ptr, _bin.Ptr);
            return ret;
        }
    }

    #region hkisapi

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct HKISAPIConfig
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string url;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string username;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string password;

        public ushort timeout;
        ushort pad;
    };

    //
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct HKISAPIRaw
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string isapi;//url: /ISAPI/*
        public int mode;//0 get, 1 put

        //
        public int reqSize;
        public int respSizeMax;
        public int respSizeReturn;


        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
        public string req;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
        public string resp;
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct HKISAPIRaw2
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string isapi;//url: /ISAPI/*
        public int mode;//0 get, 1 put

        //
        public int reqSize;
        public int respSizeMax;
        public int respSizeReturn;
        public IntPtr req;
        public IntPtr resp;
    };

    //
    public class HKISAPIBinType
    {
        public const int HKISAPIBinType_ZoomFocus = 0;
        public const int HKISAPIBinType_Color = 1;
        public const int HKISAPIBinType_Exposure = 2;
        public const int HKISAPIBinType_Iris = 3;
        public const int HKISAPIBinType_FocusConfiguration = 4;
        public const int HKISAPIBinType_SupplementLight = 5;
        public const int HKISAPIBinType_XXXEnd = 6;
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct HKISAPI_ResponseStatus
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string requestURL;
        public int statusCode;//0,1 ok; x bad

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
        public string statusString;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
        public string subStatusCode;
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct HKISAPI_ZoomFocus
    {
        public int pqrsZoom;
        public int mnstFocus;
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct HKISAPI_Color
    {
        public int brightnessLevel;//0,100
        public int contrastLevel;//0,100
        public int saturationLevel;//0,100
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct HKISAPI_Exposure
    {
        public static readonly string[] All_ExposureType = new string[] { "manual", "auto", "IrisFirst", "ShutterFirst" };

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
        public string ExposureType;//manual,auto,IrisFirst,ShutterFirst
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct HKISAPI_Iris
    {
        public static readonly int[] All_IrisLevel = new int[] { 160, 200, 240, 280, 340, 400, 480, 560, 680, 960, 1100, 1400, 1600, 1900, 2200 };

        public int IrisLevel;//160,200,240,280,340,400,480,560,680,960,1100,1400,1600,1900,2200
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct HKISAPI_FocusConfiguration
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string focusStyle;//AUTO,MANUAL,SEMIAUTOMATIC

        public int focusLimited;//10,30,100,150,300,600,1000,2000,65535
    };

    //
    public enum ESupplementLightMode
    {
        close,
        colorVuWhiteLight,
    }

    public enum EMixedLightBrightnessRegulatMode
    {
        auto,
        manual,
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct HKISAPI_SupplementLight
    {
        public static readonly string[] All_SupplementLightMode = new string[] { "close", "colorVuWhiteLight" };
        public static readonly string[] All_MixedLightBrightnessRegulatMode = new string[] { "auto", "manual" };

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string supplementLightMode;//close, colorVuWhiteLight

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string mixedLightBrightnessRegulatMode;//manual, auto

        public int whiteLightBrightness;//[0,100]

        public int irLightBrightness;//[0,100]


        public void enum_get(ref ESupplementLightMode e)
        {
            string[] ss = All_SupplementLightMode;
            int i;
            for (i = 0; i < ss.Length; i++)
            {
                if (ss[i] == supplementLightMode)
                {
                    e = (ESupplementLightMode)i;
                    break;
                }
            }
        }

        public bool enum_set(ESupplementLightMode e)
        {
            int i = (int)e;
            string s = All_SupplementLightMode[i];
            bool b = s != supplementLightMode;
            if (b)
            {
                supplementLightMode = s;
            }
            return b;
        }

        public void enum_get(ref EMixedLightBrightnessRegulatMode e)
        {
            string[] ss = All_MixedLightBrightnessRegulatMode;
            int i;
            for (i = 0; i < ss.Length; i++)
            {
                if (ss[i] == mixedLightBrightnessRegulatMode)
                {
                    e = (EMixedLightBrightnessRegulatMode)i;
                    break;
                }
            }
        }

        public bool enum_set(EMixedLightBrightnessRegulatMode e)
        {
            int i = (int)e;
            string s = All_MixedLightBrightnessRegulatMode[i];
            bool b = s != mixedLightBrightnessRegulatMode;
            if (b)
            {
                mixedLightBrightnessRegulatMode = s;
            }
            return b;
        }
    };


    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct HKISAPIBinHead
    {
        public int type;//url: type2url
        public int mode;//0 get, 1 put
        public int reqSize;
        public int respSizeReturn;

        //1024, req


        //1024, resp
    }

    public class HKISAPIBin : IDisposable
    {
        HKISAPIBinHead _head;
        IntPtr _ptr;
        public IntPtr Ptr => _ptr;

        public HKISAPIBin()
        {
            _ptr = Win32Helper.calloc(1, 1024 * 1024);
            if (IntPtr.Zero == _ptr) throw new Exception("calloc, error");
        }
        public void Dispose()
        {
            if (IntPtr.Zero == _ptr) return;
            Win32Helper.free(_ptr); _ptr = IntPtr.Zero;
        }

        public void ReadResp<T>(ref T x)
        {
            x = Marshal.PtrToStructure<T>(_ptr + 16 + 1024);
        }

        public void WriteReq_ZoomFocus(int mode, ref HKISAPI_ZoomFocus x)
        {
            _head.type = HKISAPIBinType.HKISAPIBinType_ZoomFocus;
            _head.mode = mode;
            _head.reqSize = 0;
            _head.respSizeReturn = 0;
            Marshal.StructureToPtr<HKISAPIBinHead>(_head, _ptr, false);
            if (0 != mode)
            {
                Marshal.StructureToPtr<HKISAPI_ZoomFocus>(x, _ptr + 16, false);
            }
        }

        public void WriteReq_Color(int mode, ref HKISAPI_Color x)
        {
            _head.type = HKISAPIBinType.HKISAPIBinType_Color;
            _head.mode = mode;
            _head.reqSize = 0;
            _head.respSizeReturn = 0;
            Marshal.StructureToPtr<HKISAPIBinHead>(_head, _ptr, false);
            if (0 != mode)
            {
                Marshal.StructureToPtr<HKISAPI_Color>(x, _ptr + 16, false);
            }
        }

        public void WriteReq_Exposure(int mode, ref HKISAPI_Exposure x)
        {
            _head.type = HKISAPIBinType.HKISAPIBinType_Exposure;
            _head.mode = mode;
            _head.reqSize = 0;
            _head.respSizeReturn = 0;
            Marshal.StructureToPtr<HKISAPIBinHead>(_head, _ptr, false);
            if (0 != mode)
            {
                Marshal.StructureToPtr<HKISAPI_Exposure>(x, _ptr + 16, false);
            }
        }

        public void WriteReq_Iris(int mode, ref HKISAPI_Iris x)
        {
            _head.type = HKISAPIBinType.HKISAPIBinType_Iris;
            _head.mode = mode;
            _head.reqSize = 0;
            _head.respSizeReturn = 0;
            Marshal.StructureToPtr<HKISAPIBinHead>(_head, _ptr, false);
            if (0 != mode)
            {
                Marshal.StructureToPtr<HKISAPI_Iris>(x, _ptr + 16, false);
            }
        }

        public void WriteReq_FocusConfiguration(int mode, ref HKISAPI_FocusConfiguration x)
        {
            _head.type = HKISAPIBinType.HKISAPIBinType_FocusConfiguration;
            _head.mode = mode;
            _head.reqSize = 0;
            _head.respSizeReturn = 0;
            Marshal.StructureToPtr<HKISAPIBinHead>(_head, _ptr, false);
            if (0 != mode)
            {
                Marshal.StructureToPtr<HKISAPI_FocusConfiguration>(x, _ptr + 16, false);
            }
        }

        public void WriteReq_SupplementLight(int mode, ref HKISAPI_SupplementLight x)
        {
            _head.type = HKISAPIBinType.HKISAPIBinType_SupplementLight;
            _head.mode = mode;
            _head.reqSize = 0;
            _head.respSizeReturn = 0;
            Marshal.StructureToPtr<HKISAPIBinHead>(_head, _ptr, false);
            if (0 != mode)
            {
                Marshal.StructureToPtr<HKISAPI_SupplementLight>(x, _ptr + 16, false);
            }
        }
    };
    #endregion
}
