/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkScenePicker.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkScenePicker.h"

#include "vtkCommand.h"
#include "vtkDataObject.h"
#include "vtkHardwareSelector.h"
#include "vtkObjectFactory.h"
#include "vtkProp.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"

class vtkScenePickerSelectionRenderCommand : public vtkCommand
{
public:
  vtkScenePicker* m_Picker;
  static vtkScenePickerSelectionRenderCommand* New()
  {
    return new vtkScenePickerSelectionRenderCommand;
  }

  void Execute(vtkObject* vtkNotUsed(o), unsigned long event, void*) override
  {
    if (event == vtkCommand::StartInteractionEvent)
    {
      this->InteractiveRender = true;
    }
    else if (event == vtkCommand::EndInteractionEvent)
    {
      this->InteractiveRender = false;
    }
    else if (event == vtkCommand::EndEvent)
    {
      if (!this->InteractiveRender)
      {
        m_Picker->PickRender();
      }
      this->m_Picker->SetRenderer(this->m_Picker->Renderer);
    }
  }

protected:
  vtkScenePickerSelectionRenderCommand()
    : InteractiveRender(false)
  {
  }
  ~vtkScenePickerSelectionRenderCommand() override = default;
  bool InteractiveRender;
};

vtkStandardNewMacro(vtkScenePicker);

//------------------------------------------------------------------------------
vtkScenePicker::vtkScenePicker()
{
  this->EnableVertexPicking = 1;
  this->Renderer = nullptr;
  this->Interactor = nullptr;
  this->Selector = vtkHardwareSelector::New();
  this->NeedToUpdate = false;
  this->VertId = -1;
  this->CellId = -1;
  this->Prop = nullptr;
  this->SelectionRenderCommand = vtkScenePickerSelectionRenderCommand::New();
  this->SelectionRenderCommand->m_Picker = this;
}

//------------------------------------------------------------------------------
vtkScenePicker::~vtkScenePicker()
{
  this->SetRenderer(nullptr);
  this->Selector->Delete();
  this->SelectionRenderCommand->Delete();
}

//------------------------------------------------------------------------------
void vtkScenePicker::SetRenderer(vtkRenderer* r)
{
  vtkRenderWindowInteractor* rwi = nullptr;
  if (r && r->GetRenderWindow())
  {
    rwi = r->GetRenderWindow()->GetInteractor();
  }
  this->SetInteractor(rwi);

  if (this->Renderer == r)
  {
    return;
  }
  if (r && !r->GetRenderWindow())
  {
    vtkErrorMacro(<< "Renderer: " << this->Renderer << " does not have its render window set.");
    return;
  }

  if (this->Renderer)
  {
    this->Renderer->GetRenderWindow()->RemoveObserver(this->SelectionRenderCommand);
  }

  vtkSetObjectBodyMacro(Renderer, vtkRenderer, r);

  if (this->Renderer)
  {
    this->Renderer->GetRenderWindow()->AddObserver(
      vtkCommand::EndEvent, this->SelectionRenderCommand, 0.01);
  }

  this->Selector->SetRenderer(this->Renderer);
}

//------------------------------------------------------------------------------
void vtkScenePicker::SetInteractor(vtkRenderWindowInteractor* rwi)
{
  if (this->Interactor == rwi)
  {
    return;
  }
  if (this->Interactor)
  {
    this->Interactor->RemoveObserver(this->SelectionRenderCommand);
  }

  vtkSetObjectBodyMacro(Interactor, vtkRenderWindowInteractor, rwi);

  if (this->Interactor)
  {
    this->Interactor->AddObserver(
      vtkCommand::StartInteractionEvent, this->SelectionRenderCommand, 0.01);
    this->Interactor->AddObserver(
      vtkCommand::EndInteractionEvent, this->SelectionRenderCommand, 0.01);
  }
}

