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

  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 "vtkKWMarker2D.h"

#include "vtkActor2D.h"
#include "vtkCallbackCommand.h"
#include "vtkCellArray.h"
#include "vtkObjectFactory.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper2D.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkProperty2D.h"

#include "vtkTextActor.h"

vtkStandardNewMacro( vtkKWMarker2D );
vtkCxxRevisionMacro(vtkKWMarker2D, "$Revision: 1.21 $");

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLKWMarker2DReader.h"
#include "XML/vtkXMLKWMarker2DWriter.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtkKWMarker2D, vtkXMLKWMarker2DReader, vtkXMLKWMarker2DWriter);

//----------------------------------------------------------------------------
vtkKWMarker2D::vtkKWMarker2D()
{
  this->EventCallbackCommand->SetCallback(vtkKWMarker2D::ProcessEvents);
  this->State = vtkKWMarker2D::Outside;
  this->Priority = 0.55;

  double pts[][3] = { {   5,  0, 0 },
                     {   5, 10, 0 },
                     {   0,  5, 0 },
                     {  10,  5, 0 } };
  vtkIdType lins[][2] = { { 0, 1 },
                          { 2, 3 } };
  
  vtkPolyData* pd = vtkPolyData::New();
  this->Points = vtkPoints::New();
  vtkCellArray *lines = vtkCellArray::New();
  int cc;
  for ( cc = 0; cc < 4; cc ++ )
    {
    this->Points->InsertPoint(cc, pts[cc]);
    }
  
  lines->InsertNextCell(2, lins[0]);
  lines->InsertNextCell(2, lins[1]);
  pd->SetPoints(this->Points);
  pd->SetLines(lines);
  lines->Delete();

  vtkPolyDataMapper2D* mapper = vtkPolyDataMapper2D::New();
  mapper->SetInput(pd);
  pd->Delete();
  this->Actor = vtkActor2D::New();
  this->Actor->SetMapper(mapper);
  this->Actor->SetWidth(0.1);
  this->Actor->SetHeight(0.1);  
  mapper->Delete();

  this->Actor->GetPositionCoordinate()
    ->SetCoordinateSystemToNormalizedViewport();
  this->Actor->GetPosition2Coordinate()
    ->SetCoordinateSystemToNormalizedViewport();
}

//----------------------------------------------------------------------------
vtkKWMarker2D::~vtkKWMarker2D()
{
  this->Actor->Delete();
  this->Points->Delete();
}

