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

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/VolViewCopyright.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 "vtkKWCursorWidget.h"
#include "vtkObjectFactory.h"
#include "vtkActor2D.h"
#include "vtkCallbackCommand.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkImageData.h"
#include "vtkLineSource.h"
#include "vtkPolyDataMapper2D.h"
#include "vtkCoordinate.h"
#include "vtkProperty2D.h"
#include "vtkCamera.h"
#include "vtkKWEvent.h"
#include "vtkKW2DRenderWidget.h"

vtkCxxRevisionMacro(vtkKWCursorWidget, "$Revision: 1.27 $");
vtkStandardNewMacro(vtkKWCursorWidget);

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLKWCursorWidgetReader.h"
#include "XML/vtkXMLKWCursorWidgetWriter.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtkKWCursorWidget, vtkXMLKWCursorWidgetReader, vtkXMLKWCursorWidgetWriter);

//----------------------------------------------------------------------------
vtkKWCursorWidget::vtkKWCursorWidget()
{
  this->EventCallbackCommand->SetCallback(vtkKWCursorWidget::ProcessEvents);
  
  this->Axis1Source = vtkLineSource::New();
  this->Axis2Source = vtkLineSource::New();
  this->Axis1Actor = vtkActor2D::New();
  this->Axis2Actor = vtkActor2D::New();
  
  vtkPolyDataMapper2D *pdm = vtkPolyDataMapper2D::New();
  vtkCoordinate *tcoord = vtkCoordinate::New();
  tcoord->SetCoordinateSystemToWorld();
  pdm->SetTransformCoordinate(tcoord);
  tcoord->Delete();
  this->Axis1Actor->SetMapper(pdm);
  this->Axis1Actor->GetProperty()->SetColor(1, 1, 1);
  this->Axis1Actor->GetProperty()->SetLineWidth(2.0);
  pdm->SetInput(this->Axis1Source->GetOutput());
  pdm->Delete();
  
  pdm = vtkPolyDataMapper2D::New();
  tcoord = vtkCoordinate::New();
  tcoord->SetCoordinateSystemToWorld();
  pdm->SetTransformCoordinate(tcoord);
  tcoord->Delete();
  this->Axis2Actor->SetMapper(pdm);
  this->Axis2Actor->GetProperty()->SetColor(1, 1, 1);
  this->Axis2Actor->GetProperty()->SetLineWidth(
    this->Axis1Actor->GetProperty()->GetLineWidth());
  pdm->SetInput(this->Axis2Source->GetOutput());
  pdm->Delete();
  
  this->SliceOrientation = vtkKW2DRenderWidget::SLICE_ORIENTATION_XY;
  this->MouseCursorState = vtkKWCursorWidget::NeitherAxis;
  this->Position[0] = this->Position[1] = this->Position[2] = 0.0;
  this->Moving = 0;
  
  this->Interactive = 0;
}