//------------------------------------------------------------------------------
// Do a selection render.. for caching object selection stuff.
// This is used for Object selection . We have to perform
// "select" and "mouse over" and "mouse out" as the mouse moves around the
// scene (or the mouse is clicked in the case of "select"). I do not want
// to do a conventional pick for this function because it's too darn slow.
// The Selector will be used here to pick-render the entire
// screen, store on a buffer the colored cells and read back as
// the mouse moves around the moused pick. This extra render from the
// Selector will be done only if the camera isn't in motion,
// otherwise motion would be too frickin slow.
//
void vtkScenePicker::PickRender()
{
  if (!this->Renderer || !this->Renderer->GetRenderWindow())
  {
    return;
  }

  double vp[4];
  this->Renderer->GetViewport(vp);
  int size[2] = { this->Renderer->GetRenderWindow()->GetSize()[0],
    this->Renderer->GetRenderWindow()->GetSize()[1] };

  int rx1 = static_cast<int>(vp[0] * (size[0] - 1));
  int ry1 = static_cast<int>(vp[1] * (size[1] - 1));
  int rx2 = static_cast<int>(vp[2] * (size[0] - 1));
  int ry2 = static_cast<int>(vp[3] * (size[1] - 1));

  this->PickRender(rx1, ry1, rx2, ry2);
}

//------------------------------------------------------------------------------
// Do a selection render.. for caching object selection stuff.
void vtkScenePicker::PickRender(int x0, int y0, int x1, int y1)
{
  this->Renderer->GetRenderWindow()->RemoveObserver(this->SelectionRenderCommand);

  if (this->EnableVertexPicking)
  {
    this->Selector->SetFieldAssociation(vtkDataObject::FIELD_ASSOCIATION_POINTS);
  }
  else
  {
    this->Selector->SetFieldAssociation(vtkDataObject::FIELD_ASSOCIATION_CELLS);
  }
  cout << "Area: " << x0 << ", " << y0 << ", " << x1 << ", " << y1 << endl;
  this->Selector->SetArea(x0, y0, x1, y1);
  if (!this->Selector->CaptureBuffers())
  {
    vtkErrorMacro("Failed to capture buffers.");
  }
  this->NeedToUpdate = true;
  this->PickRenderTime.Modified();
  this->Renderer->GetRenderWindow()->AddObserver(
    vtkCommand::EndEvent, this->SelectionRenderCommand, 0.01);
}

//------------------------------------------------------------------------------
vtkIdType vtkScenePicker::GetCellId(int displayPos[2])
{
  if (this->EnableVertexPicking)
  {
    return -1;
  }
  this->Update(displayPos);
  return this->CellId;
}

//------------------------------------------------------------------------------
vtkProp* vtkScenePicker::GetViewProp(int displayPos[2])
{
  this->Update(displayPos);
  return this->Prop;
}

//------------------------------------------------------------------------------
vtkIdType vtkScenePicker::GetVertexId(int displayPos[2])
{
  if (!this->EnableVertexPicking)
  {
    return -1;
  }
  this->Update(displayPos);
  return this->CellId;
}

//------------------------------------------------------------------------------
void vtkScenePicker::Update(int displayPos[2])
{
  if (this->PickRenderTime <= this->GetMTime())
  {
    this->PickRender();
  }

  if (this->NeedToUpdate || this->LastQueriedDisplayPos[0] != displayPos[0] ||
    this->LastQueriedDisplayPos[1] != displayPos[1])
  {
    this->Prop = nullptr;
    unsigned int dpos[2] = { 0, 0 };
    if (displayPos[0] >= 0 && displayPos[1] >= 0)
    {
      dpos[0] = static_cast<unsigned int>(displayPos[0]);
      dpos[1] = static_cast<unsigned int>(displayPos[1]);
      vtkHardwareSelector::PixelInformation info = this->Selector->GetPixelInformation(dpos);
      this->CellId = info.AttributeID;
      this->Prop = info.Prop;
    }
    this->LastQueriedDisplayPos[0] = displayPos[0];
    this->LastQueriedDisplayPos[1] = displayPos[1];
    this->NeedToUpdate = false;
  }
}

//------------------------------------------------------------------------------
void vtkScenePicker::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "Renderer: " << this->Renderer << endl;
  os << indent << "EnableVertexPicking: " << this->EnableVertexPicking << endl;
}
