using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Management;
using System.Text.RegularExpressions;
using System.Threading;
using libzkfpcsharp;
using Newtonsoft.Json.Linq;
using Sample;

namespace FingerprintScanner
{
    public class FingerprintCaptureManager
    {
        public delegate void FingerprintCapturedHandler(string jsonData);
        public event FingerprintCapturedHandler OnFingerprintCaptured;

        private IntPtr _deviceHandle = IntPtr.Zero;
        private byte[] _fpBuffer;
        private byte[] _templateBuffer = new byte[2048];
        private int _width = 0;
        private int _height = 0;
        private int _dpi = 0;

        private string _deviceId = "";
        private string _deviceStatus = "INACTIVE";

        private Thread _captureThread;
        private volatile bool _isCapturing = false;

        private const string TargetVid = "1B55";

        public void SetDeviceInfo(string deviceId, string deviceStatus)
        {
            _deviceId = deviceId;
            _deviceStatus = deviceStatus;
            Console.WriteLine($"[Info] Device info set: ID={_deviceId}, Status={_deviceStatus}");
        }

        /// <summary>
        /// Detect and return the serial number of the first connected USB device with VID=1B55
        /// </summary>
        public string DetectDeviceByVid()
        {
            Console.WriteLine($"[Info] Detecting USB devices with VID={TargetVid}...");
            var devices = GetConnectedUSBDevices();

            foreach (var dev in devices)
            {
                int lastIndex = dev.DeviceID.LastIndexOf('\\');
                string serialNumber = lastIndex >= 0 ? dev.DeviceID.Substring(lastIndex + 1) : "";

                string vid = GetMatch(dev.DeviceID, @"VID_(\w{4})");

                Console.WriteLine($"[Debug] Device found - VID: {vid}, Serial: {serialNumber}");

                if (vid.Equals(TargetVid, StringComparison.OrdinalIgnoreCase))
                {
                    Console.WriteLine($"[Info] Matching device detected: Serial={serialNumber}");
                    return serialNumber;
                }
            }
            Console.WriteLine("[Warning] No matching USB device with target VID found.");
            return "";
        }

        /// <summary>
        /// Start fingerprint capture on the detected device with VID=1B55
        /// </summary>
        public void Start()
        {
            if (_isCapturing)
            {
                Console.WriteLine("[Warning] Capture already running.");
                return;
            }

            string detectedSerial = DetectDeviceByVid();
            if (string.IsNullOrEmpty(detectedSerial))
            {
                Console.WriteLine("[Error] No target USB device detected, aborting capture start.");
                return;
            }

            Console.WriteLine("[Info] Initializing zkfp2 SDK...");
            int ret = zkfp2.Init();
            if (ret != zkfperrdef.ZKFP_ERR_OK)
            {
                Console.WriteLine($"[Error] zkfp2.Init failed with code: {ret}");
                return;
            }

            int deviceCount = zkfp2.GetDeviceCount();
            Console.WriteLine($"[Info] SDK reports {deviceCount} fingerprint device(s) connected.");

            if (deviceCount == 0)
            {
                Console.WriteLine("[Error] No fingerprint devices detected by SDK.");
                zkfp2.Terminate();
                return;
            }

            // Open device only at index 0 (matching old working code behavior)
            Console.WriteLine($"[Info] Trying to open device at index 0...");
            _deviceHandle = zkfp2.OpenDevice(0);
            if (_deviceHandle == IntPtr.Zero)
            {
                Console.WriteLine("[Error] Failed to open device at index 0.");
                zkfp2.Terminate();
                return;
            }
            Console.WriteLine("[Info] Opened device at index 0.");

            // Get device parameters
            byte[] paramValue = new byte[4];
            int size = 4;

            zkfp2.GetParameters(_deviceHandle, 1, paramValue, ref size);
            zkfp2.ByteArray2Int(paramValue, ref _width);
            Console.WriteLine($"[Info] Device width: {_width}");

            size = 4;
            zkfp2.GetParameters(_deviceHandle, 2, paramValue, ref size);
            zkfp2.ByteArray2Int(paramValue, ref _height);
            Console.WriteLine($"[Info] Device height: {_height}");

            size = 4;
            zkfp2.GetParameters(_deviceHandle, 3, paramValue, ref size);
            zkfp2.ByteArray2Int(paramValue, ref _dpi);
            Console.WriteLine($"[Info] Device DPI: {_dpi}");

            _fpBuffer = new byte[_width * _height];

            _deviceStatus = "ACTIVE";
            _deviceId = detectedSerial;

            _isCapturing = true;

            _captureThread = new Thread(CaptureLoop)
            {
                IsBackground = true
            };
            _captureThread.Start();

            Console.WriteLine($"[Info] Fingerprint capture started on device {_deviceId}");
        }



