// +-------------------------------- 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 "CameraHandler.h"
#include <chrono>
#include <map>

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

CameraHandler::CameraHandler(const std::string&): data_(nullptr), width_(0),
    height_(0), pitch_(0), abort_(false), camera_(), buffer_(new EImageC24())
{}

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

void CameraHandler::launchCamera()
{
  try
  {
    std::cout << "Connecting to camera" << std::endl;
    camera_.Connect();
    std::cout << "Setting exposure time" << std::endl;
    camera_.f().ExposureTime.Set(10000);

    std::cout << "Getting camera info" << std::endl;

    int width = static_cast<int>(camera_.f().Width);
    int height = static_cast<int>(camera_.f().Height);

    std::cout << "Width is " << width << std::endl;
    std::cout << "Height is " << height << std::endl;

    std::string pixelFormat = std::string(camera_.f().PixelFormat.GetString());

    std::cout << "Camera pixel format: " << pixelFormat << std::endl;

    buffer_->SetSize(width, height);
    width_ = buffer_->GetWidth();
    height_ = buffer_->GetHeight();
    pitch_ = buffer_->GetRowPitch();
    data_ = (uint8_t*) buffer_->GetImagePtr();
    std::cout << "Buffer initialized" << std::endl;

    // run main loop in a separate thread
    cameraThread_ = std::thread(&CameraHandler::run, this);
  }
  catch (NeoAPI::NeoException& exc)
  {
    std::cout << "error: " << exc.GetDescription() << std::endl;
    abort_ = true;
  }
  catch (std::exception& exc)
  {
    std::cout << "oops, error" << "[" << exc.what() << "]" << std::endl;
    abort_ = true;
  }
  catch (...)
  {
    std::cout << "oops, error" << std::endl;
    abort_ = true;
  }
}

void CameraHandler::run()
{
  try
  {
    while(!abort_)
    {
      NeoAPI::Image image = camera_.GetImage();
      if (image.IsEmpty()) {
        continue;
      }

      if (!image.IsPixelFormatAvailable("BGR8")) {
        throw std::runtime_error("Mono8 image format not available");
      }

      image = image.Convert("BGR8");
      uint8_t* cbuf = (uint8_t*) image.GetImageData();
      {
        const std::lock_guard<std::mutex> lock(processImageMutex_);
        buffer_->InitializeFromUnalignedBuffer(buffer_->GetWidth(), buffer_->GetHeight(), cbuf, 0);
        canProcessImage_.notify_one();
      }
    }
  }
  catch (NeoAPI::NeoException& exc)
  {
    std::cout << "error: " << exc.GetDescription() << std::endl;
    abort_ = true;
  }
  catch (std::exception& exc)
  {
    std::cout << "oops, error" << "[" << exc.what() << "]" << std::endl;
    abort_ = true;
  }
  catch (...)
  {
    std::cout << "oops, error" << std::endl;
    abort_ = true;
  }
}
