#pragma once

#include "k_vb_comm.h"
#include "mpi_sys_api.h"
#include "mpi_vb_api.h"

#include <stdio.h>

/**
 * @brief Manages a video buffer (VB) block
 */
struct VbBuffer {
    k_vb_blk_handle handle    = VB_INVALID_HANDLE;
    k_u32           pool_id   = VB_INVALID_POOLID;
    k_u64           phys_addr = 0;
    void*           virt_addr = nullptr;
    k_u32           size      = 0;

    ~VbBuffer() { release(); }

    // Prohibit copy
    VbBuffer(const VbBuffer&)            = delete;
    VbBuffer& operator=(const VbBuffer&) = delete;

    // Allow move
    VbBuffer(VbBuffer&& other) noexcept
        : handle(other.handle)
        , pool_id(other.pool_id)
        , phys_addr(other.phys_addr)
        , virt_addr(other.virt_addr)
        , size(other.size)
    {
        other.handle    = VB_INVALID_HANDLE;
        other.phys_addr = 0;
        other.virt_addr = nullptr;
    }

    VbBuffer& operator=(VbBuffer&& other) noexcept
    {
        if (this != &other) {
            release();
            handle          = other.handle;
            pool_id         = other.pool_id;
            phys_addr       = other.phys_addr;
            virt_addr       = other.virt_addr;
            size            = other.size;
            other.handle    = VB_INVALID_HANDLE;
            other.phys_addr = 0;
            other.virt_addr = nullptr;
        }
        return *this;
    }

    VbBuffer() = default; // Default constructor

    /**
     * @brief Allocate a buffer of a specific size
     */
    bool get(k_u32 size, k_u32 pool_id = VB_INVALID_POOLID)
    {
        if (handle != VB_INVALID_HANDLE) {
            printf("Buffer already allocated\n");
            return false;
        }
        this->size = size;
        handle     = kd_mpi_vb_get_block(pool_id, size, nullptr);
        if (handle == VB_INVALID_HANDLE) {
            printf("kd_mpi_vb_get_block failed\n");
            return false;
        }
        this->pool_id = kd_mpi_vb_handle_to_pool_id(handle);
        phys_addr     = kd_mpi_vb_handle_to_phyaddr(handle);
        virt_addr     = nullptr;

        return true;
    }

    bool mmap(void)
    {
        virt_addr = kd_mpi_sys_mmap_cached(phys_addr, size);

        return virt_addr ? true : false;
    }

    bool maped(void) {
        return virt_addr ? true : false;
    }

    void munmap(void)
    {
        if (nullptr != virt_addr) {
            kd_mpi_sys_munmap(virt_addr, size);
        }
        virt_addr = nullptr;
    }

    /**
     * @brief Release the buffer
     */
    void release()
    {
        if (handle != VB_INVALID_HANDLE) {
            kd_mpi_vb_release_block(handle);
            handle    = VB_INVALID_HANDLE;
            phys_addr = 0;
            virt_addr = nullptr;
        }
    }
};
