#pragma once

#include <iostream>
#include <future>
#include <thread>
#include <functional>

#include <opencv2/opencv.hpp>

#include <input.h>
#include <processing.h>
#include <control_module.h>
#include <interpreter.h>
#include <intersection_handler.h>


using namespace cv;

struct LFR_Result
{
    bool validLane;
    cv::Mat rawImage;
    cv::Mat processedImage;
    FrameData data;
    std::vector<double> motorSignals;
};

class LFR
{
public:
    using ListenerKey = void const*;
    using ExceptionCallback = std::function<bool(std::exception const &ex)>;
    using ListenerCallback = std::function<void(LFR_Result)>;

private:
    using ListenerPair = std::pair<ListenerKey, ListenerCallback>;
    using ListenerVector = std::vector<ListenerPair>;

    Input input;
    Processing processing;
    ControlModule controlModule;
    Interpreter interpreter;
    IntersectionHandler intersectionHandler;

    int thresholdBinary;
    int gaussKernelSize;

    ListenerVector listeners;
    ExceptionCallback cb;
    bool stop;
    std::unique_ptr<std::thread> thread;
    mutable std::mutex mutex;
    
    //void provideOutput(Mat originalImage, Mat processedImage, const FrameData& frameData, const Rect& roi);
    void createThread();
    void setStop(bool val);

public:
    LFR() = delete;
    LFR(int videoHeight, int videoWidth, int thresholdBinary, int gaussKernelSize, double maxSpeed, ExceptionCallback cb);
    ~LFR();

    void startLoop();
    void endLoop();
    void addListener(ListenerCallback cv, ListenerKey key);
    void removeListener(ListenerKey key);
    void isStopped() const noexcept;
    Mat provideOutput(Mat originalImage, Mat processedImage, const FrameData& frameData, const Rect& roi);    
};