#ifndef CAMERA_H
#define CAMERA_H

#pragma once
#include <chrono>
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <stdexcept>
#include <future>
#include <mutex>
#include <opencv2/opencv.hpp>
// #include "camera_template.hpp"

#include <mvIMPACT_CPP/mvIMPACT_acquire.h>
#include <mvIMPACT_CPP/mvIMPACT_acquire_GenICam.h>

#include "exampleHelper.h"

#undef ERROR

  namespace camera{
      struct frame{
      cv::Mat image;
      int64_t id = 0;
      int64_t timestamp_us = 0; 
      double acquisition_time;
    };
  }//namespace camera

  namespace interfaceBALLUFF{
    struct camInfo{
	    unsigned int id = 0;
	    std::string serial;
	    std::string name;
	    std::string ver;
	    std::string resolution;
    };

    std::vector<camInfo> availableCameras();
    
    class Camera {
    public: 
      enum CALLBACK_TYPE {NONE, WAIT, ASYNC};
      bool connected = false;
    private: // attributes
      using Clock = std::chrono::high_resolution_clock;
      
      mvIMPACT::acquire::DeviceManager* devMan = nullptr;
      mvIMPACT::acquire::Device* _pDev = nullptr;
      mvIMPACT::acquire::Request* _lastReq = nullptr;
      mvIMPACT::acquire::Request* _prevReq = nullptr;
      std::unique_ptr<mvIMPACT::acquire::GenICam::EventControl> _eCtr;
      std::unique_ptr<mvIMPACT::acquire::FunctionInterface> funcintf_;
      std::unique_ptr<mvIMPACT::acquire::ImageDestination> imgdest_;
      std::unique_ptr<mvIMPACT::acquire::GenICam::AcquisitionControl > acqC_;
      std::unique_ptr<mvIMPACT::acquire::GenICam::ImageFormatControl> formCont_;
      std::chrono::time_point<std::chrono::high_resolution_clock> start_time, end_time;
      std::chrono::time_point<Clock> _lastTime;
      int _nextNR = -1;
      CALLBACK_TYPE typeOfCallback = CALLBACK_TYPE::NONE;
      std::function<void(camera::frame)> userCallback;
      std::future<void> runCapture;
      std::mutex trig_mux;
    
    private: // methods
      void init(const std::string& settings = "");

    public:
      Camera(unsigned int numID, const std::string& settings = "");

      Camera(std::string serial, const std::string& settings = "");

      Camera()=default;

      Camera(const Camera&)=delete;
      ~Camera()=default;

      public:

      void set_idPf(mvIMPACT::acquire::TImageDestinationPixelFormat idpf = idpfRGB888Packed);

      void set_idPf(const std::string idpf);

      std::string get_Name();
      
      uint8_t get_ID();

      std::string get_idPf();

      int get_Format_px();

      double get_Exposure_us();

      void set_Exposure_us(double duration);

      camera::frame createImage(int imTYPE, mvIMPACT::acquire::Request* pRequest);

      mvIMPACT::acquire::Device* get();

      bool isConnected();
      
      void reset();

      void close();

      void forceClose();

      bool loadConfig(const std::string& path2XML);

      void reduceResolution(int ratio);
     
      /* Read Image by default setting or setting from "setting file", not with external trigger */
      camera::frame readImage(const int imTYPE = CV_8UC3, int timeout_ms = -1, bool unlocking = false);
      
    };//class Camera
  }// namespace interfaceBALLUFF
 #endif // CAMERA_H
