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

  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 "vtkIrregularSplineSurfaceWidget.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 "vtkCardinalSplinePatch.h"
#include "vtkTransform.h"
#include "vtkTriangleFilter.h"

#include "vtkBezierPatch.h"

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

vtkIrregularSplineSurfaceWidget::vtkIrregularSplineSurfaceWidget()
{
  this->EventCallbackCommand->SetCallback(vtkIrregularSplineSurfaceWidget::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 = 10; // by now...
  this->Handle         = new vtkActor* [this->NumberOfHandles];
  this->HandleMapper   = vtkPolyDataMapper::New();
  this->HandleGeometry = vtkCylinderSource::New();
  this->HandleGeometry->SetResolution(9);
  this->HandleGeometry->Update();
  this->HandleMapper->SetInput(this->HandleGeometry->GetOutput());

  // 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->SubdivisionFilter->SetNumberOfSubdivisions(5);


  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();


  vtkBezierPatch * patch = vtkBezierPatch::New();
  this->BezierPatch.push_back( patch );

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

}

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

  // then release all the resources
  BezierPatchContainer::const_iterator itr = this->BezierPatch.begin();
  BezierPatchContainer::const_iterator end = this->BezierPatch.begin();
  while( itr != end )
    {
    (*itr)->Delete();
    ++itr;
    }
  this->BezierPatch.clear();

  if( this->HandlesGrid )
    {
    this->HandlesGrid->Delete();
    this->HandlesGrid = 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 vtkIrregularSplineSurfaceWidget::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  if ( this->BezierPatch.size() )
    {
    BezierPatchContainer::const_iterator itr = this->BezierPatch.begin();
    BezierPatchContainer::const_iterator end = this->BezierPatch.begin();
    while( itr != end )
      {
      os << indent << "Patch: " << *itr << "\n";
      ++itr;
      }
    }
  else
    {
    os << indent << "No patches defined\n";
    }
}


void vtkIrregularSplineSurfaceWidget::BuildRepresentation()
{
  // Set the handles in the Bezier Patch
  // FIXME THIS MUST BE CHANGED WHEN WE ADD SEVERAL PATCHES
  
  // Set the handles in the Bezier Patch
  vtkBezierPatch * patch = this->BezierPatch[0];
  if( patch == NULL )
    {
    return;
    }

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

  //
  // Update Handles here...
  // 
  this->GenerateSurfacePoints();
}


//----------------------------------------------------------------------------
void vtkIrregularSplineSurfaceWidget::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 b030
  //
  i = 0;
  x = (bounds[0]+bounds[1])/2.0;
  y =  bounds[3];
  this->Handle[i++]->SetPosition(x,y,z);
 
  // Node b021
  //
  x = (2*bounds[0]+bounds[1])/3.0;
  y = (bounds[2]+2*bounds[3])/3.0;
  this->Handle[i++]->SetPosition(x,y,z);
  
  // Node b120
  //
  x = (bounds[0]+2*bounds[1])/3.0;
  y = (bounds[2]+2*bounds[3])/3.0;
  this->Handle[i++]->SetPosition(x,y,z);
   
  // Node b012
  //
  x = (5*bounds[0]+bounds[1])/6.0;
  y = (2*bounds[2]+bounds[3])/3.0;
  this->Handle[i++]->SetPosition(x,y,z);
    
  // Node b111
  //
  x = (bounds[0]+bounds[1])/2.0;
  y = (2*bounds[2]+bounds[3])/3.0;
  this->Handle[i++]->SetPosition(x,y,z);
     
  // Node b210
  //
  x = (bounds[0]+5*bounds[1])/6.0;
  y = (2*bounds[2]+bounds[3])/3.0;
  this->Handle[i++]->SetPosition(x,y,z);
     
  // Node b003
  //
  x = bounds[0];
  y = bounds[2];
  this->Handle[i++]->SetPosition(x,y,z);
      
  // Node b102
  //
  x = (2*bounds[0]+bounds[1])/3.0;
  y =  bounds[2];
  this->Handle[i++]->SetPosition(x,y,z);
      
  // Node b201
  //
  x = (bounds[0]+2*bounds[1])/3.0;
  y =  bounds[2];
  this->Handle[i++]->SetPosition(x,y,z);

  // Node b300
  //
  x =  bounds[1];
  y =  bounds[2];
  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 associated to the Handle array
//
void vtkIrregularSplineSurfaceWidget::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 deleted 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 vtkIrregularSplineSurfaceWidget::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->Update();

  this->SurfaceData->SetPoints( this->SubdivisionFilter->GetOutput()->GetPoints() );
  this->SurfaceData->SetPolys(  this->SubdivisionFilter->GetOutput()->GetPolys() );

  newPoints->Delete();
}

