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

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

#include "vtkActor.h"
#include "vtkAssemblyNode.h"
#include "vtkAssemblyPath.h"
#include "vtkCallbackCommand.h"
#include "vtkCamera.h"
#include "vtkCardinalSpline.h"
#include "vtkCellArray.h"
#include "vtkCellPicker.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"

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

vtkRegularSplineSurfaceWidget::vtkRegularSplineSurfaceWidget()
{
  this->State = vtkRegularSplineSurfaceWidget::Start;
  this->EventCallbackCommand->SetCallback(vtkSplineSurfaceWidget::ProcessEvents);
  this->PlaneSource = NULL;
  this->SplinePositionsU = NULL;
  this->SplinePositionsV = NULL;
  this->RemoteMode = 0; // by default running in local mode

  // Build the representation of the widget

  this->Spline = vtkCardinalSplinePatch::New();
  this->Spline->Register(this);
  this->Spline->Delete();

  // 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;

  // Create the handles in a 3 X 3 grid
  this->NumberOfHandlesU = 3;
  this->NumberOfHandlesV = 3;
  this->NumberOfHandles = this->NumberOfHandlesU * this->NumberOfHandlesV;
  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());
  int i;
  int u;
  int v;

  // Create a default flat plane parallel to XY and in the middle of Z extent.
  double x00 = (bounds[0]+bounds[1])/2;
  double x01 = (bounds[0]+bounds[1])/2;
  double x10 = (bounds[0]+bounds[1])/2;
  double x11 = (bounds[0]+bounds[1])/2;
  double y00 =  bounds[2];
  double y01 =  bounds[3];
  double y10 =  bounds[2];
  double y11 =  bounds[3];
  double z00 =  bounds[4];
  double z01 =  bounds[4]; 
  double z10 =  bounds[5];
  double z11 =  bounds[5];

  double x;
  double y;
  double z;
  
  this->Spline->SetNumberOfHandlesU( this->NumberOfHandlesU );
  this->Spline->SetNumberOfHandlesV( this->NumberOfHandlesV );
  this->Spline->Allocate();
  this->Spline->Compute();

  int handle = 0;
  for (v=0; v<this->NumberOfHandlesV; v++)
    {
    const double pv = v / (this->NumberOfHandlesV - 1.0);
    for (u=0; u<this->NumberOfHandlesU; u++)
      {
      const double pu = u / (this->NumberOfHandlesU - 1.0);
      x = (1.0-pu)*(1.0-pv)*x00 + (1.0-pu)*pv*x01 + pu*(1.0-pv)*x10 + pu*pv*x11;
      y = (1.0-pu)*(1.0-pv)*y00 + (1.0-pu)*pv*y01 + pu*(1.0-pv)*y10 + pu*pv*y11;
      z = (1.0-pu)*(1.0-pv)*z00 + (1.0-pu)*pv*z01 + pu*(1.0-pv)*z10 + pu*pv*z11;
      this->Spline->SetHandlePosition(u,v,x,y,z);
      this->Handle[handle] = vtkActor::New();
      this->Handle[handle]->SetMapper(this->HandleMapper);
      this->Handle[handle]->SetPosition(x,y,z);
      handle++;
      }
    }

  this->Spline->Compute();

  // Define the points and line segments representing the spline
  this->ResolutionU = 50;
  this->ResolutionV = 50;
  this->NumberOfSplinePointsU = this->ResolutionU + 1;
  this->NumberOfSplinePointsV = this->ResolutionV + 1;
  this->SplinePositionsU = new double [this->NumberOfSplinePointsU];
  this->SplinePositionsV = new double [this->NumberOfSplinePointsV];
  
  // Initial creation of the widget, serves to initialize it
  // and generates its surface representation
  this->PlaceWidget(bounds);

  // Manage the picking stuff
  for (i=0; i<this->NumberOfHandles; i++)
    {
    this->HandlePicker->AddPickList(this->Handle[i]);
    }
  this->HandlePicker->PickFromListOn();

}

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

  // then release all the resources
  
  if( this->SplinePositionsU )
    {
    delete [] this->SplinePositionsU;
    this->SplinePositionsU = NULL;
    }

  if( this->SplinePositionsV )
    {
    delete [] this->SplinePositionsV;
    this->SplinePositionsV = NULL;
    }

  if ( this->Spline)
    {
    this->Spline->UnRegister(this);
    }
}


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

  if ( this->Spline )
    {
    os << indent << "Spline: "
       << this->Spline << "\n";
    }
  else
    {
    os << indent << "Spline: (none)\n";
    }

  os << indent << "Resolution U: " << this->ResolutionU << "\n";
  os << indent << "Resolution V: " << this->ResolutionV << "\n";
  os << indent << "Number Of Handles U: " << this->NumberOfHandlesU << "\n";
  os << indent << "Number Of Handles V: " << this->NumberOfHandlesV << "\n";
}