//----------------------------------------------------------------------------
void vtkKWMarker2D::SetEnabled(int enabling)
{
  if ( ! this->Interactor )
    {
    vtkErrorMacro(<<"The interactor must be set prior to enabling/disabling widget");
    return;
    }
  
  if ( enabling ) 
    {
    vtkDebugMacro(<<"Enabling line widget");
    if ( this->Enabled ) //already enabled, just return
      {
      return;
      }
    
    if ( ! this->CurrentRenderer )
      {
      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);

    this->CurrentRenderer->AddViewProp(this->Actor);
    double size[2];
    this->GetSize(size);
    if ( size[0] == 1 || size[1] == 1 )
      {
      int *rsize = this->CurrentRenderer->GetSize();
      size[0] = rsize[0] * .1;
      size[1] = rsize[1] * .1;
      }
    
    this->SetSize(size);

    // Add the marker
    this->InvokeEvent(vtkCommand::EnableEvent,NULL);
    }
  else //disabling------------------------------------------
    {
    vtkDebugMacro(<<"Disabling line 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 line
    if (this->CurrentRenderer)
      {
      this->CurrentRenderer->RemoveActor(this->Actor);
      }

    this->InvokeEvent(vtkCommand::DisableEvent,NULL);
    }

  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWMarker2D::SetColor(double r, double g, double b)
{
  this->Actor->GetProperty()->SetColor(r,g,b);
}

//----------------------------------------------------------------------------
double* vtkKWMarker2D::GetColor()
{
  return this->Actor->GetProperty()->GetColor();
}

//----------------------------------------------------------------------------
void vtkKWMarker2D::SetPosition(double *pos)
{
  this->SetPosition(pos[0], pos[1], pos[2], pos[3]);
}

//----------------------------------------------------------------------------
void vtkKWMarker2D::SetPosition(double x1, double y1, double x2, double y2)
{
  if (!this->CurrentRenderer)
    {
    return;
    }

  this->Actor->SetPosition(x1, y1);
  this->Actor->SetPosition2(x2, y2);

  double size[4];
  this->GetSize(size);
  this->SetSize(size);
}

//----------------------------------------------------------------------------
void vtkKWMarker2D::GetPosition(double *pos)
{
  if (!this->CurrentRenderer)
    {
    pos[0] = pos[1] = pos[2] = pos[3] = 0.0;
    return;
    }
  
  double *apos;

  apos = this->Actor->GetPosition();
  pos[0] = apos[0];
  pos[1] = apos[1];

  apos = this->Actor->GetPosition2();
  pos[2] = apos[0];
  pos[3] = apos[1];
}

//----------------------------------------------------------------------------
void vtkKWMarker2D::GetSize(double* s)
{
  if (!this->CurrentRenderer)
    {
    s[0] = s[1] = 0.0;
    return;
    }

  int *p1 = this->Actor->GetPositionCoordinate()->GetComputedViewportValue(
    this->CurrentRenderer);
  int *p2 = this->Actor->GetPosition2Coordinate()->GetComputedViewportValue(
    this->CurrentRenderer);
  s[0] = p2[0] - p1[0];
  s[1] = p2[1] - p1[1];
}

//----------------------------------------------------------------------------
void vtkKWMarker2D::SetSize(double x, double y)
{
  double point[4][3];
  int cc;
  for (cc = 0; cc < 4; cc ++)
    {
    this->Points->GetPoint(cc, point[cc]);
    }
  double *pt;
  pt = point[0];
  pt[0] = x / 2.0;
  pt = point[1];
  pt[0] = x / 2.0;
  pt[1] = y;
  pt = point[2];
  pt[1] = y / 2.0;
  pt = point[3];
  pt[0] = x;
  pt[1] = y / 2.0;
  for (cc = 0; cc < 4; cc ++)
    {
    this->Points->SetPoint(cc, point[cc]);
    }
}

//----------------------------------------------------------------------------
void vtkKWMarker2D::ProcessEvents(vtkObject* vtkNotUsed(object), 
                                       unsigned long event,
                                       void* clientdata, 
                                       void* vtkNotUsed(calldata))
{
  vtkKWMarker2D* self = reinterpret_cast<vtkKWMarker2D *>( clientdata );
  
  //okay, let's do the right thing
  switch(event)
    {
    case vtkCommand::LeftButtonPressEvent:
      self->OnLeftButtonDown();
      break;
    case vtkCommand::LeftButtonReleaseEvent:
      self->OnLeftButtonUp();
      break;
    case vtkCommand::MouseMoveEvent:
      self->OnMouseMove();
      break;
    }
}


//----------------------------------------------------------------------------
int vtkKWMarker2D::ComputeStateBasedOnPosition(int X, int Y, 
                                                    int *pos1, int *pos2)
{
  int Result;
  
  // what are we modifying? The position, or size?
  // if size what piece?
  // if we are within 7 pixels of an edge...
  int e1 = 0;
  int e2 = 0;
  int e3 = 0;
  int e4 = 0;
  if (X - pos1[0] < 7)
    {
    e1 = 1;
    }
  if (pos2[0] - X < 7)
    {
    e3 = 1;
    }
  if (Y - pos1[1] < 7)
    {
    e2 = 1;
    }
  if (pos2[1] - Y < 7)
    {
    e4 = 1;
    }

  // assume we are moving
  Result = vtkKWMarker2D::Moving;
  // unless we are on a corner or edges
  if (e2)
    {
    Result = vtkKWMarker2D::AdjustingE2;
    }
  if (e4)
    {
    Result = vtkKWMarker2D::AdjustingE4;
    }
  if (e1)
    {
    Result = vtkKWMarker2D::AdjustingE1;
    if (e2)
      {
      Result = vtkKWMarker2D::AdjustingP1;
      }
    if (e4)
      {
      Result = vtkKWMarker2D::AdjustingP4;
      }
    }
  if (e3)
    {
    Result = vtkKWMarker2D::AdjustingE3;
    if (e2)
      {
      Result = vtkKWMarker2D::AdjustingP2;
      }
    if (e4)
      {
      Result = vtkKWMarker2D::AdjustingP3;
      }
    }

  return Result;
}

//----------------------------------------------------------------------------
void vtkKWMarker2D::SetCursor(int cState)
{
  switch (cState)
    {
    case vtkKWMarker2D::AdjustingP1:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZESW);
      break;
    case vtkKWMarker2D::AdjustingP3:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZENE);
      break;
    case vtkKWMarker2D::AdjustingP2:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZESE);
      break;
    case vtkKWMarker2D::AdjustingP4:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZENW);
      break;
    case vtkKWMarker2D::AdjustingE1:
    case vtkKWMarker2D::AdjustingE3:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZEWE);
      break;
    case vtkKWMarker2D::AdjustingE2:
    case vtkKWMarker2D::AdjustingE4:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZENS);
      break;
    case vtkKWMarker2D::Moving:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZEALL);
      break;        
    }
}


