﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using TIS.Imaging;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.IO;

namespace TIASshot {
    internal class IScam : CameraBase {

        // Color Enhancement プロパティの GUID（DFK23UX249.xml に記載）
        private static readonly Guid ColorEnhancementGuid = new Guid("{3A3A8F77-6440-46CC-940A-8752B02E6C29}");
        ICImagingControl _ic;
        VCDRangeProperty _brightness;
        VCDRangeProperty _gain;
        VCDRangeProperty _exposure;
        VCDRangeProperty _gamma;
        VCDRangeProperty _whiteBalanceBlue;
        VCDRangeProperty _whiteBalanceGreen;
        VCDRangeProperty _whiteBalanceRed;
        FrameQueueSink _queueSink;
        FrameSnapSink _snapSink;
        FrameSnapSink _snapSinkRef;

        /// <summary>
        /// IScamコンストラクタ
        /// </summary>
        /// <param name="form"></param>
        /// <param name="ic"></param>
        public IScam(Form1 form, ICImagingControl ic, string deviceName) : base(form) {
            _ic = ic;
            DeviceName = deviceName;
        }

        /// <summary>
        /// IScam接続
        /// </summary>
        /// <returns></returns>
        public override bool Connect() {
            if (!BootCheck()) return false;

            var configFile = $"{DeviceName}.xml";
            if (!File.Exists(configFile)) {
                ErrorMsg = $"{configFile}が見つかりません";
                return false;
            }
            _ic.LoadDeviceStateFromFile(configFile, true);
            if (!_ic.DeviceValid) {
                ErrorMsg = $"設定ファイル{configFile}の読み込みに失敗しました";
                return false;
            }
            //if (!_ic.LoadShowSaveDeviceState(configFile)) {
            //    return false;
            //}
            _roi = new Rect(
                Config.GetInt($"{DeviceName}/ROI/X"),
                Config.GetInt($"{DeviceName}/ROI/Y"),
                Config.GetInt($"{DeviceName}/ROI/W"),
                Config.GetInt($"{DeviceName}/ROI/H"));

            _queueSink = new FrameQueueSink(Retrieve, MediaSubtypes.RGB24, 5);
            _snapSink = new FrameSnapSink(MediaSubtypes.RGB24);
            _ic.Sink = _queueSink;
            DeviceName = _ic.DeviceCurrent.Name;
            SetInfo("カメラ型番", DeviceName);
            SerialNumber = _ic.DeviceCurrent.GetSerialNumber();
            SetInfo("カメラSN", SerialNumber);
            _brightness = _ic.VCDPropertyItems.Find<VCDRangeProperty>(VCDGUIDs.VCDID_Brightness, VCDGUIDs.VCDElement_Value);
            _gain = _ic.VCDPropertyItems.Find<VCDRangeProperty>(VCDGUIDs.VCDID_Gain, VCDGUIDs.VCDElement_Value);
            _exposure = _ic.VCDPropertyItems.Find<VCDRangeProperty>(VCDGUIDs.VCDID_Exposure, VCDGUIDs.VCDElement_Value);
            _gamma = _ic.VCDPropertyItems.Find<VCDRangeProperty>(VCDGUIDs.VCDID_Gamma, VCDGUIDs.VCDElement_Value);
            _whiteBalanceBlue = _ic.VCDPropertyItems.Find<VCDRangeProperty>(VCDGUIDs.VCDID_WhiteBalance, VCDGUIDs.VCDElement_WhiteBalanceBlue);
            _whiteBalanceGreen = _ic.VCDPropertyItems.Find<VCDRangeProperty>(VCDGUIDs.VCDID_WhiteBalance, VCDGUIDs.VCDElement_WhiteBalanceGreen);
            _whiteBalanceRed = _ic.VCDPropertyItems.Find<VCDRangeProperty>(VCDGUIDs.VCDID_WhiteBalance, VCDGUIDs.VCDElement_WhiteBalanceRed);
            Debug.WriteLine($"Exposure range: {_exposure.RangeMin} to {_exposure.RangeMax}  value={_exposure.Value}" );
            Debug.WriteLine($"Gain range: {_gain.RangeMin} to {_gain.RangeMax}  value={_gain.Value}");
            Debug.WriteLine($"Brightness range: {_brightness.RangeMin} to {_brightness.RangeMax}  value={_brightness.Value}");
            Debug.WriteLine($"Gamma range: {_gamma.RangeMin} to {_gamma.RangeMax}  value={_gamma.Value}");
            Debug.WriteLine($"White Balance Blue range: {_whiteBalanceBlue.RangeMin} to {_whiteBalanceBlue.RangeMax}  value={_whiteBalanceBlue.Value}");
            Debug.WriteLine($"White Balance Green range: {_whiteBalanceGreen.RangeMin} to {_whiteBalanceGreen.RangeMax}  value={_whiteBalanceGreen.Value}");
            Debug.WriteLine($"White Balance Red range: {_whiteBalanceRed.RangeMin} to {_whiteBalanceRed.RangeMax}  value={_whiteBalanceRed.Value}");

            _ic.LiveStart();
            return true;
        }

