#pragma once

#include "fgc/ICameraSource.h"
#include "fgc/IControlChannel.h"

#include <atomic>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <optional>
#include <queue>
#include <string>
#include <thread>
#include <vector>

namespace fgc {

// Gimbal orientation in degrees, stamped onto each CamEvent.
struct Orientation {
    float yaw_deg   = 0.f;
    float pitch_deg = 0.f;
};

// Consumes captured frames and turns them into stored artifacts: rotate 90 deg
// CCW, encode to JPEG XL, write to <output_dir>/<label>/<timestamp>.jxl, and
// publish a CamEvent. Runs a background worker so encoding never blocks
// acquisition. Split out of the old VimbaHandler::SaveImage so any camera
// source (real or mock) can feed it.
class ImagePipeline {
public:
    struct Params {
        std::string              output_dir;
        std::vector<std::string> labels = {"RGB", "ACR", "NIR"};  // by camera index
        std::string              tower;
        double                   jxl_distance = 2.0;  // 0 = lossless
        int                      jxl_effort   = 3;
        bool                     demo = false;          // copy demo_image instead of encoding
        std::string              demo_image = "test_smoke.jxl";
        bool                     display = false;        // show an OpenCV preview window
    };

    // orientation: returns the current gimbal yaw+pitch (degrees) for CamEvent.
    ImagePipeline(IControlChannel& channel, std::function<Orientation()> orientation, Params params);
    ~ImagePipeline();

    void start();
    void stop();

    // Enqueue a frame for processing (thread-safe; copies the frame).
    void submit(const Frame& frame);

    // Runtime-adjustable encoder settings (console "set camera ..." commands).
    void setDistance(double d) { params_.jxl_distance = d; }
    void setEffort(int e) { params_.jxl_effort = e; }
    void setDisplay(bool on) { params_.display = on; }

    // Last CamEvent published (camera label, heading/pitch, timestamp), for the
    // UI's camera panel. Thread-safe; nullopt until the first capture is saved.
    std::optional<CamEvent> lastEvent() const;

private:
    void run();
    void process(const Frame& frame);
    std::string labelFor(int cam_id) const;

    IControlChannel&             channel_;
    std::function<Orientation()> orientation_;
    Params                       params_;

    std::queue<Frame>       queue_;
    std::mutex              mutex_;
    std::condition_variable cv_;
    std::atomic<bool>       running_{false};
    std::thread             worker_;

    mutable std::mutex      last_event_mutex_;
    std::optional<CamEvent> last_event_;
};

}  // namespace fgc
