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

  Program:   Visualization Toolkit
  Module:    vtkResliceImageViewerMeasurements.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 "vtkResliceImageViewerMeasurements.h"

#include "vtkAngleRepresentation.h"
#include "vtkAngleWidget.h"
#include "vtkBiDimensionalRepresentation.h"
#include "vtkBiDimensionalWidget.h"
#include "vtkCallbackCommand.h"
#include "vtkCamera.h"
#include "vtkCaptionRepresentation.h"
#include "vtkCaptionWidget.h"
#include "vtkCommand.h"
#include "vtkContourRepresentation.h"
#include "vtkContourWidget.h"
#include "vtkDistanceRepresentation.h"
#include "vtkDistanceWidget.h"
#include "vtkHandleRepresentation.h"
#include "vtkHandleWidget.h"
#include "vtkImageActor.h"
#include "vtkImageData.h"
#include "vtkObjectFactory.h"
#include "vtkPlane.h"
#include "vtkPointHandleRepresentation3D.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkResliceCursor.h"
#include "vtkResliceCursorActor.h"
#include "vtkResliceCursorLineRepresentation.h"
#include "vtkResliceCursorPolyDataAlgorithm.h"
#include "vtkResliceCursorWidget.h"
#include "vtkResliceImageViewer.h"
#include "vtkSeedRepresentation.h"
#include "vtkSeedWidget.h"
#include "vtkSmartPointer.h"

vtkStandardNewMacro(vtkResliceImageViewerMeasurements);

//------------------------------------------------------------------------------
vtkResliceImageViewerMeasurements::vtkResliceImageViewerMeasurements()
{
  this->ResliceImageViewer = nullptr;
  this->WidgetCollection = vtkCollection::New();

  // Setup event processing
  this->EventCallbackCommand = vtkCallbackCommand::New();
  this->EventCallbackCommand->SetClientData(this);
  this->EventCallbackCommand->SetCallback(vtkResliceImageViewerMeasurements::ProcessEventsHandler);

  this->ProcessEvents = 1;
  this->Tolerance = 6;
}

//------------------------------------------------------------------------------
vtkResliceImageViewerMeasurements::~vtkResliceImageViewerMeasurements()
{
  // Remove any added observers
  if (this->ResliceImageViewer)
  {
    this->ResliceImageViewer->GetResliceCursor()->RemoveObservers(
      vtkResliceCursorWidget::ResliceAxesChangedEvent, this->EventCallbackCommand);
  }

  this->WidgetCollection->Delete();
  this->EventCallbackCommand->Delete();
}

//------------------------------------------------------------------------------
void vtkResliceImageViewerMeasurements ::SetResliceImageViewer(vtkResliceImageViewer* i)
{
  // Weak reference. No need to delete
  this->ResliceImageViewer = i;

  if (i)
  {
    // Add the observer
    i->GetResliceCursor()->AddObserver(
      vtkResliceCursorWidget::ResliceAxesChangedEvent, this->EventCallbackCommand);
    i->GetResliceCursor()->AddObserver(
      vtkResliceCursorWidget::ResliceAxesChangedEvent, this->EventCallbackCommand);
  }
}

//------------------------------------------------------------------------------
void vtkResliceImageViewerMeasurements::Render()
{
  this->ResliceImageViewer->Render();
}

//------------------------------------------------------------------------------
void vtkResliceImageViewerMeasurements ::ProcessEventsHandler(
  vtkObject*, unsigned long, void* clientdata, void*)
{
  vtkResliceImageViewerMeasurements* self =
    reinterpret_cast<vtkResliceImageViewerMeasurements*>(clientdata);

  // if ProcessEvents is Off, we ignore all interaction events.
  if (!self->GetProcessEvents())
  {
    return;
  }

  self->Update();
}