void vtkRegularSplineSurfaceWidget::BuildRepresentation()
{
  // Handles have changed position, re-compute the spline coeffs
  double ctr[3];
  int i=0;
  int u;
  int v;
  for (v=0; v<this->NumberOfHandlesV; v++)
    {
    for (u=0; u<this->NumberOfHandlesU; u++)
      {
      this->Handle[i]->GetPosition(ctr);
      this->Spline->SetHandlePosition(u,v,ctr);
      i++;
      }
    }
  this->Spline->Compute();
  this->GenerateSurfacePoints();
}


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

  // Create a default flat plane parallel to XY and in the middle of Z extent.
  double x00 = (bounds[0]+bounds[1])/2;
  double x01 = (bounds[0]+bounds[1])/2;
  double x10 = (bounds[0]+bounds[1])/2;
  double x11 = (bounds[0]+bounds[1])/2;
  double y00 =  bounds[2];
  double y01 =  bounds[3];
  double y10 =  bounds[2];
  double y11 =  bounds[3];
  double z00 =  bounds[4];
  double z01 =  bounds[4]; 
  double z10 =  bounds[5];
  double z11 =  bounds[5];
  double x;
  double y;
  double z;
  int handle = 0;
  for (int u=0; u<this->NumberOfHandlesU; u++)
    {
    const double pu = u / (this->NumberOfHandlesU - 1.0);
    for (int v=0; v<this->NumberOfHandlesV; v++)
      {
      const double pv = v / (this->NumberOfHandlesV - 1.0);
      x = (1.0-pu)*(1.0-pv)*x00 + (1.0-pu)*pv*x01 + pu*(1.0-pv)*x10 + pu*pv*x11;
      y = (1.0-pu)*(1.0-pv)*y00 + (1.0-pu)*pv*y01 + pu*(1.0-pv)*y10 + pu*pv*y11;
      z = (1.0-pu)*(1.0-pv)*z00 + (1.0-pu)*pv*z01 + pu*(1.0-pv)*z10 + pu*pv*z11;
      this->Handle[handle]->SetPosition(x,y,z);
      handle++;
      }
    }

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

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

void vtkRegularSplineSurfaceWidget::SetNumberOfHandles(int nu,int nv)
{
  if (this->NumberOfHandlesU == nu && this->NumberOfHandlesV == nv )
    {
    return;
    }
  if (nu < 2 || nv < 2 )
    {
    vtkGenericWarningMacro(<<"vtkRegularSplineSurfaceWidget: minimum of 2 points required.");
    return;
    }
 
  // Allocate resources for the new handlers
  //
  const unsigned int newNumberOfHandlers = nu * nv;

  vtkActor          ** newHandle         = new vtkActor* [newNumberOfHandlers];
  vtkPolyDataMapper  * newHandleMapper   = vtkPolyDataMapper::New();
  vtkCylinderSource  * newHandleGeometry = vtkCylinderSource::New();

  newHandleGeometry->SetResolution(9);

  newHandleMapper->SetInput(newHandleGeometry->GetOutput());

  const double handleFactorU = (this->NumberOfHandlesU - 1.0)/(nu - 1.0);
  const double handleFactorV = (this->NumberOfHandlesV - 1.0)/(nv - 1.0);


  int u;
  int v;
  int handle = 0;
  double point[3];
  for (v=0; v<nv; v++)
    {
    const double pv = v * handleFactorV;
    this->Spline->PrepareToEvaluateAlongV(pv);
    for (u=0; u<nu; u++)
      {
      const double pu = u * handleFactorU;
      this->Spline->EvaluateAfterFixingV(pu,point);
      newHandle[handle] = vtkActor::New();
      newHandle[handle]->SetMapper(newHandleMapper);
      newHandle[handle]->SetProperty(this->HandleProperty);
      newHandle[handle]->SetPosition(point[0],point[1],point[2]);
      this->HandlePicker->AddPickList(newHandle[handle]);
      handle++;
      }
    }

  // Finally delete the existing ones
  this->Initialize();

    
  // Configure the new ones.
  this->NumberOfHandlesU = nu;
  this->NumberOfHandlesV = nv;

  this->NumberOfHandles = nu * nv;

  this->HandleGeometry = newHandleGeometry;
  this->HandleMapper   = newHandleMapper;
  this->Handle         = newHandle;


  // Now pass those handles to the Spline
  this->Spline->SetNumberOfHandlesU( this->NumberOfHandlesU );
  this->Spline->SetNumberOfHandlesV( this->NumberOfHandlesV );
  this->Spline->Allocate();
  this->Spline->Compute();

  const double factorU = (this->NumberOfHandlesU - 1.0)/
                         (this->NumberOfSplinePointsU - 1.0);
  const double factorV = (this->NumberOfHandlesV - 1.0)/
                         (this->NumberOfSplinePointsV - 1.0);

  for (u=0; u<this->NumberOfSplinePointsU; u++)
    {
    this->SplinePositionsU[u] = u * factorU;
    }

  for (v=0; v<this->NumberOfSplinePointsV; v++)
    {
    this->SplinePositionsV[v] = v * factorV;
    }

  this->BuildRepresentation();

  if ( this->Interactor )
    {
    if (!this->CurrentRenderer)
      {
      this->SetCurrentRenderer(this->Interactor->FindPokedRenderer(
        this->Interactor->GetLastEventPosition()[0],
        this->Interactor->GetLastEventPosition()[1]));
      }
    if (this->CurrentRenderer != NULL)
      {
      for (int h=0; h<this->NumberOfHandles; h++)
        {
        this->CurrentRenderer->AddViewProp(this->Handle[h]);
        }
      }
    this->Interactor->Render();
    }
  this->InvokeEvent(
    vtkSplineSurfaceWidget::SplineSurfaceNumberOfHandlesChangedEvent, NULL);
}


