#ifndef UTILS_H
#define UTILS_H

#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
#include <chrono>
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include "kpu.h"
#include "k_video_comm.h"
#include "mpi_sys_api.h"

using cv::Mat;
using std::cout;
using std::endl;
using std::ifstream;
using std::vector;

//------------------------------------------------------------------------------
// ScopedTiming: A lightweight timing utility to measure execution duration
// Automatically starts timing upon construction and reports elapsed time
// when going out of scope (RAII style).
//
// Example usage:
//   {
//       PROFILE_SCOPE("Image Preprocessing", debug_mode);
//       preprocess_image();
//   } // Automatically prints "Image Preprocessing took XXX ms" if debug_mode == true
//------------------------------------------------------------------------------

struct ScopedTiming {
    std::chrono::steady_clock::time_point start; // Start time point
    std::string name;                            // Name/label of the measured scope
    bool enabled;                                // Whether timing output is enabled

    // Constructor overload: accepts C-style string
    ScopedTiming(const char* n, bool e = true)
        : start(std::chrono::steady_clock::now()), name(n), enabled(e) {}

    // Constructor overload: accepts std::string
    ScopedTiming(const std::string& n, bool e = true)
        : start(std::chrono::steady_clock::now()), name(n), enabled(e) {}

    // Destructor: prints elapsed time in milliseconds if enabled
    ~ScopedTiming() {
        if (enabled)
            std::cout << name << " took "
                      << std::chrono::duration<double, std::milli>(
                             std::chrono::steady_clock::now() - start)
                             .count()
                      << " ms\n";
    }
};

//------------------------------------------------------------------------------
// PROFILE_SCOPE(name, debug_mode)
// Helper macro for profiling a code block.
// Creates a ScopedTiming instance with the specified name and debug flag.
// Usage:
//     PROFILE_SCOPE("inference", debug_mode);
//------------------------------------------------------------------------------
#define PROFILE_SCOPE(name, debug_mode) ScopedTiming _timing(name, debug_mode)


/**
 * @brief Color palette (80 predefined colors).
 *        If the number of classes exceeds 80, colors are reused cyclically.
 */
