using Ayay.SerilogLogs;
using OpenCvSharp;
using Serilog;

namespace SHH.CameraSdk;

/// <summary>
/// [零延迟核心] 智能帧对象池
/// 功能：预分配并复用 SmartFrame 实例，杜绝频繁 new Mat() 与 GC 回收，消除内存分配停顿
/// 核心策略：
/// <para>1. 预热分配：启动时创建初始数量帧，避免运行时内存申请</para>
/// <para>2. 上限控制：最大池大小限制内存占用，防止内存溢出</para>
/// <para>3. 背压丢帧：池空时返回 null，强制丢帧保证实时性，不阻塞生产端</para>
/// </summary>
public class FramePool : IDisposable
{
    private ILogger _sdkLog => Log.ForContext("SourceContext", LogModules.HikVisionSdk);

    #region --- 私有资源与配置 (Private Resources & Configurations) ---

    /// <summary> 可用帧队列（线程安全）：存储待借出的空闲智能帧 </summary>
    private readonly ConcurrentQueue<SmartFrame> _availableFrames = new();

    /// <summary> 所有已分配帧列表：用于统一销毁释放内存 </summary>
    private readonly List<SmartFrame> _allAllocatedFrames = new();

    /// <summary> 创建新帧锁：确保多线程下创建新帧的线程安全 </summary>
    private readonly object _lock = new();

    /// <summary> 帧宽度（与相机输出分辨率一致） </summary>
    private readonly int _width;

    /// <summary> 帧高度（与相机输出分辨率一致） </summary>
    private readonly int _height;

    /// <summary> 帧数据类型（如 CV_8UC3 对应 RGB 彩色图像） </summary>
    private readonly MatType _type;

    /// <summary> 池最大容量：限制最大分配帧数，防止内存占用过高 </summary>
    private readonly int _maxPoolSize;

    #endregion

    #region --- 构造与预热 (Constructor & Warm-Up) ---

    /// <summary>
    /// 初始化智能帧对象池
    /// </summary>
    /// <param name="width">帧宽度</param>
    /// <param name="height">帧高度</param>
    /// <param name="type">帧数据类型</param>
    /// <param name="initialSize">初始预热帧数（默认5）</param>
    /// <param name="maxSize">池最大容量（默认10）</param>
    public FramePool(int width, int height, MatType type, int initialSize = 5, int maxSize = 10)
    {
        _width = width;
        _height = height;
        _type = type;
        _maxPoolSize = maxSize;

        // 预热：启动时预分配初始数量帧，避免运行时动态申请内存
        for (int i = 0; i < initialSize; i++)
        {
            CreateNewFrame();
        }
    }

    /// <summary>
    /// 创建新智能帧并加入池（内部调用，加锁保护）
    /// </summary>
    private void CreateNewFrame()
    {
        var frame = new SmartFrame(this, _width, _height, _type);
        _allAllocatedFrames.Add(frame);
        _availableFrames.Enqueue(frame);
    }

    #endregion

    #region --- 帧借出与归还 (Frame Borrow & Return) ---

    /// <summary>
    /// 从池借出一个智能帧（O(1) 时间复杂度）
    /// </summary>
    /// <returns>可用智能帧 / 池空且达上限时返回 null（触发背压丢帧）</returns>
    public SmartFrame? Get()
    {
        // 1. 优先从可用队列取帧，无锁快速路径
        if (_availableFrames.TryDequeue(out var frame))
        {
            frame.Activate();
            frame.MarkBorrowed(); // 记录起始时间
            return frame;
        }

        // 2. 可用队列为空，检查是否达最大容量
        if (_allAllocatedFrames.Count < _maxPoolSize)
        {
            // 加锁创建新帧，避免多线程重复创建
            lock (_lock)
            {
                // 双重检查：防止等待锁期间其他线程已创建新帧
                if (_allAllocatedFrames.Count < _maxPoolSize)
                {
                    CreateNewFrame();
                }
            }
            // 递归重试取帧
            return Get();
        }

        // ============================================================
        // 3. [自愈触发] 如果走到这里，说明池子满了且所有帧都在外借中。
        // 可能存在“僵尸帧”死锁。执行强制回收哨兵。
        // ============================================================
        if (ForceRecycleZombies())
        {
            // 如果哨兵成功救回了至少一帧，递归重试就能拿到帧
            return Get();
        }

        // 3. 背压策略：池空且达上限，返回 null 强制丢帧，保证生产端不阻塞
        // 适用场景：消费端处理过慢导致帧堆积，丢帧保实时性
        return null;
    }

    /// <summary>
    /// 哨兵巡检：强制回收占用超过 5 秒不还的僵尸帧
    /// </summary>
    private bool ForceRecycleZombies()
    {
        bool anyRescued = false;
        long now = Environment.TickCount64;

        // Optimized: [原因] 使用 lock 或 ToArray() 防止在哨兵巡检期间，
        // 其他线程触发 CreateNewFrame 导致 List 集合修改异常。
        SmartFrame[] snapshot;
        lock (_lock) { snapshot = _allAllocatedFrames.ToArray(); }

        // 遍历所有已分配的帧，找出超时的僵尸
        foreach (var frame in snapshot)
        {
            // 条件：当前不在池中（_refCount > 0）且借出时间超过 2000ms
            // 注意：这里需要给 SmartFrame 暴露一个只读的 RefCount 属性，或者直接判断 BorrowedTick
            if ((now - frame.BorrowedTick) > 2000)
            {
                // 强行重置该帧的所有权
                frame.ForceReset();

                // 重新塞回队列
                _availableFrames.Enqueue(frame);

                anyRescued = true;

                // 记录一条警告日志，告诉你哪路视频出问题了
                // 可以在 AddAuditLog 里看到
                _sdkLog.Warning("[Sdk] SmartFrame(借出超2秒) 被强制回收.");
            }
        }
        return anyRescued;
    }

    /// <summary>
    /// [系统内部调用] 将帧归还至池（由 SmartFrame.Dispose 自动触发）
    /// </summary>
    /// <param name="frame">待归还的智能帧</param>
    public void Return(SmartFrame frame)
    {
        _availableFrames.Enqueue(frame);
    }

    #endregion

    #region --- 资源释放 (Resource Disposal) ---

    /// <summary>
    /// 释放帧池所有资源，销毁所有 Mat 内存
    /// </summary>
    public void Dispose()
    {
        // 遍历所有已分配帧，释放 OpenCV Mat 底层内存
        foreach (var frame in _allAllocatedFrames)
        {
            frame.InternalMat.Dispose();
        }
        _allAllocatedFrames.Clear();
        _availableFrames.Clear();
    }

    #endregion
}