#pragma once

#include <cstdint>
#include <memory>
#include <string>
#include <mutex>
#include <stop_token>
#include <unordered_set>
#include <vector>

#include "core/bounded_queue.hpp"

namespace smartcut::core {

struct PreviewFrame {
  std::uint64_t sequence{};
  std::uint64_t frame_index{};
  int width{};
  int height{};
  int stride{};
  int pixel_format{};
  int color_range{};
  int color_primaries{};
  int color_transfer{};
  int color_matrix{};
  std::string decode_backend{"software"};
  std::string display_color_mode{"source"};
  bool tone_mapped{false};
  std::vector<std::uint8_t> pixels;
};

class PreviewFrameTransport {
 public:
  explicit PreviewFrameTransport(std::size_t capacity) : queue_(capacity) {}
  bool publish(std::shared_ptr<PreviewFrame> frame) {
    if (!frame) return false;
    if (!queue_.push(std::move(frame))) return false;
    return true;
  }
  std::shared_ptr<PreviewFrame> acquire() {
    auto frame = queue_.pop();
    if (!frame.has_value()) return {};
    { std::lock_guard lock(mutex_); outstanding_.insert((*frame)->sequence); }
    return *frame;
  }
  std::shared_ptr<PreviewFrame> wait_acquire(std::stop_token stop = {}) {
    auto frame = queue_.wait_pop(stop);
    if (!frame.has_value()) return {};
    { std::lock_guard lock(mutex_); outstanding_.insert((*frame)->sequence); }
    return *frame;
  }
  bool acknowledge(std::uint64_t sequence) {
    std::lock_guard lock(mutex_);
    return outstanding_.erase(sequence) == 1;
  }
  std::size_t outstanding() const { std::lock_guard lock(mutex_); return outstanding_.size(); }
  void close() { queue_.close(); }
  bool closed() const { return queue_.closed(); }
 private:
  BoundedQueue<std::shared_ptr<PreviewFrame>> queue_;
  mutable std::mutex mutex_;
  std::unordered_set<std::uint64_t> outstanding_;
};

}  // namespace smartcut::core