void vtkIrregularSplineSurfaceWidget::GenerateSurfacePointsOld()
{
  vtkPoints* newPoints = vtkPoints::New();

  const unsigned numberOfPointsOnTheSide = 32;

  // The Triangle of the numberOfPointsOnTheSide
  const unsigned numberOfPoints = (numberOfPointsOnTheSide+1)*
                                  (numberOfPointsOnTheSide+2)/2; 

  newPoints->Allocate( numberOfPoints );

  // Do one patch (the only one) by now...
  vtkBezierPatch * patch = this->BezierPatch[0];

  vtkIdType pointId=0;
  double point[3];

  for(unsigned int pu=0; pu < numberOfPointsOnTheSide; pu++)
    {
    const double u = (double)pu / (double)(numberOfPointsOnTheSide-1);
    for(unsigned int pv=0; pv <= numberOfPointsOnTheSide-1-pu; pv++)
      {
      const double v = (double)pv / (double)(numberOfPointsOnTheSide-1);
      patch->Evaluate( u, v, point );
      newPoints->InsertPoint(pointId++,point);
      }
    }

  this->SurfaceData->SetPoints(newPoints);
  newPoints->Delete();


  // Adding now the triangle strips
  vtkCellArray *newStrips  = vtkCellArray::New();

  const unsigned int numberOfPointsPerStrip = 2*numberOfPointsOnTheSide; // overestimation
  const unsigned int numberOfStrips = numberOfPointsOnTheSide-1;
  newStrips->Allocate( newStrips->EstimateSize(
                                      numberOfStrips,
                                      numberOfPointsPerStrip));

  vtkIdType firstIndex=0;
  for(unsigned int qu=0; qu < numberOfPointsOnTheSide-1; qu++)
    {
    const unsigned int numberOfPointsInThisStrip = 2*(numberOfPointsOnTheSide-qu)-1;
    vtkIdType * pointIds = new vtkIdType[ numberOfPointsInThisStrip ];
    vtkIdType pointIndex=0;
    for(unsigned int qv=0; qv < numberOfPointsOnTheSide-qu-1; qv++)
      {
      const vtkIdType pa = qv+firstIndex; 
      const vtkIdType pb = pa+(numberOfPointsInThisStrip+1)/2;
      pointIds[pointIndex++] = pa;
      pointIds[pointIndex++] = pb;
      }
    pointIds[pointIndex] = pointIds[pointIndex-2]+1;
    newStrips->InsertNextCell( numberOfPointsInThisStrip, pointIds );
    delete [] pointIds;
    firstIndex += numberOfPointsOnTheSide-qu;
    }
      

  this->SurfaceData->SetStrips(newStrips);
  newStrips->Delete();
}


// Node insertion is done here by adding a control point to the Bezier Patch.
void vtkIrregularSplineSurfaceWidget::InsertHandle()
{
  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 * additionsHandle = vtkActor::New();

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

  //  Find a position for new Handle. 
  //  Compute the surface point where the user clicked.
  double pickPoint[3];
  this->SurfacePicker->GetPickPosition( pickPoint );
  newHandles[this->NumberOfHandles]->SetPosition(pickPoint);

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

  // Notify observers about the change
  this->InvokeEvent(
    vtkSplineSurfaceWidget::SplineSurfaceHandlePositionChangedEvent, NULL);
}


// Node removal is done here by removing a control point from the Bezier Patch.
void vtkIrregularSplineSurfaceWidget::RemoveHandle()
{
}



