﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TIASshot {
    public partial class PreviewMonitor : Form {
        private Point _location;
        private Size _size = new Size(0, 0);
        private Bitmap _dispBuf;
        private bool _isUpdateBuf = false;
        private readonly object _dispBufLock = new object();

        // 副モニタ（セカンダリ・非アクティブ・ボーダーレスウィンドウ）は，コールバックスレッドから
        // 毎フレーム Invalidate() すると WM_PAINT が飢餓状態になり sub_paint_fps が数fpsまで落ち込む
        // （実描画コスト・GDI 枯渇・画像サイズ肥大は計測により否定済み）。
        // そのため UI タイマーで一定周期（約30fps）に自ペース再描画し，コールバックスレッドからは
        // 「最新フレームの保持」のみを行い Invalidate は一切呼ばない。
        private const int RefreshIntervalMs = 33; // ≒30fps
        private readonly System.Windows.Forms.Timer _refreshTimer;

        public bool IsShown => (_size.Width > 0);

        /// <summary>
        /// プレビューウィンドウのコンストラクタ
        /// </summary>
        public PreviewMonitor() {
            InitializeComponent();

            // プレビューウィンドウの位置とサイズを取得
            foreach (var scr in Screen.AllScreens) {
                if (scr.Primary) continue; // プライマリスクリーンは除外
                if (scr.Bounds.Width <= Config.GetInt("Shot/PreviewMonitorWidth")) {
                    _location = scr.Bounds.Location;
                    _size = scr.Bounds.Size;
                }
            }

            _refreshTimer = new System.Windows.Forms.Timer();
            _refreshTimer.Interval = RefreshIntervalMs;
            _refreshTimer.Tick += RefreshTimer_Tick;

            this.FormClosing += (s, e) => _refreshTimer.Stop();
        }

        /// <summary>
        /// プレビューウィンドウのロードイベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PreviewMonitor_Load(object sender, EventArgs e) {
            // プレビューウィンドウの位置とサイズを設定
            Location = _location;
            Size = _size;

            if (IsShown) _refreshTimer.Start();
        }

        /// <summary>
        /// プレビューウィンドウの表示状態を更新（カメラのコールバックスレッドから毎フレーム呼ばれる）
        /// 最新フレームの保持のみを行い，UI 再描画のトリガー（Invalidate）は呼ばない。
        /// 実際の表示更新は RefreshTimer_Tick（UI スレッド）が自ペースで行う。
        /// </summary>
        /// <param name="image"></param>
        public void UpdateImage(Bitmap image) {
#if MEMMONITOR
            PreviewProfiler.IncrementSubUpdates();
#endif
            // 原因3: RefreshTimer_Tick に反映されなかった旧バッファを上書き前に Dispose する
            // _dispBufLock で UpdateImage（コールバックスレッド）と RefreshTimer_Tick（UIスレッド）の
            // 競合・二重 Dispose を防ぐ
            Bitmap toDispose = null;
            lock (_dispBufLock) {
                if (_isUpdateBuf && _dispBuf != null) {
                    toDispose = _dispBuf;
                }
                _dispBuf = image;
                _isUpdateBuf = true;
            }
#if MEMMONITOR
            if (toDispose != null) PreviewProfiler.IncrementSubDrops();
#endif
            toDispose?.Dispose();   // ロック外で GDI 解放（コールバックスレッド上だが，まだ表示に使われていないフレームのみ）
            // Invalidate() は呼ばない（毎フレーム呼ぶと WM_PAINT が飢餓状態になるため）。
        }

        /// <summary>
        /// UI タイマーによる自ペース再描画（約30fps）。
        /// 新フレームがあるときだけ picPreview.Image を差し替える（静止フレームは無駄に再描画しない）。
        /// </summary>
        private void RefreshTimer_Tick(object sender, EventArgs e) {
            if (!IsShown) return;

            // 原因3: _dispBufLock で UpdateImage との競合を防ぎながらバッファを取り出す
            Bitmap toDisplay;
            lock (_dispBufLock) {
                if (!_isUpdateBuf || _dispBuf == null) return;
                toDisplay = _dispBuf;
                _dispBuf = null;        // 参照を手放し、UpdateImage 側が誤 Dispose しないようにする
                _isUpdateBuf = false;
            }

            // picPreview.Image の Dispose・差し替えは UI スレッド（このタイマー Tick）でのみ行う。
            // 代入によって picPreview 自身が再描画され，MEMMONITOR の TimedPictureBox.OnPaint 計測も
            // 引き続き機能する。
            picPreview.Image?.Dispose();    // 直前に表示していたバッファを解放
            picPreview.Image = toDisplay;
#if MEMMONITOR
            PreviewProfiler.IncrementSubPaints();
#endif
        }
    }
}