        /// <summary>
        /// IScam切断
        /// </summary>
        public override void Disconnect() {
            _ic.LiveStop();
        }

        /// <summary>
        /// フレーム取得処理
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        private FrameQueuedResult Retrieve(IFrameQueueBuffer buffer) {
            var frameType = buffer.FrameType;

            using (Mat img = Mat.FromPixelData(frameType.Height, frameType.Width, MatType.CV_8UC3, buffer.GetIntPtr())) {
                using (Mat imgt_full = img.T()) {
                    using (var imgt = new Mat(imgt_full, _roi)) {
                        _form.ShowImage(imgt.ToBitmap());
                        ProcessCalibrationFrame(imgt);
                    }
                }
            }

            return FrameQueuedResult.ReQueue;
        }

        /// <summary>
        /// ゲイン/ホワイトバランスの反映（IScam 固有）
        /// </summary>
        /// <param name="whitePatch">白パッチの計測値</param>
        protected override void ApplyWhiteBalance(Scalar whitePatch) {
            _whiteBalanceBlue.Value = (int)(0.5 + _whiteBalanceBlue.Value * GetRatio((float)whitePatch.Val0, Config.GetFloat("Calib/Reference/B")));
            _whiteBalanceGreen.Value = (int)(0.5 + _whiteBalanceGreen.Value * GetRatio((float)whitePatch.Val1, Config.GetFloat("Calib/Reference/G")));
            _whiteBalanceRed.Value = (int)(0.5 + _whiteBalanceRed.Value * GetRatio((float)whitePatch.Val2, Config.GetFloat("Calib/Reference/R")));
        }

        /// <summary>
        /// 校正収束時のホワイトバランス値ログ出力（IScam 固有）
        /// CalcTcc の前に呼ばれ，元コードの Debug.WriteLine 順序を再現する．
        /// </summary>
        protected override void LogCalibrationConverged() {
            Debug.WriteLine($"White Balance Blue range: {_whiteBalanceBlue.RangeMin} to {_whiteBalanceBlue.RangeMax}  value={_whiteBalanceBlue.Value}");
            Debug.WriteLine($"White Balance Green range: {_whiteBalanceGreen.RangeMin} to {_whiteBalanceGreen.RangeMax}  value={_whiteBalanceGreen.Value}");
            Debug.WriteLine($"White Balance Red range: {_whiteBalanceRed.RangeMin} to {_whiteBalanceRed.RangeMax}  value={_whiteBalanceRed.Value}");
        }

        /// <summary>
        /// 校正完了時のカメラ情報記録（IScam 固有）
        /// CalcTcc の後に呼ばれ，SetInfo で各値を保存する．
        /// </summary>
        /// <param name="imgt">処理対象 Mat</param>
        protected override void RecordCalibrationInfo(Mat imgt) {
            SetInfo("Exposure(ms)", $"{(float)_exposure.Value / 10}");
            SetInfo("Brightness", $"{_brightness.Value}");
            SetInfo("Gamma", $"{_gamma.Value}");
            SetInfo("Gain", $"{_gain.Value}");
            SetInfo("Gain B", $"{_whiteBalanceBlue.Value}");
            SetInfo("Gain G", $"{_whiteBalanceGreen.Value}");
            SetInfo("Gain R", $"{_whiteBalanceRed.Value}");
            SetInfo("画像幅", $"{imgt.Width}");
            SetInfo("画像高さ", $"{imgt.Height}");
        }