const std::vector<cv::Scalar> color_four = {
    cv::Scalar(60, 20, 220, 127),
    cv::Scalar(32, 11, 119, 127),
    cv::Scalar(142, 0, 0, 127),
    cv::Scalar(230, 0, 0, 127),
    cv::Scalar(228, 0, 106, 127),
    cv::Scalar(100, 60, 0, 127),
    cv::Scalar(100, 80, 0, 127),
    cv::Scalar(70, 0, 0, 127),
    cv::Scalar(192, 0, 0, 127),
    cv::Scalar(30, 170, 250, 127),
    cv::Scalar(30, 170, 100, 127),
    cv::Scalar(0, 220, 220, 127),
    cv::Scalar(175, 116, 175, 127),
    cv::Scalar(30, 0, 250, 127),
    cv::Scalar(42, 42, 165, 127),
    cv::Scalar(255, 77, 255, 127),
    cv::Scalar(252, 226, 0, 127),
    cv::Scalar(255, 182, 182, 127),
    cv::Scalar(0, 82, 0, 127),
    cv::Scalar(157, 166, 120, 127),
    cv::Scalar(0, 76, 110, 127),
    cv::Scalar(255, 57, 174, 127),
    cv::Scalar(0, 100, 199, 127),
    cv::Scalar(118, 0, 72, 127),
    cv::Scalar(240, 179, 255, 127),
    cv::Scalar(92, 125, 0, 127),
    cv::Scalar(151, 0, 209, 127),
    cv::Scalar(182, 208, 188, 127),
    cv::Scalar(176, 220, 0, 127),
    cv::Scalar(164, 99, 255, 127),
    cv::Scalar(73, 0, 92, 127),
    cv::Scalar(255, 129, 133, 127),
    cv::Scalar(255, 180, 78, 127),
    cv::Scalar(0, 228, 0, 127),
    cv::Scalar(243, 255, 174, 127),
    cv::Scalar(255, 89, 45, 127),
    cv::Scalar(103, 134, 134, 127),
    cv::Scalar(174, 148, 145, 127),
    cv::Scalar(186, 208, 255, 127),
    cv::Scalar(255, 226, 197, 127),
    cv::Scalar(1, 134, 171, 127),
    cv::Scalar(54, 63, 109, 127),
    cv::Scalar(255, 138, 207, 127),
    cv::Scalar(95, 0, 151, 127),
    cv::Scalar(61, 80, 9, 127),
    cv::Scalar(51, 105, 84, 127),
    cv::Scalar(105, 65, 74, 127),
    cv::Scalar(102, 196, 166, 127),
    cv::Scalar(210, 195, 208, 127),
    cv::Scalar(65, 109, 255, 127),
    cv::Scalar(149, 143, 0, 127),
    cv::Scalar(194, 0, 179, 127),
    cv::Scalar(106, 99, 209, 127),
    cv::Scalar(0, 121, 5, 127),
    cv::Scalar(205, 255, 227, 127),
    cv::Scalar(208, 186, 147, 127),
    cv::Scalar(1, 69, 153, 127),
    cv::Scalar(161, 95, 3, 127),
    cv::Scalar(0, 255, 163, 127),
    cv::Scalar(170, 0, 119, 127),
    cv::Scalar(199, 182, 0, 127),
    cv::Scalar(120, 165, 0, 127),
    cv::Scalar(88, 130, 183, 127),
    cv::Scalar(0, 32, 95, 127),
    cv::Scalar(135, 114, 130, 127),
    cv::Scalar(133, 129, 110, 127),
    cv::Scalar(118, 74, 166, 127),
    cv::Scalar(185, 142, 219, 127),
    cv::Scalar(114, 210, 79, 127),
    cv::Scalar(62, 90, 178, 127),
    cv::Scalar(15, 70, 65, 127),
    cv::Scalar(115, 167, 127, 127),
    cv::Scalar(106, 105, 59, 127),
    cv::Scalar(45, 108, 142, 127),
    cv::Scalar(0, 172, 196, 127),
    cv::Scalar(80, 54, 95, 127),
    cv::Scalar(255, 76, 128, 127),
    cv::Scalar(1, 57, 201, 127),
    cv::Scalar(122, 0, 246, 127),
    cv::Scalar(208, 162, 191, 127)
};

/**
 * @brief Frame size (width and height).
 */
typedef struct FrameSize
{
    int width;   ///< Frame width
    int height;  ///< Frame height
} FrameSize;

/**
 * @brief YOLO bounding box structure.
 *        Supports detection, segmentation, and oriented bounding boxes.
 */
typedef struct YOLOBbox
{
    cv::Rect box;      ///< Bounding box (used in det/seg/obb)
    float confidence;  ///< Confidence score (used in all tasks)
    int index;         ///< Class index (used in all tasks)
    cv::Mat mask;      ///< Mask (used only in segmentation)
    cv::Mat mask_;      ///< Mask (used only in segmentation)
    float angle;       ///< Rotation angle (used only in OBB)
} YOLOBbox;

// // Configuration structure for YOLO model settings
// typedef struct YoloConfig
// {
//     char* model_type = "yolov8";         // Type of YOLO model (e.g., "yolov8", "yolov5", "yolo11")
//     char* task_type = "detect";          // Type of AI task (e.g., "classify", "detect", "segment", "obb")
//     char* task_mode = "video";           // Mode of processing (e.g., "image", "video")
//     char* image_path = "test.jpg";       // Path to input image (used if task_mode is "image")
//     char* kmodel_path = "yolov8n.kmodel"; // Path to the kmodel file for inference
//     float conf_thres = 0.35;             // Confidence threshold for detection filtering
//     float nms_thres = 0.65;              // Non-Maximum Suppression (NMS) threshold
//     float mask_thres = 0.5;              // Threshold for mask segmentation (if applicable)
//     std::string labels_txt_filepath = "coco_labels.txt"; // Path to the labels text file
//     int debug_mode = 0;                   // Debug mode flag (0: off, 1: on)
// } YoloConfig;


