using System;
using System.Runtime.InteropServices;

namespace CS_TEST
{
    internal enum HwsResult { OK = 0, Fail = -1 }
    internal enum HwsPixelFormat { Unknown = 0, YUY2 = 1, NV12 = 2, RGB24 = 3, RGB32 = 4, MJPG = 5, H264 = 6 }
    internal enum HwsRenderInterpolationMode { Linear = 0, Point = 1, Auto = 2 }
    internal enum HwsSdkPipelineMode { CPU = 0, GPU = 1 }
    internal enum HwsColorSpace { Auto = 0, BT601 = 1, BT709 = 2, BT2020 = 3 }
    internal enum HwsColorRange { Auto = 0, Full = 1, Limited = 2 }
    internal enum HwsOsdOutput { Bypass = 0, Osd = 1 }
    internal enum HwsOsdItemType { Text = 1, Image = 2 }
    internal enum HwsImageFormat { BMP = 1, JPG = 2 }
    internal enum HwsContainerFormat { MP4 = 1, MOV = 2 }
    internal enum HwsVideoCodec { H264 = 1 }
    internal enum HwsRecorderInputMode { Auto = 0, Reencode = 1, DirectH264 = 2 }

    [StructLayout(LayoutKind.Sequential)]
    internal struct HwsDeviceInfo
    {
        public int index;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public byte[] name;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] public byte[] id;

        public string Name => Utf8Z(name);
        public string Id => Utf8Z(id);

