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

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

#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkSpline.h"
#include "vtkCardinalSpline.h"

#include <vtksys/stl/vector>

class SplineVectorType : public vtksys_stl::vector<vtkSpline *>
{
};

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

vtkCardinalSplinePatch::vtkCardinalSplinePatch()
{
  // Create the handles in a 3 X 3 grid
  this->NumberOfHandlesU = 3;
  this->NumberOfHandlesV = 3;

  this->XSpline= new SplineVectorType();
  this->YSpline= new SplineVectorType();
  this->ZSpline= new SplineVectorType();

  this->VXSpline = NULL;
  this->VYSpline = NULL;
  this->VZSpline = NULL;

  this->Handles = NULL;

  this->Allocate();
}

vtkCardinalSplinePatch::~vtkCardinalSplinePatch()
{
  if( this->Handles )
    {
    delete [] this->Handles;
    }
  this->ReleaseAllSplines();

  delete this->XSpline;
  delete this->YSpline;
  delete this->ZSpline;
}

// Creates an instance of a vtkCardinalSpline by default
vtkSpline* 
vtkCardinalSplinePatch::CreateDefaultSpline()
{
  return vtkCardinalSpline::New();
}


void vtkCardinalSplinePatch::Allocate()
{
  if( this->Handles )
    {
    delete [] this->Handles;
    }
  this->Handles = new double [ 3 * this->NumberOfHandlesU * NumberOfHandlesV ];
  this->CreateSplines();
}

void vtkCardinalSplinePatch::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  os << indent << "Number Of Handles U: " << this->NumberOfHandlesU << "\n";
  os << indent << "Number Of Handles V: " << this->NumberOfHandlesV << "\n";
}

void vtkCardinalSplinePatch::SetHandlePosition(unsigned int u,unsigned int v, double xyz[3])
{
  this->SetHandlePosition(u,v,xyz[0],xyz[1],xyz[2]);
}

void vtkCardinalSplinePatch::SetHandlePosition(unsigned int u,unsigned int v, double x, double y, double z)
{
  if( u >= this->NumberOfHandlesU )
    {
    return;
    }
  if( v >= this->NumberOfHandlesV )
    {
    return;
    }
  if( !this->Handles )
    {
    this->Allocate();
    }
  double * ptr = this->Handles + 3 * ( u + v * this->NumberOfHandlesU );
  *ptr++ = x;
  *ptr++ = y;
  *ptr++ = z;
}

void vtkCardinalSplinePatch::GetHandlePosition(unsigned int u,unsigned int v, double xyz[3])
{
  if( u >= this->NumberOfHandlesU )
    {
    return;
    }
  if( v >= this->NumberOfHandlesV )
    {
    return;
    }
  double * ptr = this->Handles + 3 * ( u + v * this->NumberOfHandlesU );
  xyz[0] = *ptr++;
  xyz[1] = *ptr++;
  xyz[2] = *ptr++;
}

double * vtkCardinalSplinePatch::GetHandlePosition(unsigned int u,unsigned int v)
{
  if( u >= this->NumberOfHandlesU )
    {
    return 0;
    }
  if( v >= this->NumberOfHandlesV )
    {
    return 0;
    }
  double * ptr = this->Handles + 3 * ( u + v * this->NumberOfHandlesU );
  return ptr;
}

void vtkCardinalSplinePatch::ReleaseAllSplines(void)
{
  const unsigned int numberOfSplines = this->XSpline->size();

  for(unsigned ns=0; ns<numberOfSplines; ns++)
    {
    if( (*this->XSpline)[ns] )
      {
      (*this->XSpline)[ns]->UnRegister(this);
      }
    if( (*this->YSpline)[ns] )
      {
      (*this->YSpline)[ns]->UnRegister(this);
      }
    if( (*this->ZSpline)[ns] )
      {
      (*this->ZSpline)[ns]->UnRegister(this);
      }
    }
  this->XSpline->clear();
  this->YSpline->clear();
  this->ZSpline->clear();

  if ( this->VXSpline)
    {
    this->VXSpline->UnRegister(this);
    }
  if ( this->VYSpline)
    {
    this->VYSpline->UnRegister(this);
    }
  if ( this->VZSpline)
    {
    this->VZSpline->UnRegister(this);
    }
}


