#include "hik_camera/recorder.hpp"

#include <chrono>

namespace fyt::camera_driver {
Recorder::Recorder(const std::filesystem::path &file, int fps, cv::Size size)
: path(file), size_(size), fps_(fps) {}

Recorder::~Recorder() { stop(); }

void Recorder::start() {
  if (recording_.exchange(true)) {
    return;
  }

  writer_.open(
    path.string(), cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), fps_, size_, true);

  recorder_thread_ = std::thread(&Recorder::recorderThread, this);
}

void Recorder::stop() {
  if (!recording_.exchange(false)) {
    return;
  }

  cv_.notify_all();
  if (recorder_thread_.joinable()) {
    recorder_thread_.join();
  }

  if (writer_.isOpened()) {
    writer_.release();
  }
}

void Recorder::addFrame(const Frame &frame) {
  if (!recording_.load()) {
    return;
  }

  {
    std::lock_guard<std::mutex> lock(mutex_);
    frame_queue_.push_back(frame);
  }
  cv_.notify_one();
}

void Recorder::recorderThread() {
  while (recording_.load()) {
    Frame frame;
    {
      std::unique_lock<std::mutex> lock(mutex_);
      cv_.wait_for(lock, std::chrono::milliseconds(100), [this]() {
        return !recording_.load() || !frame_queue_.empty();
      });

      if (!recording_.load()) {
        break;
      }
      if (frame_queue_.empty()) {
        continue;
      }

      frame = std::move(frame_queue_.front());
      frame_queue_.pop_front();
    }

    if (!writer_.isOpened()) {
      continue;
    }

    if (frame.size() != static_cast<size_t>(size_.width * size_.height * 3)) {
      continue;
    }

    cv::Mat img(size_.height, size_.width, CV_8UC3, frame.data());
    writer_.write(img);
  }
}

}  // namespace fyt::camera_driver
