#pragma once

#include <iostream>
#include <memory>
#include <stdexcept>
#include <utility>

#include "k_video_comm.h"

/**
 * @brief RAII wrapper for k_video_frame that automatically manages
 * the underlying memory block's reference count.
 */
class VideoFrame {
public:
    // Default constructor (empty frame)
    VideoFrame()
        : m_vf({})
        , m_block_handle(0)
    {
    }

    // Constructor to initialize the frame from a k_video_frame struct
    VideoFrame(const k_video_frame& frame)
        // Note: The physical address must be valid before calling this!
        : m_vf(frame)
        , m_block_handle(get_and_increase_handle(frame))
    {
    }

    // Destructor: Decreases the reference count.
    ~VideoFrame() { decrease_handle_refcnt(m_block_handle); }

    // --- Rule of Five/Three: Mandatory for RAII classes ---

    // Copy Constructor
    VideoFrame(const VideoFrame& other)
        : m_vf(other.m_vf)
        , m_block_handle(other.m_block_handle)
    {
        // Simply copy the handle, and increase the ref count of the underlying block.
        if (m_block_handle != 0) {
            kd_mpi_vb_increase_block_refcnt(m_block_handle);
        }
    }

    // Copy Assignment Operator
    VideoFrame& operator=(const VideoFrame& other)
    {
        if (this != &other) {
            // 1. Manage the current resource: decrease its refcount
            decrease_handle_refcnt(m_block_handle);

            // 2. Copy data
            m_vf           = other.m_vf;
            m_block_handle = other.m_block_handle;

            // 3. Manage the new resource: increase its refcount
            if (m_block_handle != 0) {
                kd_mpi_vb_increase_block_refcnt(m_block_handle);
            }
        }
        return *this;
    }

    // Move Constructor
    VideoFrame(VideoFrame&& other) noexcept
        : m_vf(other.m_vf)
        , m_block_handle(other.m_block_handle)
    {
        // Transfer ownership by nullifying the handle in the source object
        other.m_block_handle = 0;
    }

    // Move Assignment Operator
    VideoFrame& operator=(VideoFrame&& other) noexcept
    {
        if (this != &other) {
            // 1. Manage the current resource: decrease its refcount
            decrease_handle_refcnt(m_block_handle);

            // 2. Transfer ownership
            m_vf           = std::move(other.m_vf);
            m_block_handle = other.m_block_handle;

            // 3. Nullify the source handle
            other.m_block_handle = 0;
        }
        return *this;
    }

    // Accessors
    const k_video_frame& frame() const { return m_vf; }
    k_vb_blk_handle      block_handle() const { return m_block_handle; }

private:
    k_video_frame   m_vf;
    k_vb_blk_handle m_block_handle;

    // Helper function to extract the handle and increase refcount
    static k_vb_blk_handle get_and_increase_handle(const k_video_frame& frame)
    {
        // Use the physical address of the first plane (phys_addr[0])
        k_u64 phys_addr = frame.phys_addr[0];

        if (phys_addr == 0) {
            return 0;
        }

        k_vb_blk_handle handle = kd_mpi_vb_phyaddr_to_handle(phys_addr);

        if (handle != 0) {
            if (kd_mpi_vb_increase_block_refcnt(handle) != 0) {
                // Handle error, maybe throw or log
                throw std::runtime_error("Failed to increase VB block ref count.");
            }
        }
        return handle;
    }

    // Helper function to decrease refcount
    static void decrease_handle_refcnt(k_vb_blk_handle handle)
    {
        if (handle != 0) {
            if (kd_mpi_vb_decrease_block_refcnt(handle) != 0) {
                // Log error in destructor, avoid throwing
                // std::cerr << "Failed to decrease VB block ref count." << std::endl;
            }
        }
    }
};

// -------------------------------------------------------------------

/**
 * @brief Simple wrapper for k_video_frame_info.
 * This class now contains a VideoFrame object, inheriting the RAII behavior.
 */
class VideoFrameInfo {
public:
    VideoFrameInfo() { }
    // Constructor initializes the internal k_video_frame_info and
    // initializes the VideoFrame member using the k_video_frame from vf_info.
    VideoFrameInfo(const k_video_frame_info& vf_info)
        : m_vf_info(vf_info)
        , m_video_frame(vf_info.v_frame)
    {
    }

    ~VideoFrameInfo() { }

    // Accessors
    const k_video_frame_info& info() const { return m_vf_info; }
    const VideoFrame&         video_frame() const { return m_video_frame; }

private:
    k_video_frame_info m_vf_info;
    // The VideoFrame wrapper handles the reference counting for the memory block.
    VideoFrame m_video_frame;
};