        private static string Utf8Z(byte[]? bytes)
        {
            if (bytes == null || bytes.Length == 0) return string.Empty;
            int n = Array.IndexOf(bytes, (byte)0);
            if (n < 0) n = bytes.Length;
            return n > 0 ? System.Text.Encoding.UTF8.GetString(bytes, 0, n) : string.Empty;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct HwsVideoFormat
    {
        public int width;
        public int height;
        public int fpsNum;
        public int fpsDen;
        public HwsPixelFormat pixelFormat;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct HwsVideoInputConfig
    {
        public int deviceIndex;
        public int width;
        public int height;
        public int fps;
        public HwsPixelFormat pixelFormat;
        public HwsColorSpace colorSpace;
        public HwsColorRange colorRange;
        public int bufferCount;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct HwsVideoRenderConfig
    {
        public int width;
        public int height;
        public HwsPixelFormat pixelFormat;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct HwsCropRect
    {
        public int x;
        public int y;
        public int width;
        public int height;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal struct HwsOsdItem
    {
        public int id;
        public int enable;
        public HwsOsdItemType type;
        public int zOrder;
        public int x;
        public int y;
        public int width;
        public int height;
        public float opacity;
        public uint color;
        public uint backgroundColor;
        public int transparentBackground;
        public int fontSize;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 512)] public string text;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string fontName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string filePath;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal struct HwsRecorderConfig
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string filePath;
        public HwsContainerFormat container;
        public HwsVideoCodec videoCodec;
        public int width;
        public int height;
        public int fps;
        public int recordFps;
        public int videoBitrate;
        public int enableAudio;
        public HwsRecorderInputMode inputMode;
        public int splitMode;
        public ulong maxSegmentBytes;
        public int segmentSeconds;
    }

    [StructLayout(LayoutKind.Sequential)] internal struct HwsVideoFrame { public IntPtr data0; public IntPtr data1; public IntPtr data2; public IntPtr data3; public int stride0; public int stride1; public int stride2; public int stride3; public int width; public int height; public HwsPixelFormat pixelFormat; public long timestamp100ns; public long frameIndex; }
    [StructLayout(LayoutKind.Sequential)] internal struct HwsEncodedVideoPacket { public IntPtr data; public int size; public int format; public long pts100ns; public long dts100ns; public long frameIndex; public int keyFrame; public int width; public int height; }
    [StructLayout(LayoutKind.Sequential)] internal struct HwsEncodedAudioPacket { public IntPtr data; public int size; public int format; public long pts100ns; public int sampleRate; public int channels; public int bitsPerSample; }

    [UnmanagedFunctionPointer(CallingConvention.StdCall)] internal delegate void HwsVideoCallback(IntPtr frame, IntPtr userData);
    [UnmanagedFunctionPointer(CallingConvention.StdCall)] internal delegate void HwsEncodedVideoCallback(IntPtr packet, IntPtr userData);
    [UnmanagedFunctionPointer(CallingConvention.StdCall)] internal delegate void HwsEncodedAudioCallback(IntPtr packet, IntPtr userData);

    internal static class Native
    {
        private const string Dll = "hwscapture.dll";

        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_Init();
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_Shutdown();
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetDefaultPipelineMode(HwsSdkPipelineMode mode);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern HwsSdkPipelineMode HWS_GetDefaultPipelineMode();

        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_RefreshDeviceList();
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_GetVideoDeviceCount();
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_GetVideoDeviceInfo(int index, out HwsDeviceInfo info);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_GetAudioInputDeviceCount();
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_GetAudioInputDeviceInfo(int index, out HwsDeviceInfo info);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_GetVideoCapabilityCount(int deviceIndex);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_GetVideoCapability(int deviceIndex, int capIndex, out HwsVideoFormat cap);

        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr HWS_CreateVideoInput();
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr HWS_CreateAudioInput();
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr HWS_CreateVideoRender(IntPtr hwnd);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr HWS_CreateAudioRender();
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr HWS_CreateAudioMux();
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr HWS_CreateVideoOsd();
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr HWS_CreateVideoCrop();
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr HWS_CreateRecorder([MarshalAs(UnmanagedType.LPStr)] string? unused);

        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetVideoInputConfig(IntPtr port, ref HwsVideoInputConfig config);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetVideoRenderConfig(IntPtr port, ref HwsVideoRenderConfig config);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetVideoRenderInterpolationMode(IntPtr port, HwsRenderInterpolationMode mode);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetVideoRenderPipelineMode(IntPtr port, HwsSdkPipelineMode mode);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern HwsSdkPipelineMode HWS_GetVideoRenderPipelineMode(IntPtr port);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetAudioInputDevice(IntPtr port, int deviceIndex);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetAudioInputPCM(IntPtr port, int sampleRate, int channels, int bitsPerSample);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetAudioRenderDefaultDevice(IntPtr port);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetVideoCropRect(IntPtr port, ref HwsCropRect rect);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_EnableVideoCrop(IntPtr port, int enable);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetRecorderConfig(IntPtr port, ref HwsRecorderConfig config);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetRecorderVideoCallback(IntPtr port, HwsEncodedVideoCallback? cb, IntPtr userData);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetRecorderAudioCallback(IntPtr port, HwsEncodedAudioCallback? cb, IntPtr userData);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SetVideoFrameCallback(IntPtr port, HwsVideoCallback? cb, IntPtr userData);

        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_AddVideoOsdText(IntPtr port, ref HwsOsdItem item);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_AddVideoOsdImage(IntPtr port, ref HwsOsdItem item);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_UpdateVideoOsdText(IntPtr port, int itemId, [MarshalAs(UnmanagedType.LPWStr)] string text);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_RemoveVideoOsdItem(IntPtr port, int itemId);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_ClearVideoOsd(IntPtr port);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_SaveVideoRenderSnapshot(IntPtr port, [MarshalAs(UnmanagedType.LPWStr)] string filePath, HwsImageFormat fmt);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_ShowVideoDevicePropertyPage(IntPtr parent, int deviceIndex);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_ShowVideoCrossbarPropertyPage(IntPtr parent, int deviceIndex);

        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_Connect(IntPtr src, IntPtr dst);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_ConnectVideoOsdOutput(IntPtr osd, HwsOsdOutput output, IntPtr dst);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_Disconnect(IntPtr src, IntPtr dst);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_Start(IntPtr port);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_Stop(IntPtr port);
        [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int HWS_DestroyPort(IntPtr port);
    }
}
