#pragma once

#include "k230_vb.h"

// K230 SDK Headers
#include "k_connector_comm.h"
#include "k_payload_comm.h"
#include "k_vo_comm.h"
#include "mpi_connector_api.h"
#include "mpi_vo_api.h"

#include <array>
#include <memory>
#include <string>
#include <tuple>
#include <vector>

/**
 * @brief Main class for controlling the K230 Display (VO)
 */
class Display {
public:
    // VO Channels
    enum Layer {
        LAYER_VIDEO1 = K_VO_LAYER_VIDEO1,
        LAYER_VIDEO2 = K_VO_LAYER_VIDEO2,
        LAYER_OSD0   = K_VO_LAYER_OSD0,
        LAYER_OSD1   = K_VO_LAYER_OSD1,
        LAYER_OSD2   = K_VO_LAYER_OSD2,
        LAYER_OSD3   = K_VO_LAYER_OSD3
    };

    // Flags (Rotation, Mirror, Gray)
    enum Flags {
        FLAG_ROTATION_0   = K_VPU_ROTATION_0,
        FLAG_ROTATION_90  = K_VPU_ROTATION_90,
        FLAG_ROTATION_180 = K_VPU_ROTATION_180,
        FLAG_ROTATION_270 = K_VPU_ROTATION_270,
        FLAG_MIRROR_NONE,
        FLAG_MIRROR_HOR,
        FLAG_MIRROR_VER,
        FLAG_MIRROR_BOTH,
        FLAG_GRAY_ENABLE,
        FLAG_GRAY_DISABLE
    };

    /**
     * @brief Get the singleton instance of the Display class
     */
    static Display& instance()
    {
        static Display instance;
        return instance;
    }

    Display(const Display&)            = delete;
    Display& operator=(const Display&) = delete;
    Display(Display&&)                 = delete;
    Display& operator=(Display&&)      = delete;

    /**
     * @brief Initialize the display connector and VO system.
     */
    int init(k_connector_type type, int osd_num = 1, int width = 640, int height = 480, int fps = 90);

    /**
     * @brief Deinitialize the display and release all resources.
     */
    int deinit();

    /**
     * @brief Bind a media source (e.g., camera) to a display layer.
     */
    int bind_layer(std::tuple<int, int, int> src, int layer, std::tuple<int, int, int, int> rect, int pixelFormat, int flag,
                   int alpha);

    /**
     * @brief Unbind a media source from a display layer.
     */
    int unbind_layer(int layer);

    void disable_layer(int layer);

    /**
     * @brief Show a software-rendered frame on an OSD layer.
     * This function manages internal buffers for display and uses DMA for rotation.
     */
    int show(k_video_frame_info& src_frame, std::tuple<int, int> pos, int layer, int alpha, int flag);

    /**
     * @brief Get the display width.
     */
    int width() const { return m_width; }

    /**
     * @brief Get the display height.
     */
    int height() const { return m_height; }

    /**
     * @brief Get the display framerate.
     */
    int fps();

private:
    // Private constructor/destructor for singleton
    Display();
    ~Display();

    // Internal struct to hold layer configuration
    struct LayerConfig {
        int                            layer      = -1;
        std::tuple<int, int, int, int> rect       = { 0, 0, 0, 0 };
        int                            pix_format = 0;
        int                            flag       = 0;
        int                            alpha      = 255;
        bool                           configured = false;

        bool operator==(const LayerConfig& o) const
        {
            return layer == o.layer && rect == o.rect && pix_format == o.pix_format && flag == o.flag && alpha == o.alpha;
        }
        bool operator!=(const LayerConfig& o) const { return !(*this == o); }
    };

    // Internal struct to hold binding information
    struct BindConfig {
        k_mpp_chn   src_chn;
        k_mpp_chn   dst_chn;
        LayerConfig layer_config;
        bool        linked = false;
        void bind()
        {
            if (!linked) {
                if(K_SUCCESS != kd_mpi_sys_bind(&src_chn, &dst_chn)) {
                    printf("bind error!\n");
                }
                linked = true;
            }
        }

        void unbind()
        {
            if (linked) {
                if(K_SUCCESS != kd_mpi_sys_unbind(&src_chn, &dst_chn)) {
                    printf("unbind error!\n");
                }
                linked = false;
            }
        }
    };

    // =================================================================
    // Internal VO/VB Helper Methods
    // =================================================================
    int  _config_video_layer(const LayerConfig& config);
    int  _config_osd_layer(const LayerConfig& config);
    int  _config_layer(int layer, std::tuple<int, int, int, int> rect, int pix_format, int flag, int alpha);
    bool _config_vb(k_u32 blk_size, int blk_cnt, k_vb_remap_mode mode);

    // =================================================================
    // Core Display Members
    // =================================================================
    bool  m_is_inited     = false;
    int   m_osd_layer_num = 1;
    k_s32 m_pool_id       = VB_INVALID_POOLID;

    int              m_width  = 0;
    int              m_height = 0;
    k_connector_type m_connector_type;
    k_connector_info m_connector_info;

    std::array<LayerConfig, K_VO_MAX_CHN_NUMS>                 m_layer_cfgs;
    std::array<std::unique_ptr<BindConfig>, K_VO_MAX_CHN_NUMS> m_layer_bind_cfgs;

    std::array<std::unique_ptr<VbBuffer>, K_VO_MAX_CHN_NUMS> m_layer_disp_buffers;
};