// Release the resources associated to the Handle array
void vtkRegularSplineSurfaceWidget::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;
  this->NumberOfHandlesU = 0;
  this->NumberOfHandlesV = 0;

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

void vtkRegularSplineSurfaceWidget::SetResolutionV(int resolution)
{
  if (this->ResolutionV == resolution || resolution < (this->NumberOfHandlesV-1))
    {
    return;
    }

  this->NumberOfSplinePointsV = resolution + 1;

  if(resolution > this->ResolutionV)  //only delete when necessary
    {
    delete [] this->SplinePositionsV;
    if ( (this->SplinePositionsV = new double[this->NumberOfSplinePointsV]) == NULL )
      {
      vtkErrorMacro(<<"vtkRegularSplineSurfaceWidget: failed to reallocate SplinePositions.");
      return;
      }
    }

  this->ResolutionV = resolution;

  this->BuildRepresentation();
}

void vtkRegularSplineSurfaceWidget::SetResolutionU(int resolution)
{
  if (this->ResolutionU == resolution || resolution < (this->NumberOfHandlesU-1))
    {
    return;
    }

  this->NumberOfSplinePointsU = resolution + 1;

  if(resolution > this->ResolutionU)  //only delete when necessary
    {
    delete [] this->SplinePositionsU;
    if ( (this->SplinePositionsU = new double[this->NumberOfSplinePointsU]) == NULL )
      {
      vtkErrorMacro(<<"vtkRegularSplineSurfaceWidget: failed to reallocate SplinePositions.");
      return;
      }
    }

  this->ResolutionU = resolution;

  this->BuildRepresentation();
}

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

  const unsigned numberOfPoints = this->NumberOfSplinePointsU *
                                  this->NumberOfSplinePointsV;

  newPoints->Allocate( numberOfPoints );

  const double factorU = (this->NumberOfHandlesU - 1.0)/
                         (this->NumberOfSplinePointsU - 1.0);
  const double factorV = (this->NumberOfHandlesV - 1.0)/
                         (this->NumberOfSplinePointsV - 1.0);

  int u;
  int v;
  int i=0;
  double point[3];
  for (v=0; v<this->NumberOfSplinePointsV; v++)
    {
    const double pv = v * factorV;
    this->SplinePositionsV[v] = pv;
    this->Spline->PrepareToEvaluateAlongV(pv);
    for (u=0; u<this->NumberOfSplinePointsU; u++)
      {  
      const double pu = u * factorU;
      this->SplinePositionsU[u] = pu;
      this->Spline->EvaluateAfterFixingV(pu,point);
      newPoints->InsertPoint(i++,point[0],point[1],point[2]);
      }
    }

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


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

  const unsigned int numberOfPointsPerStrip = 2*this->NumberOfSplinePointsV;
  newStrips->Allocate( newStrips->EstimateSize(
                                      this->ResolutionU,
                                      numberOfPointsPerStrip));

  vtkIdType * pointIds = new vtkIdType[ numberOfPointsPerStrip ];

  for (u=0; u<this->ResolutionU; u++)
    {
    unsigned int pt=0;
    for (v=0; v<this->NumberOfSplinePointsV; v++)
      {  
      pointIds[pt++] = v +  u    * this->NumberOfSplinePointsV;
      pointIds[pt++] = v + (u+1) * this->NumberOfSplinePointsV;
      }
    newStrips->InsertNextCell( numberOfPointsPerStrip, pointIds );
    }
  delete [] pointIds;

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

}


