// +-------------------------------- DISCLAIMER ---------------------------------+
// |                                                                             |
// | This program is provided to you free of charge "as such".                   |
// | You should not assume that this program is error-free or suitable for any   |
// | purpose whatsoever.                                                         |
// |                                                                             |
// | EURESYS does not give any representation, warranty or undertaking that this |
// | program is free of any defect or error or suitable for any purpose. EURESYS |
// | shall not be liable, in contract, in torts or otherwise, for any damages,   |
// | loss, costs, expenses or other claims for compensation, including those     |
// | asserted by third parties, arising out of or in connection with the use of  |
// | this program.                                                               |
// +-----------------------------------------------------------------------------+

#include "CameraApi.h"
#include "CameraHandler.h"
#include "CameraDefine.h"

#include <chrono>
#include <iostream>

#include "Easy.h"
using namespace Euresys::Open_eVision;

CameraHandler::CameraHandler(): data_(nullptr), width_(0),
  height_(0), pitch_(0), image_(new EImageBW8()), abort_(false), camera_(-1)
{
  if (init_SDK() == -1) {
    std::cout << "Initialisation of SDK failed" << std::endl;
  }
}

CameraHandler::~CameraHandler()
{
  std::cout << "Close camera" << std::endl;
  if (cameraThread_.joinable())
  {
    abort_ = true;
    // waits for ProcessingThread::run to end
    cameraThread_.join();
  }
  delete[] data_;
}

void CameraHandler::launchCamera()
{
  // run main loop in a separate thread
  cameraThread_ = std::thread(&CameraHandler::run, this);

  CameraSetAeState(camera_, false);

  CameraSetAnalogGain(camera_, 2);

  CameraSetExposureTime(camera_, 6000);

}

void CameraHandler::run()
{
  BYTE* pRawBuffer = nullptr;
  tSdkFrameHead tFrameHead;

  while(!abort_)
  {
    processImageMutex_.lock();
    if (CameraGetImageBuffer(camera_, &tFrameHead, &pRawBuffer, 200) == CAMERA_STATUS_SUCCESS)
    {
      CameraImageProcess(camera_, pRawBuffer, data_, &tFrameHead);
      CameraReleaseImageBuffer(camera_, pRawBuffer);

      width_ = tFrameHead.iWidth;
      height_ = tFrameHead.iHeight;
      pitch_ = tFrameHead.iWidth * (tCapability.sIspCapacity.bMonoSensor ? 1 : 3);

      canProcessImage_.notify_one();
      processImageMutex_.unlock();
    }
    else
    {
      processImageMutex_.unlock();

      if (abort_) break;
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
  }
}

void CameraHandler::stop()
{
  abort_ = true;
  canProcessImage_.notify_all();
}

void CameraHandler::getImage(EImageBW8 &image)
{
  std::unique_lock<std::mutex> lock(processImageMutex_);
  canProcessImage_.wait(lock);
  image.InitializeFromUnalignedBuffer(width_, height_, data_, pitch_);
}

bool CameraHandler::isAborted()
{
  return abort_;
}

void CameraHandler::setExposureModeManual(int analogGain, double exposureTime)
{
  CameraSetAeState(camera_, false);

  CameraSetAnalogGain(camera_, analogGain);

  CameraSetExposureTime(camera_, exposureTime);
}

int CameraHandler::init_SDK()
{
  int                     iCameraCounts = 4;
  int                     iStatus=-1;
  tSdkCameraDevInfo       tCameraEnumList[4];

  CameraSdkInit(1);

  CameraEnumerateDevice(tCameraEnumList, &iCameraCounts);

  if(iCameraCounts==0){
    return -1;
  }

  iStatus = CameraInit(&tCameraEnumList[0], -1, -1,&camera_);

  if(iStatus!=CAMERA_STATUS_SUCCESS){
    return -1;
  }

  CameraGetCapability(camera_,&tCapability);

  data_ = new unsigned char[tCapability.sResolutionRange.iHeightMax*tCapability.sResolutionRange.iWidthMax
                                 *  (tCapability.sIspCapacity.bMonoSensor ? 1 : 3)];

  CameraPlay(camera_);

  int RPos,GPos,BPos;

  CameraGetGain(camera_,&RPos,&GPos,&BPos);

  if(tCapability.sIspCapacity.bMonoSensor){
    CameraSetIspOutFormat(camera_,CAMERA_MEDIA_TYPE_MONO8);
  }else{
    CameraSetIspOutFormat(camera_,CAMERA_MEDIA_TYPE_RGB8);
  }
  return 0;
}

