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

  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 "vtkImageActorPointHandleRepresentation3D.h"
#include "vtkCursor3D.h"
#include "vtkPolyDataMapper.h"
#include "vtkActor.h"
#include "vtkImageActorPointPlacer.h"
#include "vtkRenderer.h"
#include "vtkObjectFactory.h"
#include "vtkProperty.h"
#include "vtkPlaneCollection.h"
#include "vtkMath.h"
#include "vtkInteractorObserver.h"
#include "vtkPlane.h"
#include "vtkImageData.h"
#include "vtkRenderWindow.h"
#include "vtkImageActor.h"
#include "vtkCamera.h"

vtkCxxRevisionMacro(vtkImageActorPointHandleRepresentation3D, "$Revision: 1.10 $");
vtkStandardNewMacro(vtkImageActorPointHandleRepresentation3D);

//----------------------------------------------------------------------
vtkImageActorPointHandleRepresentation3D::vtkImageActorPointHandleRepresentation3D()
{
  this->ImageActor = NULL;
  this->Mapper->RemoveAllClippingPlanes();
  this->ImageActorDisplayExtent[0] = 0;
  this->ImageActorDisplayExtent[1] = -1;
  this->ImageActorDisplayExtent[2] = 0;
  this->ImageActorDisplayExtent[3] = -1;
  this->ImageActorDisplayExtent[4] = 0;
  this->ImageActorDisplayExtent[5] = -1;
  this->DisplayForAllSlices = 0;
}

//----------------------------------------------------------------------
vtkImageActorPointHandleRepresentation3D::~vtkImageActorPointHandleRepresentation3D()
{
  this->SetImageActor(NULL);
}

//----------------------------------------------------------------------
void vtkImageActorPointHandleRepresentation3D::SetImageActor( vtkImageActor *ia )
{
  if (this->ImageActor != ia)
    {
    vtkImageActor* tempSGMacroVar = this->ImageActor;
    this->ImageActor = ia;
    if (this->ImageActor != NULL) 
      { 
      this->ImageActor->Register(this); 

      vtkImageActorPointPlacer *placer = vtkImageActorPointPlacer::New();
      this->SetPointPlacer(placer);
      placer->SetImageActor(this->ImageActor);
      placer->SetWorldTolerance(0.0); // needed to drop seeds on the last slice.
      placer->Delete();
      }
    if (tempSGMacroVar != NULL)
      {
      tempSGMacroVar->UnRegister(this);
      }
    this->Modified();
    }
}

//----------------------------------------------------------------------
void vtkImageActorPointHandleRepresentation3D::BuildRepresentation()
{
  this->Superclass::BuildRepresentation();

  if (this->DisplayForAllSlices)
    {
    this->Mapper->RemoveAllClippingPlanes();
    return;
    }

  // Clip the reprensentation with the bounds of the image actor
  int iaDisplayExtent[6] = {0, -1, 0, -1, 0, -1};
  if (this->ImageActor)
    {
    this->ImageActor->GetDisplayExtent(iaDisplayExtent);
    
    int axis = -1;
    for (int i = 0; i < 3; i++)
      {
      if (iaDisplayExtent[2*i] == iaDisplayExtent[2*i+1])
        {
        axis = i;
        }
      }


    if ((this->ImageActorDisplayExtent[2*axis] != iaDisplayExtent[2*axis] ||
         this->ImageActorDisplayExtent[2*axis+1] != iaDisplayExtent[2*axis+1])
         && this->ImageActor->GetInput())
      {
      double bounds[6], spacing[3], normal1[3] = {0,0,0}, normal2[3] = {0,0,0};
      this->ImageActor->GetInput()->GetBounds(bounds);
      this->ImageActor->GetInput()->GetSpacing(spacing);
      double origin1[3] = {bounds[0], bounds[2], bounds[4]};
      double origin2[3] = {bounds[0], bounds[2], bounds[4]};
      double directionOfProjection
        = this->Renderer->GetActiveCamera()->GetDirectionOfProjection()[axis];

      // This strategy is the ideal one, but the image actor will hide the 
      // handle about 50% of the time.
      //
      //origin1[axis] -= spacing[axis]/2;
      //origin2[axis] += spacing[axis]/2;
      //normal1[axis] = 1;
      //normal2[axis] = -1;

      // So we show all handles that lie one unit spacing in front of the current
      // slice being displayed.
      //
      // One unit spacing towards the camera
      origin2[axis] -= spacing[axis] * directionOfProjection;
      origin2[axis] += (0.001 * directionOfProjection * spacing[axis]);

      //
      //     |       |     A represents the image actor plane and plane1.
      //     |       |     B represents plane2
      //     |       |
      //     |       |     <-------- directionOfProjection
      //     |       |
      //     |       |
      //     A <---> B
      //         |
      //        spacing[axis]
      //
      // All handles on the image between A and B will be displayed as if they
      // lie on slice A.
      //
      normal2[axis] = directionOfProjection;
      normal1[axis] = -directionOfProjection;

      //double bb[6];
      //this->Mapper->GetBounds(bb);
      //double c[3] = { (bb[0] + bb[1])/2.0, (bb[2] + bb[3])/2.0, (bb[4] + bb[5])/2.0};
      //cout << this << " planes are : (" << origin1[0] << "," << origin1[1] << "," << origin1[2] << ") to (" 
      //  << origin2[0] << "," << origin2[1] << "," << origin2[2] << "). Point is at (" << 
      //  c[0] << "," << c[1] << "," << c[2] << ")" << endl;

      // Need to set the clipping planes appropriately, so as to clip away 
      // handles that don't lie between planes A and B.
      vtkPlaneCollection *pc = vtkPlaneCollection::New();
      vtkPlane *p1 = vtkPlane::New();
      p1->SetNormal(normal1);
      p1->SetOrigin(origin1);
      vtkPlane *p2 = vtkPlane::New();
      p2->SetNormal(normal2);
      p2->SetOrigin(origin2);
      pc->AddItem(p1);
      pc->AddItem(p2);
      p1->Delete();
      p2->Delete();

      this->Mapper->SetClippingPlanes(pc);
      pc->Delete();
      }
    }
}

//----------------------------------------------------------------------
void vtkImageActorPointHandleRepresentation3D::SetDisplayForAllSlices(int arg)
{
  if (this->DisplayForAllSlices == arg)
    {
    return;
    }

  this->DisplayForAllSlices = arg;
  this->Modified();
  this->BuildRepresentation();
}

//----------------------------------------------------------------------
void vtkImageActorPointHandleRepresentation3D::PrintSelf(ostream& os, vtkIndent indent)
{
  //Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h
  this->Superclass::PrintSelf(os,indent);
  
  if ( this->ImageActor )
    {
    os << indent << "ImageActor: " << this->ImageActor << "\n";
    }
  else
    {
    os << indent << "ImageActor: (none)\n";
    }
}

