using OpenCvSharp;

namespace SHH.CameraSdk;

/// <summary>
/// [零延迟核心] 智能帧（内存复用+引用计数）
/// 功能：封装 OpenCV Mat 实现物理内存复用，通过引用计数管理生命周期，避免 GC 频繁回收导致的性能抖动
/// 特性：引用归零自动回池，全程无内存分配/释放开销，支撑高帧率实时流处理
/// </summary>
public class SmartFrame : IDisposable
{
    public ConcurrentQueue<string> SubscriberIds { get; } = new ConcurrentQueue<string>();

    #region --- 私有资源与状态 (Private Resources & States) ---

    /// <summary> 所属帧池：用于引用归零后自动回收复用 </summary>
    private readonly FramePool _pool;

    /// <summary> 引用计数器：线程安全，控制帧的生命周期 </summary>
    /// <remarks> 初始值 0，激活后设为 1；引用归零则自动回池 </remarks>
    private int _refCount = 0;

    #endregion

    #region --- 公共属性 (Public Properties) ---

    /// <summary> 帧数据物理内存载体（OpenCV Mat 对象） </summary>
    /// <remarks> 内存由帧池预分配，全程复用，不触发 GC </remarks>
    public Mat InternalMat { get; private set; }

    /// <summary> [快捷属性] 原始图像宽度 (若 TargetMat 为空则返回 0) </summary>
    public int InternalWidth => InternalMat?.Width ?? 0;

    /// <summary> [快捷属性] 原始图像高度 (若 TargetMat 为空则返回 0) </summary>
    public int InnernalHeight => InternalMat?.Height ?? 0;

    /// <summary> 帧激活时间戳（记录帧被取出池的时刻） </summary>
    public DateTime Timestamp { get; private set; }

    #endregion

    #region --- 衍生数据属性 (Derivative Properties) ---

    /// <summary>
    /// [衍生] 目标图像副本（处理后的图像）
    /// <para>用途：存储经过缩放、增强后的图像，供 UI 预览或 Web 推流直接使用</para>
    /// <para>生命周期：完全跟随 SmartFrame，主帧销毁时自动释放</para>
    /// </summary>
    public Mat? TargetMat { get; private set; }

    /// <summary> 变换类型（标记该 TargetMat 是被缩小了还是放大了） </summary>
    public FrameScaleType ScaleType { get; private set; } = FrameScaleType.None;

    /// <summary> [快捷属性] 目标图像宽度 (若 TargetMat 为空则返回 0) </summary>
    public int TargetWidth => TargetMat?.Width ?? 0;

    /// <summary> [快捷属性] 目标图像高度 (若 TargetMat 为空则返回 0) </summary>
    public int TargetHeight => TargetMat?.Height ?? 0;

    #endregion

    #region --- 构造与激活 (Constructor & Activation) ---

    /// <summary>
    /// 初始化智能帧（由帧池调用，外部禁止直接实例化）
    /// </summary>
    /// <param name="pool">所属帧池实例</param>
    /// <param name="width">帧宽度</param>
    /// <param name="height">帧高度</param>
    /// <param name="type">帧数据类型（如 MatType.CV_8UC3）</param>
    internal SmartFrame(FramePool pool, int width, int height, MatType type)
    {
        _pool = pool;
        // 预分配物理内存：内存块在帧池生命周期内复用，避免频繁申请/释放
        InternalMat = new Mat(height, width, type);

        // 确保激活时清理旧的衍生数据（防止脏数据残留）
        ResetDerivatives();
    }

    private int _isReturned = 0; // 0: 激活中, 1: 已归还池

    /// <summary>
    /// [生产者调用] 从帧池取出时激活帧
    /// 功能：初始化引用计数，标记激活时间戳
    /// </summary>
    public void Activate()
    {
        // Optimized: [原因] 使用 Exchange 强制重置归还标记，确保该帧在逻辑上完全从池中脱离，防止归还竞态
        Interlocked.Exchange(ref _isReturned, 0);

        // 激活后引用计数设为 1，代表生产者（驱动/管道）持有该帧
        _refCount = 1;

        // 记录帧被取出池的时间，用于后续延迟计算
        Timestamp = DateTime.Now;
    }