        public void Stop()
        {
            Console.WriteLine("[Info] Stopping fingerprint capture...");
            _isCapturing = false;
            _captureThread?.Join();

            if (_deviceHandle != IntPtr.Zero)
            {
                Console.WriteLine("[Info] Closing device handle...");
                zkfp2.CloseDevice(_deviceHandle);
                _deviceHandle = IntPtr.Zero;
            }

            Console.WriteLine("[Info] Terminating SDK...");
            zkfp2.Terminate();

            _deviceStatus = "INACTIVE";
            Console.WriteLine("[Info] Fingerprint capture stopped.");
        }

        private void CaptureLoop()
        {
            Console.WriteLine("[Info] Capture loop started.");

            while (_isCapturing)
            {
                int templateLength = 2048;
                int ret = zkfp2.AcquireFingerprint(_deviceHandle, _fpBuffer, _templateBuffer, ref templateLength);

                if (ret == zkfp.ZKFP_ERR_OK)
                {
                    Console.WriteLine("[Info] Fingerprint acquired.");

                    using (MemoryStream ms = new MemoryStream())
                    {
                        BitmapFormat.GetBitmap(_fpBuffer, _width, _height, ms);

                        byte[] bmpBytes = ms.ToArray();
                        string bmpBase64 = Convert.ToBase64String(bmpBytes);
                        string templateBase64 = Convert.ToBase64String(_templateBuffer, 0, templateLength);

                        var json = new JObject
                        {
                            ["deviceInfo"] = new JObject
                            {
                                ["deviceId"] = _deviceId,
                                ["deviceStatus"] = _deviceStatus,
                                ["width"] = _width,
                                ["height"] = _height,
                                ["dpi"] = _dpi
                            },
                            ["imageData"] = bmpBase64,
                            ["templateData"] = templateBase64
                        };

                        // Safe event invoke
                        OnFingerprintCaptured?.Invoke(json.ToString());
                    }
                }
                else if (ret == -7)
                {
                    // No finger detected, silently wait
                }
                else if (ret == -8)
                {
                    // Capture timeout or bad scan, silently wait
                }
                else
                {
                    Console.WriteLine($"[Warning] AcquireFingerprint returned unexpected code: {ret}");
                }

                Thread.Sleep(200); // match old working code
            }

            Console.WriteLine("[Info] Capture loop ended.");
        }



        public static USBDeviceInfo[] GetConnectedUSBDevices()
        {
            var usbDevices = new ManagementObjectSearcher("SELECT * FROM Win32_USBHub").Get();
            var list = new System.Collections.Generic.List<USBDeviceInfo>();
            foreach (var device in usbDevices)
            {
                string deviceId = device.GetPropertyValue("DeviceID")?.ToString() ?? "";
                string desc = device.GetPropertyValue("Description")?.ToString() ?? "";
                list.Add(new USBDeviceInfo { DeviceID = deviceId, Description = desc });
            }
            return list.ToArray();
        }

        public static string GetMatch(string input, string pattern)
        {
            var match = Regex.Match(input, pattern);
            if (match.Success && match.Groups.Count > 1)
                return match.Groups[1].Value;
            return "";
        }
    }

    public class USBDeviceInfo
    {
        public string DeviceID { get; set; }
        public string Description { get; set; }
    }
}
