/*
 ********************************************************************************
 * NOTICE 
 * This software is the property of HiRain Technologies. Any information
 * contained in this doc should not be reproduced, or used, or disclosed
 * without the written authorization from HiRain Technologies.
 * 
 ********************************************************************************
 *   Author:   yangtian.yi
 *   Date:     2025-09-02  09:36:44
 *
 ********************************************************************************
 *   Description: 
 *
 ********************************************************************************
 *   Limitations:  None
 *
 ********************************************************************************
 */

#include <chrono>
#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <cstring>
#include <fstream>
#include <iostream>
#include <mutex>
#include <filesystem>

#include <opencv2/opencv.hpp>

#include "camera_handler_type.h"
#include "camera_handler_interface.h"
#include "inference_interface.h"
#include "utils.h"

using namespace inference;
using namespace camera_handler;

bool stop_flag = false;
uint8_t* image_data;
CameraHandlerStatus camera_status;
InferenceStatus infer_status;
AppImageInfo image_info;
int w;
int h;
std::string directory = "/opt/model_zoo/ONR-OD-8200-yolox-nano-lite-mmdet-coco-416x416/";

// Interrupt function, used to respond to Ctrl+C input
void handle_sigint(int sig) {
  std::cout << "User inputs ctrl+c" << std::endl;
  stop_flag = true;
}

int main(int argc, char** argv) {
  // Register Interrupt Function
  signal(SIGINT, handle_sigint);
  
  // A necessary statement in all programs using gstreamer
  gst_init(&argc, &argv);
  
  // Construct pipeline
  auto pipe_ptr = CreatePipeline(CAMERA_HANDLER_NV12, 640, 640);
  camera_status = GetImageInfo(pipe_ptr, image_info);
  if (camera_status == CAMERA_HANDLER_NULLPTR) {
    std::cout << "nullptr" << std::endl;
    return 0;
  }
  w = image_info.width_;
  h = image_info.height_;

  //Construct inferer
  auto *inferer = CreateDdawInferer(directory);
  if (!inferer) {
    std::cout << "nullptr!!!" << std::endl;
    return 0;
  }
  infer_status = DumpInputInfo(inferer);
  infer_status = DumpOutputInfo(inferer);
  if (infer_status != INFERENCE_SUCCESS) {
    std::cout << "dumpinfo failed!!!" << std::endl;
  }

  // Loop to pull images from the pipeline and infer
  while (!stop_flag) {
    // pull image
    camera_status = PullImageFromPipeline(pipe_ptr, image_data);
    if (camera_status != CAMERA_HANDLER_SUCCESS) {
      std::cout << "cannot get image from pipeline" << std::endl;
      break;
    }
    cv::Mat raw_image = cv::Mat(int(1.5*h), w, CV_8UC1, image_data);
    
    // preprocess
    cv::Mat bgr_image;
    cv::cvtColor(raw_image, bgr_image, cv::COLOR_YUV2BGR_NV12);
    cv::Mat resize_image;
    cv::resize(bgr_image, resize_image, cv::Size(416,416));
    auto *inputbuff = GetInputBufferData(inferer, 0);
    Preprocess(resize_image, inputbuff);
    
    // infer
    infer_status = RunModel(inferer);
    if (infer_status != INFERENCE_SUCCESS) {
      std::cout << "infer failed!!!" << std::endl;
    }

    // postprocess
    auto *detdata = GetOutputBufferData(inferer, 0);
    std::vector<std::array<int, 4>> infer_result;
    Postprocess(infer_result, detdata);
    if (infer_result.size() > 0) {
      std::cout << "Have detected " << infer_result.size() << " objects." << std::endl;
      for (int i = 0; i < infer_result.size(); i++) {
        auto on_ret = infer_result[i];
        cv::rectangle(resize_image, 
          cv::Point(on_ret[0], on_ret[1]), 
          cv::Point(on_ret[2], on_ret[3]),
          cv::Scalar(0, 0, 255), 2);
      }
      cv::imwrite("/home/ddaw_image.jpg", resize_image);
    }
  }
  
  // destroy inferer
  infer_status = DestroyDdawInferer(inferer);
  
  // Destroy pipeline
  camera_status = DestroyPipeline(pipe_ptr);

  return 0;
}