using System;
using System.Runtime.InteropServices;
using System.Text;

class PynectDirectDemo
{
    // ---------- 数据结构（与 pynect_direct.h 对应） ----------
    [StructLayout(LayoutKind.Sequential)]
    struct PynectDirectDeviceInfo
    {
        public int protocol;
        public uint capabilities;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string protocol_name;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string model;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string serial_number;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
        public string hardware_version;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct PynectDirectSelectedWavelengthRange
    {
        public double request_start_nm;
        public double request_end_nm;
        public double actual_start_nm;
        public double actual_end_nm;
        public ushort start_pixel;
        public ushort end_pixel;
        public ushort count;
    }

    // ---------- DllImport（全部为 cdecl 调用约定） ----------
    const string DLL = "PynectDirect.dll";
    const int SUCCESS = 0;

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern int pynect_direct_scan_devices();

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern int pynect_direct_open_device(int index);

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern void pynect_direct_close_device();

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern int pynect_direct_get_device_info(ref PynectDirectDeviceInfo info);

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern int pynect_direct_get_wavelength_count(ref ushort count);

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern int pynect_direct_get_full_wavelength_range(ref double start, ref double end);

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern int pynect_direct_get_full_wavelength_data(double[] buffer, int max_len);

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern int pynect_direct_get_full_spectrum_data(ushort[] buffer, int max_len);

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern int pynect_direct_set_integration_time(uint us);

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern int pynect_direct_get_integration_time(ref uint us);

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern int pynect_direct_set_selected_wavelength_range(double start, double end);

    [DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
    static extern int pynect_direct_get_selected_wavelength_range(
        ref PynectDirectSelectedWavelengthRange range);

    static void Require(int code, string what)
    {
        if (code != SUCCESS)
            throw new InvalidOperationException(string.Format(
                "{0} failed, code={1}", what, code));
    }

    static void WaitExit()
    {
        Console.Write("按任意键退出...");
        try { Console.ReadKey(); }
        catch { /* 非交互环境（输入被重定向）时直接退出 */ }
    }

    static void Main()
    {
        try
        {
            int n = pynect_direct_scan_devices();
            if (n <= 0) { Console.WriteLine("No device found."); WaitExit(); return; }
            Console.WriteLine(string.Format("Found {0} device(s).", n));

            Require(pynect_direct_open_device(0), "open_device");

            var info = new PynectDirectDeviceInfo();
            Require(pynect_direct_get_device_info(ref info), "get_device_info");
            Console.WriteLine(string.Format(
                "Model: {0}, Serial: {1}, HW: {2}",
                info.model, info.serial_number, info.hardware_version));

            ushort count = 0;
            Require(pynect_direct_get_wavelength_count(ref count), "get_wavelength_count");
            Console.WriteLine(string.Format("Wavelength count: {0}", count));

            double start = 0, end = 0;
            Require(pynect_direct_get_full_wavelength_range(ref start, ref end),
                    "get_full_wavelength_range");
            Console.WriteLine(string.Format(
                "Wavelength range: {0:F2} ~ {1:F2} nm", start, end));

            var wavelengths = new double[count];
            Require(pynect_direct_get_full_wavelength_data(wavelengths, count),
                    "get_full_wavelength_data");

            uint integration = 0;
            Require(pynect_direct_get_integration_time(ref integration), "get_integration_time");
            Console.WriteLine(string.Format("Current integration time: {0} us", integration));
            pynect_direct_set_integration_time(50000);

            var spectrum = new ushort[count];
            Require(pynect_direct_get_full_spectrum_data(spectrum, count),
                    "get_full_spectrum_data");

            Console.WriteLine(string.Format(
                "{0,-6} {1,13} {2,9}", "Index", "Wavelength(nm)", "Intensity"));
            Console.WriteLine(string.Format(
                "{0,-6} {1,13} {2,9}", "-----", "--------------", "---------"));
            for (int i = 0; i < 5 && i < count; i++)
                Console.WriteLine(string.Format(
                    "{0,-6} {1,13:F3} {2,9}", i, wavelengths[i], spectrum[i]));
            if (count > 10)
                Console.WriteLine(string.Format("...  (total {0} points)", count));
            for (int i = (count > 5 ? count - 5 : 0); i < count; i++)
                Console.WriteLine(string.Format(
                    "{0,-6} {1,13:F3} {2,9}", i, wavelengths[i], spectrum[i]));

            if (start < 400.0 && end > 800.0)
            {
                Require(pynect_direct_set_selected_wavelength_range(400.0, 800.0),
                        "set_selected_range");
                var sel = new PynectDirectSelectedWavelengthRange();
                Require(pynect_direct_get_selected_wavelength_range(ref sel),
                        "get_selected_range");
                Console.WriteLine(string.Format(
                    "Selected: pixels {0}..{1}, count={2}",
                    sel.start_pixel, sel.end_pixel, sel.count));
            }

            pynect_direct_close_device();
            Console.WriteLine("Done.");
            WaitExit();
        }
        catch (Exception e)
        {
            Console.WriteLine(string.Format("Error: {0}", e.Message));
            WaitExit();
        }
    }
}