        /// <summary>
        /// 撮影
        /// </summary>
        /// <param name="numImages"></param>
        /// <param name="interval"></param>
        protected override void Shot(int numImages = 1, int interval = 0) {
            _ic.LiveStop();
            _ic.Sink = _snapSink;
            _ic.LiveStart();
            _snapSinkRef = _ic.Sink as FrameSnapSink;

            _shots = new BlockingCollection<Mat>();
            _snapSinkRef.SnapSingle(TimeSpan.FromSeconds(5));   // 最初のフレームを捨てる

            var thread = new Thread(() => SaveThread(numImages));
            thread.Start();

            RunShotLoop(numImages, interval);

            _ic.LiveStop();
            _ic.Sink = _queueSink;
            _ic.LiveStart();
        }

        /// <summary>
        /// 撮影開始処理（IScam 固有）: Shot 内でインライン実行済みのため空実装
        /// </summary>
        protected override void BeginShot() { }

        /// <summary>
        /// 1 フレーム取得（IScam 固有）
        /// </summary>
        /// <returns>撮影した Mat（呼び出し元が _shots に追加する）</returns>
        protected override Mat CaptureFrame() {
            var buffer = _snapSinkRef.SnapSingle(TimeSpan.FromSeconds(5));
            using (Mat img = Mat.FromPixelData(buffer.FrameType.Height, buffer.FrameType.Width, MatType.CV_8UC3, buffer.GetIntPtr())) {
                using (Mat imgt_full = img.T()) {
                    return new Mat(imgt_full, _roi);
                }
            }
        }

        /// <summary>
        /// 撮影終了処理（IScam 固有）: Shot 内でインライン実行済みのため空実装
        /// </summary>
        protected override void EndShot() { }

#if DEBUG
        // ---------------------------------------------------------------
        // 白板撮影（DEBUG 限定・照明均一性評価用）
        // ---------------------------------------------------------------

        /// <summary>
        /// 白板撮影を実行する（DEBUG 限定）．
        /// チャート検出・WB 自動調整・TCC 色補正を一切行わず，カメラ素の RGB を保存する．
        /// 校正未完了でも撮影可能．
        /// </summary>
        /// <param name="numImages">撮影枚数</param>
        public override void ShotWhiteBoard(int numImages) {
            var saveFolder = IoUtil.CreateSaveFolder("白板");

            // Color Enhancement を OFF にする（DFK23UX249 専用プロパティ）
            bool colorEnhancementDisabled = TryDisableColorEnhancement();
            if (!colorEnhancementDisabled) {
                Debug.WriteLine("[WhiteBoard] Color Enhancement の無効化をスキップしました（プロパティが存在しないか非対応）");
            }

            _ic.LiveStop();
            _ic.Sink = _snapSink;
            _ic.LiveStart();
            var snapRef = _ic.Sink as FrameSnapSink;

            try {
                // 最初のフレームを捨てる（露出安定化）
                snapRef.SnapSingle(TimeSpan.FromSeconds(5));

                var statsList = new List<FrameStats>();

                for (int i = 0; i < numImages; i++) {
                    _form.ShowMessage($"白板撮影　{i + 1} / {numImages} 枚目");

                    var buffer = snapRef.SnapSingle(TimeSpan.FromSeconds(5));
                    Mat roiFrame = null;
                    try {
                        using (Mat img = Mat.FromPixelData(
                            buffer.FrameType.Height,
                            buffer.FrameType.Width,
                            MatType.CV_8UC3,
                            buffer.GetIntPtr())) {
                            using (Mat imgt_full = img.T()) {
                                // ROI 切り出し（通常撮影と同様）
                                roiFrame = new Mat(imgt_full, _roi).Clone();
                            }
                        }

                        // RGB 画像（PNG）として保存（sRGB 変換なし）
                        var filename = $"WhiteBoard_{i + 1:0000}.png";
                        Cv2.ImWrite(Path.Combine(saveFolder, filename), roiFrame);

                        // 飽和統計の算出
                        var stats = ImageStats.Calc(roiFrame);
                        statsList.Add(stats);

                        Debug.WriteLine(
                            $"[WhiteBoard] frame={i + 1} saturated={stats.SaturatedRatio:P2} " +
                            $"p99={stats.P99:F1} meanB={stats.MeanB:F1} meanG={stats.MeanG:F1} meanR={stats.MeanR:F1}");
                    } finally {
                        if (roiFrame != null) roiFrame.Dispose();
                    }
                }

                // JSON サイドカー出力
                var jsonMeta = BuildWhiteBoardMeta(colorEnhancementDisabled, numImages);
                var jsonStr = ImageStats.BuildJson(jsonMeta, statsList);
                File.WriteAllText(
                    Path.Combine(saveFolder, "whiteboard_meta.json"),
                    jsonStr,
                    new System.Text.UTF8Encoding(encoderShouldEmitUTF8Identifier: false));

                _form.ShowMessage($"白板撮影終了（{numImages} 枚）: {saveFolder}");
                Debug.WriteLine($"[WhiteBoard] 保存完了: {saveFolder}");
            } catch (Exception ex) {
                Debug.WriteLine($"[WhiteBoard] 撮影エラー: {ex.Message}");
                _form.ShowMessage($"白板撮影エラー: {ex.Message}");
                throw;
            } finally {
                _ic.LiveStop();
                _ic.Sink = _queueSink;
                _ic.LiveStart();
            }
        }