// ---------------------- Utility Functions ------------------------------------

/**
 * @brief Get a color vector for N classes.
 * @param num_classes Number of object classes.
 * @return Vector of colors corresponding to each class.
 */
std::vector<cv::Scalar> get_colors_for_classes(int num_classes);

/**
 * @brief Read class labels from a .txt file.
 * @param labels_txt_path Path to the labels text file.
 * @return Vector of label strings.
 */
std::vector<std::string> read_labels_from_txt(std::string labels_txt_path);

/**
 * @brief Convert a cv::Mat image to a runtime_tensor.
 * @param ori_img Input image as a cv::Mat.
 * @param mode    Mode for tensor conversion ("CHW" or "HWC").
 * @return Runtime tensor containing the image data.
 */
runtime_tensor mat_to_tensor(cv::Mat &ori_img,std::string mode="CHW");

/**
 * @brief Convert a k_video_frame_info to a runtime_tensor.
 * @param videoframe Input video frame information.
 * @param mode       Mode for tensor conversion ("CHW" or "HWC").
 * @return Runtime tensor containing the image data.
 */
runtime_tensor videoframe_to_tensor(k_video_frame_info &videoframe,std::string mode="CHW");

// ---------------------- Standard NMS and Confidence Functions ----------------

/**
 * @brief Apply Non-Maximum Suppression (NMS) to filter overlapping bounding boxes.
 * @param bboxes         Input bounding boxes.
 * @param confThreshold  Minimum confidence threshold.
 * @param nmsThreshold   IoU threshold for suppression.
 * @param indices        Output indices of selected boxes.
 */
void nms(std::vector<YOLOBbox> &bboxes, float confThreshold, float nmsThreshold, std::vector<int> &indices);

/**
 * @brief Compute IoU (Intersection over Union) between two rectangles.
 * @param rect1 First rectangle.
 * @param rect2 Second rectangle.
 * @return IoU value.
 */
float iou_calculate(cv::Rect &rect1, cv::Rect &rect2);

/**
 * @brief Fast exponential function for sigmoid approximation.
 * @param x Input value.
 * @return Approximate e^x.
 */
float fast_exp(float x);

/**
 * @brief Sigmoid activation function.
 * @param x Input value.
 * @return Sigmoid(x).
 */
float sigmoid(float x);

// ---------------------- Rotated Box (OBB) NMS Functions ----------------------

/**
 * @brief Compute covariance matrix for an oriented bounding box (OBB).
 * @param obb Input OBB.
 * @return Covariance matrix (3 elements).
 */
std::array<float, 3> get_covariance_matrix(YOLOBbox &obb);

/**
 * @brief Compute IoU between two rotated bounding boxes.
 * @param obb1 First OBB.
 * @param obb2 Second OBB.
 * @param eps  Small epsilon to avoid division by zero.
 * @return IoU value.
 */
float iou_rotate_calculate(YOLOBbox &obb1, YOLOBbox &obb2, float eps = 1e-7f);

/**
 * @brief Compute the corner coordinates of a rotated bounding box.
 * @param x_center Center X coordinate.
 * @param y_center Center Y coordinate.
 * @param width    Box width.
 * @param height   Box height.
 * @param angle    Rotation angle in degrees.
 * @return Vector of corner coordinate pairs.
 */
std::vector<std::pair<int, int>> calculate_obb_corners(float x_center, float y_center,
                                                       float width, float height, float angle);

/**
 * @brief Apply Non-Maximum Suppression for rotated bounding boxes (OBB).
 * @param bboxes        Input rotated bounding boxes.
 * @param confThreshold Minimum confidence threshold.
 * @param nmsThreshold  IoU threshold for suppression.
 * @param indices       Output indices of selected boxes.
 */
void rotate_nms(std::vector<YOLOBbox> &bboxes, float confThreshold, float nmsThreshold,
                std::vector<int> &indices);

#endif // UTILS_H