//----------------------------------------------------------------------------
vtkKWCursorWidget::~vtkKWCursorWidget()
{
  this->Axis1Source->Delete();
  this->Axis2Source->Delete();
  this->Axis1Actor->Delete();
  this->Axis2Actor->Delete();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::SetSliceOrientation(int arg)
{
  if (this->SliceOrientation == arg)
    {
    return;
    }

  this->SliceOrientation = arg;
  this->Modified();

  this->UpdatePosition();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::SetPosition(double x, double y, double z)
{
  if (this->Position[0] == x &&
      this->Position[1] == y &&
      this->Position[2] == z)
    {
    return;
    }

  this->Position[0] = x;
  this->Position[1] = y;
  this->Position[2] = z;

  this->UpdatePosition();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::PlaceWidget(double bounds[6])
{
  this->Superclass::PlaceWidget(bounds);

  this->UpdatePosition();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::UpdatePosition()
{
  if (this->Position[0] < this->InitialBounds[0])
    {
    this->Position[0] = this->InitialBounds[0];
    }
  else if (this->Position[0] > this->InitialBounds[1])
    {
    this->Position[0] = this->InitialBounds[1];
    }
  if (this->Position[1] < this->InitialBounds[2])
    {
    this->Position[1] = this->InitialBounds[2];
    }
  else if (this->Position[1] > this->InitialBounds[3])
    {
    this->Position[1] = this->InitialBounds[3];
    }
  if (this->Position[2] < this->InitialBounds[4])
    {
    this->Position[2] = this->InitialBounds[4];
    }
  else if (this->Position[2] > this->InitialBounds[5])
    {
    this->Position[2] = this->InitialBounds[5];
    }
  
  switch (this->SliceOrientation)
    {
    case vtkKW2DRenderWidget::SLICE_ORIENTATION_YZ:
      this->Axis1Source->SetPoint1(
        this->Position[0], this->Position[1], this->InitialBounds[4]);
      this->Axis1Source->SetPoint2(
        this->Position[0], this->Position[1], this->InitialBounds[5]);
      this->Axis2Source->SetPoint1(
        this->Position[0], this->InitialBounds[2], this->Position[2]);
      this->Axis2Source->SetPoint2(
        this->Position[0], this->InitialBounds[3], this->Position[2]);
      break;
    case vtkKW2DRenderWidget::SLICE_ORIENTATION_XZ:
      this->Axis1Source->SetPoint1(
        this->Position[0], this->Position[1], this->InitialBounds[4]);
      this->Axis1Source->SetPoint2(
        this->Position[0], this->Position[1], this->InitialBounds[5]);
      this->Axis2Source->SetPoint1(
        this->InitialBounds[0], this->Position[1], this->Position[2]);
      this->Axis2Source->SetPoint2(
        this->InitialBounds[1], this->Position[1], this->Position[2]);
      break;
    case vtkKW2DRenderWidget::SLICE_ORIENTATION_XY:
      this->Axis1Source->SetPoint1(
        this->Position[0], this->InitialBounds[2], this->Position[2]);
      this->Axis1Source->SetPoint2(
        this->Position[0], this->InitialBounds[3], this->Position[2]);
      this->Axis2Source->SetPoint1(
        this->InitialBounds[0], this->Position[1], this->Position[2]);
      this->Axis2Source->SetPoint2(
        this->InitialBounds[1], this->Position[1], this->Position[2]);
      break;
    }
}

//----------------------------------------------------------------------------
int vtkKWCursorWidget::IsPositionInBounds(double bounds[6])
{
  return (bounds && 
          this->Position &&
          this->Position[0] >= bounds[0] &&
          this->Position[0] <= bounds[1] &&
          this->Position[1] >= bounds[2] &&
          this->Position[1] <= bounds[3] &&
          this->Position[2] >= bounds[4] &&
          this->Position[2] <= bounds[5]);
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::SetEnabled(int enabling)
{
  if ( ! this->Interactor )
    {
    vtkErrorMacro(<<"The interactor must be set prior to enabling/disabling widget");
    return;
    }
  
  if ( enabling ) 
    {
    vtkDebugMacro(<<"Enabling cursor widget");
    if ( this->Enabled ) //already enabled, just return
      {
      return;
      }
    
    this->SetCurrentRenderer(
      this->Interactor->FindPokedRenderer(
        this->Interactor->GetLastEventPosition()[0],
        this->Interactor->GetLastEventPosition()[1]));
    if (this->CurrentRenderer == NULL)
      {
      return;
      }
    
    this->Enabled = 1;
    
    // listen for the following events
    vtkRenderWindowInteractor *i = this->Interactor;
    i->AddObserver(vtkCommand::MouseMoveEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::LeftButtonPressEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::LeftButtonReleaseEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::MiddleButtonPressEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::MiddleButtonReleaseEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::RightButtonPressEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::RightButtonReleaseEvent, 
                   this->EventCallbackCommand, this->Priority);

    // Add the cursor
    this->CurrentRenderer->AddViewProp(this->Axis1Actor);
    this->CurrentRenderer->AddViewProp(this->Axis2Actor);
    this->InvokeEvent(vtkCommand::EnableEvent,NULL);
    }
  else //disabling------------------------------------------
    {
    vtkDebugMacro(<<"Disabling cursor widget");
    if ( ! this->Enabled ) //already disabled, just return
      {
      return;
      }
    this->Enabled = 0;

    // don't listen for events any more
    this->Interactor->RemoveObserver(this->EventCallbackCommand);

    // turn off the cursor
    if (this->CurrentRenderer)
      {
      this->CurrentRenderer->RemoveActor(this->Axis1Actor);
      this->CurrentRenderer->RemoveActor(this->Axis2Actor);
      }
    this->InvokeEvent(vtkCommand::DisableEvent,NULL);
    }
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::ProcessEvents(vtkObject* vtkNotUsed(object),
                                      unsigned long event,
                                      void* clientdata,
                                      void* vtkNotUsed(calldata))
{
  vtkKWCursorWidget* self = reinterpret_cast<vtkKWCursorWidget *>(clientdata);
  
  switch (event)
    {
    case vtkCommand::LeftButtonPressEvent:
    case vtkCommand::MiddleButtonPressEvent:
    case vtkCommand::RightButtonPressEvent:
      self->OnButtonPress();
      break;
    case vtkCommand::MouseMoveEvent:
      self->OnMouseMove();
      break;
    case vtkCommand::LeftButtonReleaseEvent:
    case vtkCommand::MiddleButtonReleaseEvent:
    case vtkCommand::RightButtonReleaseEvent:
      self->OnButtonRelease();
      break;
    }
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::OnButtonPress()
{
  if (this->MouseCursorState == vtkKWCursorWidget::NeitherAxis)
    {
    return;
    }
  
  this->Moving = 1;
  this->EventCallbackCommand->SetAbortFlag(1);
  this->StartInteraction();
  this->InvokeEvent(vtkCommand::StartInteractionEvent, NULL);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::OnButtonRelease()
{
  if (this->MouseCursorState == vtkKWCursorWidget::NeitherAxis)
    {
    return;
    }
  
  switch (this->MouseCursorState)
    {
    case vtkKWCursorWidget::HorizontalAxis:
      this->MoveCursorHorizontalAxis(0);
      break;
    case vtkKWCursorWidget::VerticalAxis:
      this->MoveCursorVerticalAxis(0);
      break;
    case vtkKWCursorWidget::BothAxes:
      this->MoveCursorBothAxes(0);
      break;
    }

  this->Moving = 0;
  this->EventCallbackCommand->SetAbortFlag(1);
  this->EndInteraction();
  this->InvokeEvent(vtkCommand::EndInteractionEvent, NULL);

  this->MouseCursorState = vtkKWCursorWidget::NeitherAxis;
  this->SetMouseCursor(this->MouseCursorState);

  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::OnMouseMove()
{
  if (this->Moving)
    {
    switch (this->MouseCursorState)
      {
      case vtkKWCursorWidget::HorizontalAxis:
        this->MoveCursorHorizontalAxis(1);
        break;
      case vtkKWCursorWidget::VerticalAxis:
        this->MoveCursorVerticalAxis(1);
        break;
      case vtkKWCursorWidget::BothAxes:
        this->MoveCursorBothAxes(1);
        break;
      }
    this->UpdateCursorIcon();
    this->EventCallbackCommand->SetAbortFlag(1);
    this->InvokeEvent(vtkCommand::InteractionEvent, NULL);
    this->Interactor->Render();
    }
  else
    {
    this->UpdateCursorIcon();
    }
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::MoveCursorHorizontalAxis(int changing)
{
  double newCenter[3];
  
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];

  if ( ! this->ComputeWorldCoordinate(x, y, newCenter))
    {
    return;
    }

  switch (this->SliceOrientation)
    {
    case 0:
      newCenter[1] = this->Position[1];
      break;
    case 1:
    case 2:
      newCenter[0] = this->Position[0];
      break;
    }
  
  if (changing)
    {
    if (this->Interactive)
      {
      float fargs[3];
      fargs[0] = newCenter[0];
      fargs[1] = newCenter[1];
      fargs[2] = newCenter[2];
      this->InvokeEvent(vtkKWEvent::Cursor3DPositionChangingEvent, fargs);
      }
    else
      {
      this->SetPosition(newCenter);
      }
    }
  else
    {
    float fargs[3];
    fargs[0] = newCenter[0];
    fargs[1] = newCenter[1];
    fargs[2] = newCenter[2];
    this->InvokeEvent(vtkKWEvent::Cursor3DPositionChangedEvent, fargs);
    }
  
  this->EventCallbackCommand->SetAbortFlag(1);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::MoveCursorVerticalAxis(int changing)
{
  double newCenter[3];
  
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];

  if ( ! this->ComputeWorldCoordinate(x, y, newCenter))
    {
    return;
    }
  
  switch (this->SliceOrientation)
    {
    case 0:
    case 1:
      newCenter[2] = this->Position[2];
      break;
    case 2:
      newCenter[1] = this->Position[1];
      break;
    }
  
  if (changing)
    {
    if (this->Interactive)
      {
      float fargs[3];
      fargs[0] = newCenter[0];
      fargs[1] = newCenter[1];
      fargs[2] = newCenter[2];
      this->InvokeEvent(vtkKWEvent::Cursor3DPositionChangingEvent, fargs);
      }
    else
      {
      this->SetPosition(newCenter);
      }
    }
  else
    {
    float fargs[3];
    fargs[0] = newCenter[0];
    fargs[1] = newCenter[1];
    fargs[2] = newCenter[2];
    this->InvokeEvent(vtkKWEvent::Cursor3DPositionChangedEvent, fargs);
    }
  
  this->EventCallbackCommand->SetAbortFlag(1);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::MoveCursorBothAxes(int changing)
{
  double newCenter[3];
  
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];
  
  if ( ! this->ComputeWorldCoordinate(x, y, newCenter))
    {
    return;
    }

  if (changing)
    {
    if (this->Interactive)
      {
      float fargs[3];
      fargs[0] = newCenter[0];
      fargs[1] = newCenter[1];
      fargs[2] = newCenter[2];
      this->InvokeEvent(vtkKWEvent::Cursor3DPositionChangingEvent, fargs);
      }
    else
      {
      this->SetPosition(newCenter);
      }
    }
  else
    {
    float fargs[3];
    fargs[0] = newCenter[0];
    fargs[1] = newCenter[1];
    fargs[2] = newCenter[2];
    this->InvokeEvent(vtkKWEvent::Cursor3DPositionChangedEvent, fargs);
    }
  
  this->EventCallbackCommand->SetAbortFlag(1);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::UpdateCursorIcon()
{
  if ( ! this->Enabled )
    {
    this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_DEFAULT);
    return;
    }
  
  if (!this->CurrentRenderer)
    {
    return;
    }

  if (this->Moving)
    {
    return;
    }

  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];

  double axisX = 0.0;
  double axisY = 0.0;
  
  switch (this->SliceOrientation)
    {
    case 0:
      this->CurrentRenderer->SetWorldPoint(
        this->Position[0], this->Position[1], this->InitialBounds[4], 1);
      this->CurrentRenderer->WorldToDisplay();
      axisX = this->CurrentRenderer->GetDisplayPoint()[0];
      this->CurrentRenderer->SetWorldPoint(
        this->Position[0], this->InitialBounds[2], this->Position[2], 1);
      this->CurrentRenderer->WorldToDisplay();
      axisY = this->CurrentRenderer->GetDisplayPoint()[1];
      break;
    case 1:
      this->CurrentRenderer->SetWorldPoint(
        this->Position[0], this->Position[1], this->InitialBounds[4], 1);
      this->CurrentRenderer->WorldToDisplay();
      axisX = this->CurrentRenderer->GetDisplayPoint()[0];
      this->CurrentRenderer->SetWorldPoint(
        this->InitialBounds[0], this->Position[1], this->Position[2], 1);
      this->CurrentRenderer->WorldToDisplay();
      axisY = this->CurrentRenderer->GetDisplayPoint()[1];
      break;
    case 2:
      this->CurrentRenderer->SetWorldPoint(
        this->Position[0], this->InitialBounds[2], this->Position[2], 1);
      this->CurrentRenderer->WorldToDisplay();
      axisX = this->CurrentRenderer->GetDisplayPoint()[0];
      this->CurrentRenderer->SetWorldPoint(
        this->InitialBounds[0], this->Position[1], this->Position[2], 1);
      this->CurrentRenderer->WorldToDisplay();
      axisY = this->CurrentRenderer->GetDisplayPoint()[1];
      break;
    }
  
  double xDist = fabs(x - axisX);
  double yDist = fabs(y - axisY);
  
  int pState = this->MouseCursorState;

  if (xDist < 3)
    {
    if (yDist < 3)
      {
      this->MouseCursorState = vtkKWCursorWidget::BothAxes;
      }
    else
      {
      this->MouseCursorState = vtkKWCursorWidget::VerticalAxis;
      }
    }
  else if (yDist < 3)
    {
    this->MouseCursorState = vtkKWCursorWidget::HorizontalAxis;
    }
  else
    {
    this->MouseCursorState = vtkKWCursorWidget::NeitherAxis;
    }
  
  if (pState == this->MouseCursorState)
    {
    return;
    }
  
  this->SetMouseCursor(this->MouseCursorState);
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::SetMouseCursor(int state)
{
  switch (state)
    {
    case vtkKWCursorWidget::BothAxes:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZEALL);
      break;
    case vtkKWCursorWidget::VerticalAxis:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZEWE);
      break;
    case vtkKWCursorWidget::HorizontalAxis:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZENS);
      break;
    case vtkKWCursorWidget::NeitherAxis:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_DEFAULT);
      break;
    }
}

//----------------------------------------------------------------------------
int vtkKWCursorWidget::ComputeWorldCoordinate(int x, int y, double* coord)
{
  if (!this->CurrentRenderer)
    {
    return 0;
    }

  this->CurrentRenderer->SetWorldPoint(
    this->InitialBounds[0], 
    this->InitialBounds[2], 
    this->InitialBounds[4], 
    1.0);

  this->CurrentRenderer->WorldToDisplay();
  double *dispPoint = this->CurrentRenderer->GetDisplayPoint();

  this->CurrentRenderer->SetDisplayPoint(x, y, dispPoint[2]);
  this->CurrentRenderer->DisplayToWorld();
  double *worldPoint = this->CurrentRenderer->GetWorldPoint();
  if (worldPoint[3] != 0.0)
    {
    worldPoint[0] = (double)((double)worldPoint[0] / (double)worldPoint[3]);
    worldPoint[1] = (double)((double)worldPoint[1] / (double)worldPoint[3]);
    worldPoint[2] = (double)((double)worldPoint[2] / (double)worldPoint[3]);
    }
  
  coord[0] = worldPoint[0];
  coord[1] = worldPoint[1];
  coord[2] = worldPoint[2];
  
  int idx1 = (this->SliceOrientation+1)%3;
  int idx2 = (this->SliceOrientation+2)%3;
  
  if (worldPoint[idx1] < this->InitialBounds[idx1*2] ||
      worldPoint[idx1] > this->InitialBounds[idx1*2+1] ||
      worldPoint[idx2] < this->InitialBounds[idx2*2] ||
      worldPoint[idx2] > this->InitialBounds[idx2*2+1])
    {
    return 0;
    }
  return 1;
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::SetAxis1Color(double r, double g, double b)
{
  this->Axis1Actor->GetProperty()->SetColor(r, g, b);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
double* vtkKWCursorWidget::GetAxis1Color()
{
  return this->Axis1Actor->GetProperty()->GetColor();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::GetAxis1Color(double rgb[3])
{
  this->Axis1Actor->GetProperty()->GetColor(rgb);
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::SetAxis2Color(double r, double g, double b)
{
  this->Axis2Actor->GetProperty()->SetColor(r, g, b);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
double* vtkKWCursorWidget::GetAxis2Color()
{
  return this->Axis2Actor->GetProperty()->GetColor();
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::GetAxis2Color(double rgb[3])
{
  this->Axis2Actor->GetProperty()->GetColor(rgb);
}

//----------------------------------------------------------------------------
void vtkKWCursorWidget::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  
  os << indent << "Interactive: " << this->Interactive << endl;
  os << indent << "SliceOrientation: " << this->SliceOrientation << endl;
  os << indent << "Position: (" << this->Position[0] << ", "
     << this->Position[1] << ", " << this->Position[2] << ")" << endl;
}

