// +-------------------------------- 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 "../Common/stdafx.h"
#include "C3DView.h"
#include <algorithm>


BEGIN_MESSAGE_MAP(C3DView, CStatic)
  ON_WM_PAINT()
END_MESSAGE_MAP()

C3DView::C3DView(): m_viewer(NULL), m_first_time(true), m_show_object_volume(true), m_show_object_dimensions(true),
m_show_object_center(true), m_color_3Dobject_info(255, 50, 50), m_object_info_sel(0), m_object_selected(0),
m_object_info_label_id(-1)
{
}

C3DView::~C3DView()
{
  delete m_viewer;
}

void C3DView::OnPaint()
{
  if (IsIconic())
    return;

  // Get window client area
  CRect clientRect;
  GetWindowRect(&clientRect);

  // initialize the 3D viewer if necessary
  if (m_viewer == NULL)
  {
    viewerRect_ = clientRect;

    try
    {
      // Create new Point Cloud Viewer
      m_viewer = new Easy3D::E3DViewer(0, 0, viewerRect_.Width(), viewerRect_.Height(), (void*)GetSafeHwnd());
      m_viewer->InitRendering();
      m_viewer->SetBackgroundColor(ERGBColor(220, 220, 220));
    }
    catch (EException e)
    {
      ::MessageBoxA(NULL, e.what(), "Error", MB_OK | MB_ICONERROR | MB_DEFAULT_DESKTOP_ONLY);
    }
  }
  else
  {
    if (viewerRect_ != clientRect)
    {
      // Window size has changed, resize the point cloud viewer accordingly
      viewerRect_ = clientRect;
      m_viewer->SetPosition(0, 0, viewerRect_.Width(), viewerRect_.Height());
    }
  }
  m_viewer->Show();

  CStatic::OnPaint();
}

void C3DView::SetPointCloud(const Easy3D::EPointCloud& pc)
{
  if (m_viewer != NULL)
  {
    if (pc.GetNumPoints() > 0)
    {
      m_viewer->ConfigureRenderSource(pc, !m_first_time);

      // If the pointcloud has colors, use them for the display. Otherwise, generate colors from Z.
      std::vector<int> initializedAttributes;
      pc.GetInitializedAttributes(initializedAttributes);
      if (std::find(initializedAttributes.begin(), initializedAttributes.end(), Easy3D::E3DAttribute_Color) == initializedAttributes.end())
        m_viewer->SetColorRampMode(Easy3D::EColorRampMode_HueFromZ);
      m_first_time = false;
    }
  }

  // Clear object list
  m_ObjList.clear();
  m_viewer->RemoveCurrent3DObjects();
  m_viewer->ClearTextLabels();
  m_object_info_label_id = -1;

  Easy3D::EPointCloudStatistics::GetPointCloudBounds(pc, m_cloudXRange, m_cloudYRange, m_cloudZRange);

  // Call for repaint
  Invalidate();
}

void C3DView::SetObjectList(const std::vector<Easy3D::E3DObject>& ObjList)
{
  m_ObjList = ObjList;

  m_viewer->RemoveCurrent3DObjects();
  m_viewer->Register3DObjects(ObjList);

  Easy3D::ERenderStyle style;
  style.hasFill = false;
  style.hasLine = true;
  style.lineRGB = EC24A(255, 255, 0);
  m_viewer->SetFeatureStyleForAll3DObjects(style, Easy3D::E3DObjectFeature_BoundingBox);
  m_viewer->ShowFeatureForAll3DObjects(Easy3D::E3DObjectFeature_BoundingBox);

  m_object_selected = 0;
  UpdateObjectInformation();

  Invalidate();
}

void C3DView::UpdateObjectInformation(int selection)
{
  m_object_info_sel = selection;
  UpdateObjectInformation();
}

void C3DView::UpdateObjectInformation()
{
  // Remove previous label
  if (m_object_info_label_id != -1)
  {
    m_viewer->RemoveTextLabel(m_object_info_label_id);
    m_object_info_label_id = -1;
  }

  if (m_object_info_sel != 0 && m_object_selected < (int)m_ObjList.size())
  {
    Easy3D::E3DObject obj = m_ObjList[m_object_selected];
    Easy3D::E3DPoint avg = obj.GetAveragePosition();

    char charBuffer[64];
    int nbSignificantBit = 4;

    if (m_object_info_sel == 4) // Volume
    {
      float volume = obj.GetVolume();
      sprintf_s(charBuffer, "Volume\n%.0f", volume);
    }
    else if (m_object_info_sel == 2) // Dimensions
    {
      float height = obj.GetLocalHeight();
      float width = obj.GetWidth();
      float length = obj.GetLength();
      sprintf_s(charBuffer, "Dimensions\n%.0fx%.0fx%.0f\n", length, width, height);
    }
    else if (m_object_info_sel == 1) // Center
    {
      sprintf_s(charBuffer, "Center\n%.0f,%.0f,%.0f", avg.X, avg.Y, avg.Z);
    }
    else if (m_object_info_sel == 3) // Area
    {
      float area = obj.GetArea();
      sprintf_s(charBuffer, "Area\n%.0f", area);
    }

    m_object_info_label_id = m_viewer->AddTextLabel(
      Easy3D::TextLabel(avg, m_color_3Dobject_info, 0.06f, charBuffer));
  }

  Invalidate();
}

void C3DView::UpdateObjectSelected(int diff)
{
  if (m_ObjList.size() > 0)
  {
    // diff is in {-1, 1}, make sure that the argument of the operation % is positive by adding m_ObjList.size()
    m_object_selected = (m_object_selected + diff + int(m_ObjList.size())) % int(m_ObjList.size());
  }

  UpdateObjectInformation();
}

BOOL C3DView::PreTranslateMessage(MSG* pMsg)
{
  if (pMsg->message == WM_KEYDOWN)
  {
    if (pMsg->wParam == VK_LEFT || pMsg->wParam == 'P')
    {
      UpdateObjectSelected(-1);
    }
    else if (pMsg->wParam == VK_RIGHT || pMsg->wParam == 'N')
    {
      UpdateObjectSelected(1);
    }
  }
  return CStatic::PreTranslateMessage(pMsg);
}
