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

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

#include "vtkActor.h"
#include "vtkAssemblyNode.h"
#include "vtkAssemblyPath.h"
#include "vtkButterflySubdivisionFilter.h"
#include "vtkCallbackCommand.h"
#include "vtkCamera.h"
#include "vtkCardinalSpline.h"
#include "vtkCellArray.h"
#include "vtkCellPicker.h"
#include "vtkDelaunay2D.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPlaneSource.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkCylinderSource.h"
#include "vtkTransform.h"
#include "vtkTriangleFilter.h"

vtkCxxRevisionMacro(vtkSubdivisionSplineSurfaceWidget, "$Revision: 1.3 $");
vtkStandardNewMacro(vtkSubdivisionSplineSurfaceWidget);

vtkSubdivisionSplineSurfaceWidget::vtkSubdivisionSplineSurfaceWidget()
{
  this->EventCallbackCommand->SetCallback(vtkSubdivisionSplineSurfaceWidget::ProcessEvents);

  // Build the representation of the widget

  // Default bounds to get started
  double bounds[6];
  bounds[0] = -0.5;
  bounds[1] = 0.5;
  bounds[2] = -0.5;
  bounds[3] = 0.5;
  bounds[4] = -0.5;
  bounds[5] = 0.5;

  this->NumberOfHandles = 4; // by now...
  this->Handle         = new vtkActor* [this->NumberOfHandles];

  // this->HandleMapper   and 
  // this->HandleGeometry are created in the base class.
  
  // Polydata for holding the grid of handles
  this->HandlesGrid = vtkPolyData::New();

  // Filter for generating the Delaunay triangulation of the handles
  this->DelaunayFilter = vtkDelaunay2D::New();
  this->TriangleFilter = vtkTriangleFilter::New();
  this->SubdivisionFilter = vtkButterflySubdivisionFilter::New();

  this->DelaunayFilter->SetProjectionPlaneMode( VTK_BEST_FITTING_PLANE );
 
  this->TriangleFilter->SetInput( this->DelaunayFilter->GetOutput() );
  this->SubdivisionFilter->SetInput( this->TriangleFilter->GetOutput() );

  this->Resolution = 4;
  this->SubdivisionFilter->SetNumberOfSubdivisions( this->Resolution );

  // this releases the SurfaceData allocated in the constructor of the superclass.
  if( this->SurfaceData )
    {
    this->SurfaceData->Delete();
    }
  this->SurfaceData = this->SubdivisionFilter->GetOutput();
  this->SurfaceMapper->SetInput( this->SurfaceData ) ;

  int i=0;

  // Create the handles and setup the picking 
  for (i=0; i<this->NumberOfHandles; i++)
    {
    this->Handle[i] = vtkActor::New();
    this->Handle[i]->SetMapper(this->HandleMapper);
    this->HandlePicker->AddPickList(this->Handle[i]);
    }
  this->HandlePicker->PickFromListOn();

  // Initial creation of the widget, serves to initialize it
  // and generates its surface representation
  this->PlaceWidget(bounds);

}

vtkSubdivisionSplineSurfaceWidget::~vtkSubdivisionSplineSurfaceWidget()
{
  // first disable the widget
  if( this->GetEnabled() )
    {
    this->SetEnabled(0);
    }

  if( this->HandlesGrid )
    {
    this->HandlesGrid->Delete();
    this->HandlesGrid = NULL;
    }

  // SurfaceData is a pointer to the output of the Subdivision
  // filter. Here we set it to null in order to prevent the superclass
  // destructor from attempting to Delete() the output of the subdivision
  // filter.
  if( this->SurfaceData )
    {
    this->SurfaceData = NULL;
    }

  if( this->DelaunayFilter )
    {
    this->DelaunayFilter->Delete();
    this->DelaunayFilter = NULL;
    }

  if( this->TriangleFilter )
    {
    this->TriangleFilter->Delete();
    this->TriangleFilter = NULL;
    }

  if( this->SubdivisionFilter )
    {
    this->SubdivisionFilter->Delete();
    this->SubdivisionFilter = NULL;
    }
}



void vtkSubdivisionSplineSurfaceWidget::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  os << indent << "Resolution: " << this->Resolution << "\n";
}


void vtkSubdivisionSplineSurfaceWidget::BuildRepresentation()
{
  //
  // Update Handles here...
  // 
  this->GenerateSurfacePoints();
}


//----------------------------------------------------------------------------
void vtkSubdivisionSplineSurfaceWidget::PlaceWidget(double bds[6])
{
  double bounds[6];
  double center[3];
  this->AdjustBounds(bds, bounds, center);

  // Set initial hanles here....
  unsigned int i=0;

  double x;
  double y;
  double z = (bounds[4]+bounds[5])/2.0;
  
  // Node 00
  //
  i = 0;
  x = bounds[0];
  y = bounds[2];
  this->Handle[i++]->SetPosition(x,y,z);
    
  // Node 01
  //
  x = bounds[0];
  y = bounds[3];
  this->Handle[i++]->SetPosition(x,y,z);
      
  // Node 11
  //
  x =  bounds[1];
  y =  bounds[2];
  this->Handle[i++]->SetPosition(x,y,z);
  
  // Node 10
  //
  x =  bounds[1];
  y =  bounds[3];
  this->Handle[i++]->SetPosition(x,y,z);
 

  for (i=0; i<6; i++)
    {
    this->InitialBounds[i] = bounds[i];
    }

  // Re-compute the spline coeffs
  this->BuildRepresentation();
}