void vtkCardinalSplinePatch::CreateSplines(void)
{
  this->ReleaseAllSplines();

  const unsigned int numberOfSplines = this->NumberOfHandlesU;

  this->XSpline->resize( numberOfSplines );
  this->YSpline->resize( numberOfSplines );
  this->ZSpline->resize( numberOfSplines );

  for(unsigned ns=0; ns<numberOfSplines; ns++)
    {
    (*this->XSpline)[ns] = this->CreateDefaultSpline();
    (*this->YSpline)[ns] = this->CreateDefaultSpline();
    (*this->ZSpline)[ns] = this->CreateDefaultSpline();
    (*this->XSpline)[ns]->Register(this);
    (*this->YSpline)[ns]->Register(this);
    (*this->ZSpline)[ns]->Register(this);
    (*this->XSpline)[ns]->Delete();
    (*this->YSpline)[ns]->Delete();
    (*this->ZSpline)[ns]->Delete();

    (*this->XSpline)[ns]->ClosedOff();
    (*this->YSpline)[ns]->ClosedOff();
    (*this->ZSpline)[ns]->ClosedOff();
    }

  this->VXSpline = this->CreateDefaultSpline();
  this->VYSpline = this->CreateDefaultSpline();
  this->VZSpline = this->CreateDefaultSpline();
  this->VXSpline->Register(this);
  this->VYSpline->Register(this);
  this->VZSpline->Register(this);
  this->VXSpline->Delete();
  this->VYSpline->Delete();
  this->VZSpline->Delete();

  this->VXSpline->ClosedOff();
  this->VYSpline->ClosedOff();
  this->VZSpline->ClosedOff();
}

// This must be invoked when a handle changes position.
void vtkCardinalSplinePatch::Compute(void)
{
  // Remove all the points
  // Initialize them with handle coordinates.
  for(unsigned iu=0; iu<this->NumberOfHandlesU; iu++)
    {
    (*this->XSpline)[iu]->RemoveAllPoints();
    (*this->YSpline)[iu]->RemoveAllPoints();
    (*this->ZSpline)[iu]->RemoveAllPoints();
    for(unsigned iv=0; iv<this->NumberOfHandlesV; iv++)
      {
      double * ptr = this->Handles + 3 * ( iu + iv * this->NumberOfHandlesU );
      (*this->XSpline)[iu]->AddPoint(iv,*ptr++);
      (*this->YSpline)[iu]->AddPoint(iv,*ptr++);
      (*this->ZSpline)[iu]->AddPoint(iv,*ptr++);
      }
    (*this->XSpline)[iu]->Compute();
    (*this->YSpline)[iu]->Compute();
    (*this->ZSpline)[iu]->Compute();
    }
}

void vtkCardinalSplinePatch::Evaluate(double u, double v,double * point)
{
  if( u<0.0 || u>this->NumberOfHandlesU || 
      v<0.0 || v>this->NumberOfHandlesV || point==NULL )
    {
    return;
    }
  this->VXSpline->RemoveAllPoints();
  this->VYSpline->RemoveAllPoints();
  this->VZSpline->RemoveAllPoints();

  double node[3];
  for(unsigned iu=0; iu<this->NumberOfHandlesU; iu++)
    {
    node[0] = (*this->XSpline)[iu]->Evaluate(v),
    node[1] = (*this->YSpline)[iu]->Evaluate(v),
    node[2] = (*this->ZSpline)[iu]->Evaluate(v);
    this->VXSpline->AddPoint(iu,node[0]);
    this->VYSpline->AddPoint(iu,node[1]);
    this->VZSpline->AddPoint(iu,node[2]);
    }

  point[0] = this->VXSpline->Evaluate(u);
  point[1] = this->VYSpline->Evaluate(u);
  point[2] = this->VZSpline->Evaluate(u);
}


void vtkCardinalSplinePatch::PrepareToEvaluateAlongV(double v)
{
  if( v<0.0 || v>this->NumberOfHandlesV )
    {
    return;
    }
  this->VXSpline->RemoveAllPoints();
  this->VYSpline->RemoveAllPoints();
  this->VZSpline->RemoveAllPoints();

  double node[3];
  for(unsigned iu=0; iu<this->NumberOfHandlesU; iu++)
    {
    node[0] = (*this->XSpline)[iu]->Evaluate(v),
    node[1] = (*this->YSpline)[iu]->Evaluate(v),
    node[2] = (*this->ZSpline)[iu]->Evaluate(v);
    this->VXSpline->AddPoint(iu,node[0]);
    this->VYSpline->AddPoint(iu,node[1]);
    this->VZSpline->AddPoint(iu,node[2]);
    }
}


void vtkCardinalSplinePatch::EvaluateAfterFixingV(double u, double * point)
{
  if( u<0.0 || u>this->NumberOfHandlesU || point==NULL )
    {
    return;
    }
  point[0] = this->VXSpline->Evaluate(u);
  point[1] = this->VYSpline->Evaluate(u);
  point[2] = this->VZSpline->Evaluate(u);
}

