#ifndef _CAMERA
#define _CAMERA

// Signals
#include <csignal>

// Submodules
#include "../libs/inih/INIReader.h"
#include "../libs/date/include/date/date.h"

// Utils
#include "SimpleTimer.h"
#include "Utils.h"

// Data Hanmdlers
#include "FrameGrabber.h"
#include "FrameRecorder.h"
#include "VideoRecorder.h"

class Camera {

protected:

    // Camera settings
    string cameraType;          ///< The type of camera
    string recorderType;        ///< The type of recorder
    unsigned long exposure;     ///< Camera exposure time in microseconds
    unsigned long gain;         ///< Camera gain in percent 0 to 100
    unsigned long frameRate;    ///< Camera frame rate in Hz
    unsigned long width;        ///< Image width in pixels
    unsigned long height;       ///< Image height in pixels

    // Data handlers 
    FrameGrabber * fg;            ///< Pointer to the frame grabber object
    FrameRecorder * fr;           ///< Pointer to the frame recorder object

    // Config object
    INIReader cfg;              ///< The configuration for the camera

    // Configuration flag       
    bool configured;            ///< True when the camera has been configured

    // Exit flag
    bool shouldExit;            ///< When set to true, exit the app

    // Elapsed timer
    SimpleTimer elapsedTimer;   ///< Timer to track runtime of the camera

    // Sleep settings
    unsigned int threadSleep;    ///< ms to sleep the busy wait loop

public:

    /**
     * @brief Construct a new Camera object
     * 
     */
    Camera() {
        shouldExit = false;
    }

    /**
     * @brief Destruct the Camera object
     * 
     */
    ~Camera() {

    }

    /**
     * @brief Load config from ini file
     * 
     * @param cfg 
     */
    void setConfig(INIReader & cfg) {
            

        this->cfg = cfg;
        // Get config values
        cameraType = cfg.Get("camera","camera_type","argus");
        recorderType = cfg.Get("camera","recorder_type","video");
        exposure = cfg.GetInteger("camera","exposure",10000);
        gain = cfg.GetInteger("camera", "gain", 0);

        // Log recorder config
        LOG_S(INFO) << "Camera Config:";
        LOG_S(INFO) << "Exposure: " << exposure;
        LOG_S(INFO) << "Gain: " << gain;

        // Setup the frame grabber
        if (cameraType == "argus" || cameraType == "v4l2") {
            fg = new FrameGrabber();
            fg->setConfig(cfg);
        }
        else {
            LOG_S(ERROR) << "Unsupported camera type.";
            return;
        }

        // Setup the recorder
        if (recorderType == "frame") {
            fr = new FrameRecorder();
            fr->setConfig(cfg);
        }
        else if (recorderType == "video") {
            fr = new VideoRecorder();
            fr->setConfig(cfg);
        }
        else{
            LOG_S(ERROR) << "Unsupported recorder type.";
            return;
        }
        
        fg->setFrameRecorder(fr);
        
        
        configured = true;
        
        // start the recording timer
        elapsedTimer.start();
    }

    /**
     * @brief retrun true when camera is open
     * 
     * @return bool 
     */
    bool cameraIsOpen() {
        return fg->cameraIsOpen();
    }

    /**
     * @brief Opens the camera and starts grabbers and recorders
     * 
     */
    bool open() {
        fg->openCamera();
        fg->startThread();
        return fg->cameraIsOpen();
    }

    /**
     * @brief Close the open camera and stop grabbers and recorders
     * 
     */
    bool close() {
        fg->stopThread();
        fg->waitForThread();
        return !fg->cameraIsOpen();
    }

    /**
     * @brief Reports camera status info throuh several methods
     * 
     */
    void report() {
        LOG_S(INFO) << fg->getStatus() << "," << fr->getStatus();
    }

};

#endif