        /// <summary>
        /// Color Enhancement を無効化する．
        /// プロパティが存在しない機種では安全にスキップする（GUIDE_09: 空 catch 禁止）．
        /// </summary>
        /// <returns>無効化に成功した場合は true，プロパティ未存在でスキップした場合は false</returns>
        private bool TryDisableColorEnhancement() {
            VCDSwitchProperty prop = null;
            try {
                prop = _ic.VCDPropertyItems.Find<VCDSwitchProperty>(
                    ColorEnhancementGuid,
                    VCDGUIDs.VCDElement_Value);
            } catch (Exception ex) {
                // プロパティが存在しない機種では Find が例外を投げる場合がある
                Debug.WriteLine($"[WhiteBoard] Color Enhancement プロパティの取得に失敗（スキップ）: {ex.Message}");
                return false;
            }

            if (prop == null) {
                Debug.WriteLine("[WhiteBoard] Color Enhancement プロパティが存在しません（スキップ）");
                return false;
            }

            prop.Switch = false;
            Debug.WriteLine("[WhiteBoard] Color Enhancement を OFF にしました");
            return true;
        }

        /// <summary>
        /// JSON サイドカー用の撮影条件メタ辞書を構築する．
        /// </summary>
        private Dictionary<string, object> BuildWhiteBoardMeta(bool colorEnhancementOff, int numImages) {
            var meta = new Dictionary<string, object>();
            meta["capture_datetime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            meta["device_name"] = DeviceName ?? "Unknown";
            meta["serial_number"] = SerialNumber ?? "Unknown";
            meta["roi_x"] = _roi.X;
            meta["roi_y"] = _roi.Y;
            meta["roi_width"] = _roi.Width;
            meta["roi_height"] = _roi.Height;
            meta["num_frames"] = numImages;

            // カメラプロパティ（デバイス状態ファイルの固定値を読み取り）
            if (_exposure != null)
                meta["exposure_ms"] = (double)_exposure.Value / 10.0;
            if (_gain != null)
                meta["gain"] = (double)_gain.Value;
            if (_brightness != null)
                meta["brightness"] = (int)_brightness.Value;
            if (_gamma != null)
                meta["gamma"] = (int)_gamma.Value;
            if (_whiteBalanceBlue != null)
                meta["wb_blue"] = (int)_whiteBalanceBlue.Value;
            if (_whiteBalanceGreen != null)
                meta["wb_green"] = (int)_whiteBalanceGreen.Value;
            if (_whiteBalanceRed != null)
                meta["wb_red"] = (int)_whiteBalanceRed.Value;

            meta["color_enhancement_enabled"] = !colorEnhancementOff;
            meta["color_enhancement_note"] = colorEnhancementOff
                ? "Disabled before capture"
                : "Not available (skipped)";

            // 固定状態（device-state ファイルから既知の設定を明示）
            meta["gamma_setting"] = "100 (linear, scale: 100=1.0)";
            meta["tone_mapping"] = "OFF";
            meta["denoise"] = "0 (OFF)";
            meta["sharpness"] = "0 (OFF)";
            meta["color_matrix"] = "N/A (ImagingSource: Color Enhancement used instead)";
            meta["shading_correction"] = "未補正（LSC 保留）";
            meta["shading_correction_note"] = "No flat-field/LSC correction applied. " +
                "Measured uniformity includes lens vignetting.";

            return meta;
        }
#endif
    }
}