    #endregion

    #region --- 衍生数据操作 (Derivatives Management) ---

    /// <summary>
    /// 挂载处理后的目标图像
    /// </summary>
    /// <param name="processedMat">已处理的 Mat（所有权移交给 SmartFrame）</param>
    /// <param name="scaleType">变换类型（缩小/放大/无）</param>
    public void AttachTarget(Mat processedMat, FrameScaleType scaleType)
    {
        // 防御性编程：如果之前已有 TargetMat，先释放，防止内存泄漏
        if (TargetMat != null)
        {
            TargetMat.Dispose();
        }

        TargetMat = processedMat;
        ScaleType = scaleType;
    }

    private readonly object _subscriberLock = new(); // 建议在类头部定义此锁

    /// <summary>
    /// 内部清理：释放衍生数据
    /// </summary>
    private void ResetDerivatives()
    {
        if (TargetMat != null)
        {
            TargetMat.Dispose();
            TargetMat = null;
        }
        ScaleType = FrameScaleType.None;

        // 2. [核心逻辑] 线程安全地清空订阅者列表
        // 使用 lock 确保在 Clear 的瞬间，没有其他分发线程正在执行 Add 操作
        lock (_subscriberLock)
        {
            // 注意：Clear() 只是把 Count 设为 0，底层数组容量 (Capacity) 不变
            // 这样下次复用时，SubscriberIds 不需要重新分配内存，完美符合零拷贝初衷
            SubscriberIds.Clear();
        }
    }

    #endregion

    #region --- 引用计数管理 (Reference Count Management) ---

    /// <summary>
    /// [消费者调用] 增加引用计数
    /// 适用场景：帧需要被多模块同时持有（如同时分发到 UI 和 AI 分析）
    /// </summary>
    public void AddRef()
    {
        // 原子递增：线程安全，避免多线程竞争导致计数错误
        Interlocked.Increment(ref _refCount);
    }

    #endregion

    #region --- 释放与回池 (Disposal & Pool Return) ---

    /// <summary>
    /// [消费者调用] 释放引用计数
    /// 核心逻辑：引用归零后自动将帧归还至帧池，实现内存复用
    /// </summary>
    public void Dispose()
    {
        // 原子递减：线程安全，确保计数准确
        if (Interlocked.Decrement(ref _refCount) <= 0)
        {
            // 2. 关键：原子抢占归还权。只有成功将 _isReturned 从 0 变为 1 的线程才能执行归还逻辑。
            if (Interlocked.CompareExchange(ref _isReturned, 1, 0) == 0)
            {
                // 3. 彻底清理衍生数据（TargetMat 必须释放）
                ResetDerivatives();

                // 4. 安全归还到池中
                _pool.Return(this);
            }
        }
    }

    #endregion

    // 在 SmartFrame 类中添加此方法
    /// <summary>
    /// [哨兵专用] 强制重置帧状态
    /// 用于自愈机制：当引用计数由于逻辑 Bug 永久无法归零时，由 FramePool 强行回收
    /// </summary>
    internal void ForceReset()
    {
        // 1. 强行将计数器和归还标记归零
        Interlocked.Exchange(ref _refCount, 0);
        Interlocked.Exchange(ref _isReturned, 1);

        // 2. 清理衍生数据，防止内存堆积
        ResetDerivatives();

        // 3. 标记已被强制回收，便于日志追踪
        IsForceRecycled = true;
    }

    // 记录帧被从池中取出的精确时间
    public long BorrowedTick { get; private set; }

    // 标记是否已被哨兵强制回收
    internal bool IsForceRecycled { get; set; }

    public void MarkBorrowed()
    {
        BorrowedTick = Environment.TickCount64;
        IsForceRecycled = false;
    }
}