//------------------------------------------------------------------------------
void vtkResliceImageViewerMeasurements::Update()
{
  if (this->ResliceImageViewer->GetResliceMode() != vtkResliceImageViewer::RESLICE_OBLIQUE)
  {
    return; // nothing to do.
  }

  const int nItems = this->WidgetCollection->GetNumberOfItems();
  for (int i = 0; i < nItems; i++)
  {
    vtkAbstractWidget* a =
      vtkAbstractWidget::SafeDownCast(this->WidgetCollection->GetItemAsObject(i));

    vtkSeedWidget* s = vtkSeedWidget::SafeDownCast(a);

    // seed is handled differently since its really a collection of several
    // markers which may exist on different planes.
    if (!s)
    {
      a->SetEnabled(this->IsItemOnReslicedPlane(a));
    }
  }
}

//------------------------------------------------------------------------------
bool vtkResliceImageViewerMeasurements ::IsItemOnReslicedPlane(vtkAbstractWidget* w)
{

  if (vtkDistanceWidget* dw = vtkDistanceWidget::SafeDownCast(w))
  {
    return this->IsWidgetOnReslicedPlane(dw);
  }
  if (vtkAngleWidget* aw = vtkAngleWidget::SafeDownCast(w))
  {
    return this->IsWidgetOnReslicedPlane(aw);
  }
  if (vtkBiDimensionalWidget* aw = vtkBiDimensionalWidget::SafeDownCast(w))
  {
    return this->IsWidgetOnReslicedPlane(aw);
  }
  if (vtkCaptionWidget* capw = vtkCaptionWidget::SafeDownCast(w))
  {
    return this->IsWidgetOnReslicedPlane(capw);
  }
  if (vtkContourWidget* capw = vtkContourWidget::SafeDownCast(w))
  {
    return this->IsWidgetOnReslicedPlane(capw);
  }
  if (vtkSeedWidget* s = vtkSeedWidget::SafeDownCast(w))
  {
    return this->IsWidgetOnReslicedPlane(s);
  }
  if (vtkHandleWidget* s = vtkHandleWidget::SafeDownCast(w))
  {
    return this->IsWidgetOnReslicedPlane(s);
  }

  return true;
}

//------------------------------------------------------------------------------
bool vtkResliceImageViewerMeasurements ::IsWidgetOnReslicedPlane(vtkDistanceWidget* w)
{
  if (w->GetWidgetState() != vtkDistanceWidget::Manipulate)
  {
    return true; // widget is not yet defined.
  }

  if (vtkDistanceRepresentation* rep =
        vtkDistanceRepresentation::SafeDownCast(w->GetRepresentation()))
  {
    return this->IsPointOnReslicedPlane(rep->GetPoint1Representation()) &&
      this->IsPointOnReslicedPlane(rep->GetPoint2Representation());
  }

  return true;
}

//------------------------------------------------------------------------------
bool vtkResliceImageViewerMeasurements ::IsWidgetOnReslicedPlane(vtkAngleWidget* w)
{
  if (w->GetWidgetState() != vtkAngleWidget::Manipulate)
  {
    return true; // widget is not yet defined.
  }

  if (vtkAngleRepresentation* rep = vtkAngleRepresentation::SafeDownCast(w->GetRepresentation()))
  {
    return this->IsPointOnReslicedPlane(rep->GetPoint1Representation()) &&
      this->IsPointOnReslicedPlane(rep->GetPoint2Representation()) &&
      this->IsPointOnReslicedPlane(rep->GetCenterRepresentation());
  }

  return true;
}

//------------------------------------------------------------------------------
bool vtkResliceImageViewerMeasurements ::IsWidgetOnReslicedPlane(vtkBiDimensionalWidget* w)
{
  if (w->GetWidgetState() != vtkBiDimensionalWidget::Manipulate)
  {
    return true; // widget is not yet defined.
  }

  if (vtkBiDimensionalRepresentation* rep =
        vtkBiDimensionalRepresentation::SafeDownCast(w->GetRepresentation()))
  {
    return this->IsPointOnReslicedPlane(rep->GetPoint1Representation()) &&
      this->IsPointOnReslicedPlane(rep->GetPoint2Representation()) &&
      this->IsPointOnReslicedPlane(rep->GetPoint3Representation()) &&
      this->IsPointOnReslicedPlane(rep->GetPoint4Representation());
  }

  return true;
}