//----------------------------------------------------------------------------
void vtkKWMarker2D::OnLeftButtonDown()
{
  if (!this->CurrentRenderer)
    {
    return;
    }

  // We're only here is we are enabled
  int X = this->Interactor->GetEventPosition()[0];
  int Y = this->Interactor->GetEventPosition()[1];

  // are we over the widget?
  //this->Interactor->FindPokedRenderer(X,Y);
  int *pos1 = this->Actor->GetPositionCoordinate()
    ->GetComputedDisplayValue(this->CurrentRenderer);
  int *pos2 = this->Actor->GetPosition2Coordinate()
    ->GetComputedDisplayValue(this->CurrentRenderer);

  // are we not over the marker, ignore
  if (X < pos1[0] || X > pos2[0] || Y < pos1[1] || Y > pos2[1])
    {
    return;
    }
  
  // start a drag, store the normalized view coords
  double X2 = X;
  double Y2 = Y;
  // convert to normalized viewport coordinates
  this->CurrentRenderer->DisplayToNormalizedDisplay(X2,Y2);
  this->CurrentRenderer->NormalizedDisplayToViewport(X2,Y2);
  this->CurrentRenderer->ViewportToNormalizedViewport(X2,Y2);
  this->StartPosition[0] = X2;
  this->StartPosition[1] = Y2;

  this->State = this->ComputeStateBasedOnPosition(X, Y, pos1, pos2);
  this->SetCursor(this->State);
  
  this->EventCallbackCommand->SetAbortFlag(1);
  this->StartInteraction();
  this->InvokeEvent(vtkCommand::StartInteractionEvent,NULL);
}

