// +-------------------------------- 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.                                                               |
// +-----------------------------------------------------------------------------+

#pragma once

#include "../Common/GrabberInterface.h"

#include "DummyDialog.h"

class DummyGrabber : public GrabberInterface
{
public:

  // Construction and destruction

  DummyGrabber(const std::string& device_name = "") : GrabberInterface(device_name), started(false), locked(false), frame_counter(0), frame(NULL), frame_width(1024), frame_height(768)
  {}

  ~DummyGrabber()
  {
    delete frame;
  }

  // Parameter handling

  virtual void SetParameter(const std::string& name, int value)
  {
    if (name == "Width")
      frame_width = value;
    else if (name == "Height")
      frame_height = value;
  }

  // Acquisition control

  virtual void Start(int num_frame = 1)
  {
    if (frame == NULL)
      frame = new EImageBW8(frame_width, frame_height);
    else
      frame->SetSize(frame_width, frame_height);

    started = true;
  }

  virtual long long int WaitFrame(int timeout_ms = -1)
  {
    // Set image background
    for (int y = 0; y < frame_height; ++y)
    {
      unsigned char* p = (unsigned char*)frame->GetImagePtr(0, y);
      memset(p, (64 * y) / frame_height, frame_width);
    }

    // Add some shapes
    EImageBW8 region_img(frame_width, frame_height);
    ERectangleRegion(EPoint(frame_width / 4.0f, frame_height / 4.0f), frame_width / 4.0f, frame_height / 4.0f).ToImage(region_img, 0, 64);
    EasyImage::Oper(EArithmeticLogicOperation_Add, &region_img, frame, frame);
    ERectangleRegion(EPoint(frame_width / 4.0f, 3.0f * frame_height / 4.0f), frame_width / 4.0f, frame_height / 5.0f, 30.f).ToImage(region_img, 0, 40);
    EasyImage::Oper(EArithmeticLogicOperation_Add, &region_img, frame, frame);
    ECircleRegion(EPoint(3 * frame_width / 4.0f, frame_height / 4.0f), frame_width / 8.0f).ToImage(region_img, 0, 80);
    EasyImage::Oper(EArithmeticLogicOperation_Add, &region_img, frame, frame);
    EEllipseRegion(EPoint(3 * frame_width / 4.0f, 3.0f * frame_height / 4.0f), frame_width / 10.0f, frame_width / 20.0f, -70.f).ToImage(region_img, 0, 100);
    EasyImage::Oper(EArithmeticLogicOperation_Add, &region_img, frame, frame);

    locked = true;
    frame_counter++;

    return frame_counter;
  }

  virtual void ReleaseFrame()
  {
    locked = false;
  }

  virtual void Stop()
  {
    started = false;
  }

  // Data retrieval

  virtual void GetDepthMap(Easy3D::EDepthMap& map)
  {
    if (!locked)
      throw(std::runtime_error("Frame not available"));

    if (map.GetType() != EImageType_Depth8)
      throw(std::runtime_error("Incompatible Depth Map format"));

    map.SetSize(frame->GetWidth(), frame->GetHeight());
    EasyImage::Copy(frame, &(dynamic_cast<Easy3D::EDepthMap8&>(map).AsEImage()));
  }

  virtual void GetPointCloud(Easy3D::EPointCloud& pc)
  {
    if (!locked)
      throw(std::runtime_error("Frame not available"));

    Easy3D::EDepthMap8 tmp_map;
    tmp_map.UseExternalBuffer(frame->GetWidth(), frame->GetHeight(), frame->GetImagePtr(), frame->GetRowPitch());

    Easy3D::EScaleCalibrationModel model(1.f, 1.f, 1.f);

    Easy3D::EDepthMapToPointCloudConverter converter;
    converter.SetCalibrationModel(model);
    converter.Convert(tmp_map, pc);
  }

  virtual void GetZMap(Easy3D::EZMap& zmap)
  {
    if (!locked)
      throw(std::runtime_error("Frame not available"));

    if (zmap.GetType() != EImageType_Depth8)
      throw(std::runtime_error("Incompatible ZMap format"));

    zmap.SetSize(frame->GetWidth(), frame->GetHeight());
    EasyImage::Copy(frame, &(dynamic_cast<Easy3D::EZMap8&>(zmap).AsEImage()));
  }

  // Parameter handling GUI

  virtual void InvokeParameterPanel()
  {
    DummyParamDialog param_dialog(frame_width, frame_height);

    if (param_dialog.DoModal() == IDOK)
    {
      frame_width = param_dialog.width;
      frame_height = param_dialog.height;
    }
  }

protected:
  // Device data
  EImageBW8* frame;

  // Grabbing state
  bool started;
  bool locked;
  long long int frame_counter;

  // Device user configuration
  int frame_width;
  int frame_height;
};