//
//  Release the resources related to Handles
//  
void vtkSubdivisionSplineSurfaceWidget::Initialize(void)
{
  int i;
  if ( this->Interactor )
    {
    if (!this->CurrentRenderer)
      {
      this->SetCurrentRenderer(this->Interactor->FindPokedRenderer(
        this->Interactor->GetLastEventPosition()[0],
        this->Interactor->GetLastEventPosition()[1]));
      }
    if ( this->CurrentRenderer != NULL)
      {
      for (i=0; i<this->NumberOfHandles; i++)
        {
        this->CurrentRenderer->RemoveViewProp(this->Handle[i]);
        }
      }
    }

  // HandleMapper and 
  // HandleGeometry should not be released here.

  for (i=0; i<this->NumberOfHandles; i++)
    {
    this->HandlePicker->DeletePickList(this->Handle[i]);
    this->Handle[i]->Delete();
    }

  this->NumberOfHandles = 0;

  delete [] this->Handle;
  this->Handle = NULL;
}

void vtkSubdivisionSplineSurfaceWidget::GenerateSurfacePoints()
{
  vtkPoints*   newPoints   = vtkPoints::New();

  newPoints->Allocate( this->NumberOfHandles );

  vtkIdType pointId=0;

  for (int i=0; i<this->NumberOfHandles; i++)
    {
    newPoints->InsertPoint(pointId++,this->Handle[i]->GetPosition());
    }

  this->HandlesGrid->SetPoints(newPoints);

  this->DelaunayFilter->SetInput( this->HandlesGrid );
  this->SubdivisionFilter->SetNumberOfSubdivisions( this->Resolution );
  this->SubdivisionFilter->Update();

  newPoints->Delete();
}


// Node insertion is done here by adding a control point to the spline
void vtkSubdivisionSplineSurfaceWidget::InsertHandle()
{
  //  Find a position for new Handle. 
  //  Compute the surface point where the user clicked.
  double pickPoint[3];
  if( this->SurfacePicker )
    {
    this->SurfacePicker->GetPickPosition( pickPoint );
    this->InsertHandle(pickPoint);
    }
}


// Node insertion is done here by adding a control point to the spline
void vtkSubdivisionSplineSurfaceWidget::InsertHandle(double position[3])
{
  int i=0;

  unsigned int newNumberOfHandles = this->NumberOfHandles+1;
  // Add one to the number and allocate memory for them
  vtkActor ** newHandles = new vtkActor* [newNumberOfHandles];
  
  // Just copy pointers from the existing ones
  for (i=0; i<this->NumberOfHandles; i++)
    {
    newHandles[i] = this->Handle[i];
    }

  // Create the new extra one.
  vtkActor * additionalHandle = vtkActor::New();

  // Manage the picking stuff for the new one
  additionalHandle->SetMapper(this->HandleMapper);
  additionalHandle->SetProperty(this->HandleProperty);
  this->HandlePicker->AddPickList(additionalHandle);
  this->CurrentRenderer->AddViewProp(additionalHandle);
  newHandles[this->NumberOfHandles] = additionalHandle;

  additionalHandle->SetPosition(position);

  // Replace with the new array of Handle pointers.
  this->NumberOfHandles = newNumberOfHandles;
  delete [] this->Handle;
  this->Handle = newHandles;
 
  this->CurrentHandleIndex = newNumberOfHandles-1;
  this->CurrentHandle = additionalHandle;
  
  // Re-compute the spline surface
  this->BuildRepresentation();

  // Notify observers about the change
  //
  // This first event is intended for the SplineSurface2DWidget
  this->InvokeEvent(
    vtkSplineSurfaceWidget::SplineSurfaceNumberOfHandlesChangedEvent,NULL);
  //
  // This second event is intendef for the vtkVVWindow to notify
  // to other RenderWidgets and across TeleVolView.
  this->InvokeEvent(
    vtkSplineSurfaceWidget::SplineSurfaceHandlePositionChangedEvent,NULL);
}


// Node removal is done here by removing a control point from the Patch.
void vtkSubdivisionSplineSurfaceWidget::RemoveHandle()
{
  if ( this->CurrentHandleIndex < 0 || this->CurrentHandleIndex >= this->NumberOfHandles )
    {
    vtkGenericWarningMacro(<<"Spline handle index out of range. = " <<  this->CurrentHandleIndex);
    return;
    }

  int i=0;
  int j=0;

  unsigned int newNumberOfHandles = this->NumberOfHandles-1;
  // Add one to the number and allocate memory for them
  vtkActor ** newHandles = new vtkActor* [newNumberOfHandles];
  
  // Just copy pointers from the existing ones
  for (i=0,j=0; i<this->NumberOfHandles; i++)
    {
    if( i == this->CurrentHandleIndex )
      {
      this->HandlePicker->DeletePickList(this->Handle[i]);
      if ( this->CurrentRenderer != NULL)
        {
        this->CurrentRenderer->RemoveViewProp(this->Handle[i]);
        }
      this->Handle[i]->Delete();
      continue;
      }
    newHandles[j++] = this->Handle[i];
    }

  // Replace with the new array of Handle pointers.
  this->NumberOfHandles = newNumberOfHandles;
  delete [] this->Handle;
  this->Handle = newHandles;
 
  this->CurrentHandleIndex = -1;
  this->CurrentHandle = NULL;
  
  // Re-compute the spline surface
  this->BuildRepresentation();

  // Notify observers about the change
  //
  // This first event is intended for the SplineSurface2DWidget
  this->InvokeEvent(
    vtkSplineSurfaceWidget::SplineSurfaceNumberOfHandlesChangedEvent,NULL);
  //
  // This second event is intendef for the vtkVVWindow to notify
  // to other RenderWidgets and across TeleVolView.
  this->InvokeEvent(
    vtkSplineSurfaceWidget::SplineSurfaceHandlePositionChangedEvent,NULL);
}