//----------------------------------------------------------------------------
void vtkKWMarker2D::OnMouseMove()
{
  if (!this->CurrentRenderer)
    {
    return;
    }

  // compute some info we need for all cases
  int X = this->Interactor->GetEventPosition()[0];
  int Y = this->Interactor->GetEventPosition()[1];
  
  // compute the display bounds of the marker if we are inside or outside
  int *pos1, *pos2;
  if (this->State == vtkKWMarker2D::Outside ||
      this->State == vtkKWMarker2D::Inside)
    {
    pos1 = this->Actor->GetPositionCoordinate()
      ->GetComputedDisplayValue(this->CurrentRenderer);
    pos2 = this->Actor->GetPosition2Coordinate()
      ->GetComputedDisplayValue(this->CurrentRenderer);
  
    if (this->State == vtkKWMarker2D::Outside)
      {
      // if we are not over the marker, ignore
      if (X < pos1[0] || X > pos2[0] ||
          Y < pos1[1] || Y > pos2[1])
        {
        return;
        }
      // otherwise change our state to inside
      this->State = vtkKWMarker2D::Inside;
      }
  
    // if inside, set the cursor to the correct shape
    if (this->State == vtkKWMarker2D::Inside)
      {
      // if we have left then change cursor back to default
      if (X < pos1[0] || X > pos2[0] ||
          Y < pos1[1] || Y > pos2[1])
        {
        this->State = vtkKWMarker2D::Outside;
        this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_DEFAULT);
        return;
        }
      // adjust the cursor based on our position
      this->SetCursor(this->ComputeStateBasedOnPosition(X,Y,pos1,pos2));
      return;
      }
    }
  
  double XF = X;
  double YF = Y;
  // convert to normalized viewport coordinates
  this->CurrentRenderer->DisplayToNormalizedDisplay(XF,YF);
  this->CurrentRenderer->NormalizedDisplayToViewport(XF,YF);
  this->CurrentRenderer->ViewportToNormalizedViewport(XF,YF);
  
  // there are four parameters that can be adjusted
  double *fpos1 = this->Actor->GetPositionCoordinate()->GetValue();
  double *fpos2 = this->Actor->GetPosition2Coordinate()->GetValue();
  double par1[2];
  double par2[2];
  par1[0] = fpos1[0];
  par1[1] = fpos1[1];
  par2[0] = fpos1[0] + fpos2[0];  
  par2[1] = fpos1[1] + fpos2[1];  
    
  // based on the state, adjust the marker parameters
  switch (this->State)
    {
    case vtkKWMarker2D::AdjustingP1:
      par1[0] = par1[0] + XF - this->StartPosition[0];
      par1[1] = par1[1] + YF - this->StartPosition[1];
      break;
    case vtkKWMarker2D::AdjustingP2:
      par2[0] = par2[0] + XF - this->StartPosition[0];
      par1[1] = par1[1] + YF - this->StartPosition[1];
      break;
    case vtkKWMarker2D::AdjustingP3:
      par2[0] = par2[0] + XF - this->StartPosition[0];
      par2[1] = par2[1] + YF - this->StartPosition[1];
      break;
    case vtkKWMarker2D::AdjustingP4:
      par1[0] = par1[0] + XF - this->StartPosition[0];
      par2[1] = par2[1] + YF - this->StartPosition[1];
      break;
    case vtkKWMarker2D::AdjustingE1:
      par1[0] = par1[0] + XF - this->StartPosition[0];
      break;
    case vtkKWMarker2D::AdjustingE2:
      par1[1] = par1[1] + YF - this->StartPosition[1];
      break;
    case vtkKWMarker2D::AdjustingE3:
      par2[0] = par2[0] + XF - this->StartPosition[0];
      break;
    case vtkKWMarker2D::AdjustingE4:
      par2[1] = par2[1] + YF - this->StartPosition[1];
      break;
    case vtkKWMarker2D::Moving:
      // first apply the move
      par1[0] = par1[0] + XF - this->StartPosition[0];
      par1[1] = par1[1] + YF - this->StartPosition[1];
      par2[0] = par2[0] + XF - this->StartPosition[0];
      par2[1] = par2[1] + YF - this->StartPosition[1];
      break;
    }
  
  // push the change out to the marker
  // make sure the marker doesn't shrink to nothing
  if (par2[0] > par1[0] && par2[1] > par1[1])
    {
    this->Actor->GetPositionCoordinate()->SetValue(par1[0],par1[1]);
    this->Actor->GetPosition2Coordinate()->
      SetValue(par2[0] - par1[0], par2[1] - par1[1]);
    this->StartPosition[0] = XF;
    this->StartPosition[1] = YF;

    double size[2];
    this->GetSize(size);
    this->SetSize(size);
    this->InvokeEvent(vtkCommand::InteractionEvent, NULL);
    }
  
  // start a drag
  this->EventCallbackCommand->SetAbortFlag(1);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWMarker2D::OnLeftButtonUp()
{
  if (this->State == vtkKWMarker2D::Outside)
    {
    return;
    }

  // stop adjusting
  this->State = vtkKWMarker2D::Outside;
  this->EventCallbackCommand->SetAbortFlag(1);
  this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_DEFAULT);
  this->EndInteraction();
  this->InvokeEvent(vtkCommand::EndInteractionEvent,NULL);
  this->Interactor->Render();
}

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