//------------------------------------------------------------------------------
bool vtkResliceImageViewerMeasurements ::IsWidgetOnReslicedPlane(vtkHandleWidget* w)
{
  return this->IsPointOnReslicedPlane(w->GetHandleRepresentation());
}

//------------------------------------------------------------------------------
bool vtkResliceImageViewerMeasurements ::IsWidgetOnReslicedPlane(vtkCaptionWidget* w)
{
  if (vtkCaptionRepresentation* rep =
        vtkCaptionRepresentation::SafeDownCast(w->GetRepresentation()))
  {
    return this->IsPointOnReslicedPlane(rep->GetAnchorRepresentation());
  }

  return true;
}

//------------------------------------------------------------------------------
bool vtkResliceImageViewerMeasurements ::IsWidgetOnReslicedPlane(vtkContourWidget* w)
{
  if (w->GetWidgetState() != vtkContourWidget::Manipulate)
  {
    return true; // widget is not yet defined.
  }

  if (vtkContourRepresentation* rep =
        vtkContourRepresentation::SafeDownCast(w->GetRepresentation()))
  {
    const int nNodes = rep->GetNumberOfNodes();
    for (int i = 0; i < nNodes; i++)
    {
      double p[3];
      rep->GetNthNodeWorldPosition(i, p);
      if (this->IsPositionOnReslicedPlane(p) == false)
      {
        return false;
      }
    }
  }

  return true;
}

//------------------------------------------------------------------------------
bool vtkResliceImageViewerMeasurements ::IsWidgetOnReslicedPlane(vtkSeedWidget* w)
{
  if (vtkSeedRepresentation* rep = vtkSeedRepresentation::SafeDownCast(w->GetRepresentation()))
  {
    const int nNodes = rep->GetNumberOfSeeds();
    for (int i = 0; i < nNodes; i++)
    {
      w->GetSeed(i)->GetHandleRepresentation()->SetVisibility(
        w->GetEnabled() && this->IsPointOnReslicedPlane(w->GetSeed(i)->GetHandleRepresentation()));
    }
  }

  return true;
}

//------------------------------------------------------------------------------
bool vtkResliceImageViewerMeasurements ::IsPointOnReslicedPlane(vtkHandleRepresentation* h)
{
  double pos[3];
  h->GetWorldPosition(pos);
  return this->IsPositionOnReslicedPlane(pos);
}

//------------------------------------------------------------------------------
bool vtkResliceImageViewerMeasurements ::IsPositionOnReslicedPlane(double p[3])
{
  if (vtkResliceCursorRepresentation* rep = vtkResliceCursorRepresentation::SafeDownCast(
        this->ResliceImageViewer->GetResliceCursorWidget()->GetRepresentation()))
  {
    const int planeOrientation = rep->GetCursorAlgorithm()->GetReslicePlaneNormal();
    vtkPlane* plane = this->ResliceImageViewer->GetResliceCursor()->GetPlane(planeOrientation);
    const double d = plane->DistanceToPlane(p);
    return (d < this->Tolerance);
  }

  return true;
}

//------------------------------------------------------------------------------
void vtkResliceImageViewerMeasurements::AddItem(vtkAbstractWidget* w)
{
  this->WidgetCollection->AddItem(w);
}

//------------------------------------------------------------------------------
void vtkResliceImageViewerMeasurements::RemoveItem(vtkAbstractWidget* w)
{
  this->WidgetCollection->RemoveItem(w);
}

//------------------------------------------------------------------------------
void vtkResliceImageViewerMeasurements::RemoveAllItems()
{
  this->WidgetCollection->RemoveAllItems();
}

//------------------------------------------------------------------------------
void vtkResliceImageViewerMeasurements::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);

  os << indent << "ResliceImageViewer: " << this->ResliceImageViewer << "\n";
  os << indent << "WidgetCollection: " << this->WidgetCollection << endl;
  this->WidgetCollection->PrintSelf(os, indent.GetNextIndent());

  os << indent << "ProcessEvents: " << (this->ProcessEvents ? "On" : "Off") << "\n";

  os << indent << "Tolerance: " << this->Tolerance << endl;
}
