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

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

#include "vtkCommand.h"
#include "vtkCallbackCommand.h"
#include "vtkSplineSurfaceWidget.h"
#include "vtkIrregularSplineSurfaceWidget.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkProperty.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"
#include "vtkRegularSplineSurfaceWidget.h"
#include "vtkSubdivisionSplineSurfaceWidget.h"

#include <vtksys/SystemTools.hxx>
#include <vtkstd/algorithm>

vtkCxxRevisionMacro(vtkKW3DSplineSurfacesWidget, "$Revision: 1.41 $");
vtkStandardNewMacro(vtkKW3DSplineSurfacesWidget);

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLKW3DSplineSurfacesWidgetReader.h"
#include "XML/vtkXMLKW3DSplineSurfacesWidgetWriter.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtkKW3DSplineSurfacesWidget, vtkXMLKW3DSplineSurfacesWidgetReader, vtkXMLKW3DSplineSurfacesWidgetWriter);
 
//----------------------------------------------------------------------------
vtkKW3DSplineSurfacesWidget::vtkKW3DSplineSurfacesWidget()
{
  this->EventCallbackCommand->SetCallback(this->ProcessEvents);
  this->EventCallbackCommand->SetClientData((void *)this);
}

//----------------------------------------------------------------------------
vtkKW3DSplineSurfacesWidget::~vtkKW3DSplineSurfacesWidget()
{
  if( this->GetEnabled() )
    {
    this->SetEnabled(0);
    }
  
  Iterator splineSurface = this->SplineSurfaces.begin();
  Iterator endSpline     = this->SplineSurfaces.end();
  while( splineSurface != endSpline )
    {
    splineSurface->second->Delete();
    ++splineSurface;
    }

  this->SplineSurfaces.clear();
}


//----------------------------------------------------------------------------
void vtkKW3DSplineSurfacesWidget::AddSplineSurface(const char * name)
{
  vtkSubdivisionSplineSurfaceWidget*   splineSurface = vtkSubdivisionSplineSurfaceWidget::New();

  splineSurface->SetRemoteMode(1);

  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurfaceHandleChangedEvent, 
                              this->EventCallbackCommand, 
                              this->Priority );
  
  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurfaceHandlePositionChangedEvent, 
                              this->EventCallbackCommand, 
                              this->Priority );

  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurface2DHandlePositionChangedEvent, 
                              this->EventCallbackCommand, 
                              this->Priority );


  splineSurface->GetSurfaceProperty()->SetLineWidth(2.0);

  if( this->Interactor )
    {
    splineSurface->SetInteractor(this->Interactor);  
    splineSurface->SetEnabled(this->Enabled);
    }

  // splineSurface->Delete() -- no delete because the vector has it

  MapKeyType splinename = name;
  this->SplineSurfaces[splinename] = splineSurface;

  typedef const char * charptr;
  charptr * cargs = new charptr[2];
  cargs[0] = name;
  cargs[1] = (charptr)splineSurface;

  this->InvokeEvent(AddSplineSurfaceEvent,cargs);

  delete [] cargs;

}

//----------------------------------------------------------------------------
void vtkKW3DSplineSurfacesWidget::AddSplineSurface( double bounds[6], const char * name )
{
  if( !name )
    {
    return;
    }

  vtkSubdivisionSplineSurfaceWidget*   splineSurface = vtkSubdivisionSplineSurfaceWidget::New();

  splineSurface->SetRemoteMode(1);
  
  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurfaceHandleChangedEvent, 
                              this->EventCallbackCommand,
                              this->Priority );

  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurfaceHandlePositionChangedEvent, 
                              this->EventCallbackCommand, 
                              this->Priority );

  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurface2DHandlePositionChangedEvent, 
                              this->EventCallbackCommand, 
                              this->Priority );

  splineSurface->PlaceWidget( bounds[0],
                              bounds[1],
                              bounds[2],
                              bounds[3],
                              bounds[4],
                              bounds[5] );

  splineSurface->GetSurfaceProperty()->SetLineWidth(2.0);

  if( this->Interactor )
    {
    splineSurface->SetInteractor(this->Interactor);  
    splineSurface->SetEnabled(this->Enabled);
    }

  // splineSurface->Delete() -- no delete because the vector has it

  MapKeyType splinename = name;
  this->SplineSurfaces[splinename] = splineSurface;

  typedef const char * charptr;
  charptr * cargs = new charptr[2];
  cargs[0] = name;
  cargs[1] = (charptr)splineSurface;

  this->InvokeEvent(AddSplineSurfaceEvent,cargs);

  delete [] cargs;

}



//----------------------------------------------------------------------------
void vtkKW3DSplineSurfacesWidget::AddIrregularSplineSurface(const char * surfaceName)
{
  vtkIrregularSplineSurfaceWidget*   splineSurface = vtkIrregularSplineSurfaceWidget::New();

  splineSurface->SetRemoteMode(1);

  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurfaceHandleChangedEvent, 
                              this->EventCallbackCommand, 
                              this->Priority );

  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurfaceHandlePositionChangedEvent, 
                              this->EventCallbackCommand, 
                              this->Priority );

  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurface2DHandlePositionChangedEvent, 
                              this->EventCallbackCommand, 
                              this->Priority );

  splineSurface->GetSurfaceProperty()->SetLineWidth(2.0);

  if( this->Interactor )
    {
    splineSurface->SetInteractor(this->Interactor);  
    splineSurface->SetEnabled(this->Enabled);
    }

  // splineSurface->Delete() -- no delete because the vector has it

  MapKeyType splinename = surfaceName;
  this->SplineSurfaces[splinename] = splineSurface;

  typedef const char * charptr;
  charptr * cargs = new charptr[2];
  cargs[0] = surfaceName;
  cargs[1] = (charptr)splineSurface;

  this->InvokeEvent(AddSplineSurfaceEvent,cargs);

  delete [] cargs;

}

//----------------------------------------------------------------------------
void vtkKW3DSplineSurfacesWidget::AddIrregularSplineSurface( double bounds[6], const char * name )
{
  if( !name )
    {
    return;
    }

  vtkIrregularSplineSurfaceWidget*   splineSurface = vtkIrregularSplineSurfaceWidget::New();

  splineSurface->SetRemoteMode(1);

  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurfaceHandleChangedEvent, 
                              this->EventCallbackCommand,
                              this->Priority );

  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurfaceHandlePositionChangedEvent, 
                              this->EventCallbackCommand, 
                              this->Priority );

  splineSurface->AddObserver( vtkSplineSurfaceWidget::SplineSurface2DHandlePositionChangedEvent, 
                              this->EventCallbackCommand, 
                              this->Priority );

  splineSurface->PlaceWidget( bounds[0],
                              bounds[1],
                              bounds[2],
                              bounds[3],
                              bounds[4],
                              bounds[5] );

  splineSurface->GetSurfaceProperty()->SetLineWidth(2.0);

  if( this->Interactor )
    {
    splineSurface->SetInteractor(this->Interactor);  
    splineSurface->SetEnabled(this->Enabled);
    }

  // splineSurface->Delete() -- no delete because the vector has it

  MapKeyType splinename = name;
  this->SplineSurfaces[splinename] = splineSurface;

  typedef const char * charptr;
  charptr * cargs = new charptr[2];
  cargs[0] = name;
  cargs[1] = (charptr)splineSurface;

  this->InvokeEvent(AddSplineSurfaceEvent,cargs);

  delete [] cargs;

}


//----------------------------------------------------------------------------
void vtkKW3DSplineSurfacesWidget::PlaceWidget( double bounds[6] )
{
  Iterator splineSurface = this->SplineSurfaces.begin();
  Iterator endSpline     = this->SplineSurfaces.end();
  while( splineSurface != endSpline )
    {
    splineSurface->second->PlaceWidget( bounds );
    ++splineSurface;
    }
}


//----------------------------------------------------------------------------
void vtkKW3DSplineSurfacesWidget::RemoveSplineSurfaceId(const char * name)
{
  if( this->HasSplineSurface(name) )
    {
    // Remove it from the container
    MapKeyType surfacename = name;
    Iterator splineSurface = this->SplineSurfaces.find(surfacename);
    Iterator endSurface    = this->SplineSurfaces.end();

    if( splineSurface == endSurface )
      {
      return;
      }

    this->InvokeEvent(RemoveSplineSurfaceEvent,(void*)(splineSurface->second));
    splineSurface->second->SetEnabled(0);
    splineSurface->second->Delete();
    this->SplineSurfaces.erase( splineSurface );

    }
}

//----------------------------------------------------------------------------
void vtkKW3DSplineSurfacesWidget::RemoveAllSplineSurfaces()
{
  // Remove all spline surfaces from the container
  Iterator splineSurface = this->SplineSurfaces.begin();
  Iterator endSurface    = this->SplineSurfaces.end();

  while( splineSurface != endSurface )
    {
    this->InvokeEvent(RemoveSplineSurfaceEvent,(void*)(splineSurface->second));
    splineSurface->second->SetEnabled(0);
    splineSurface->second->Delete();
    ++splineSurface;
    }
  this->SplineSurfaces.clear();
}

//----------------------------------------------------------------------------
void vtkKW3DSplineSurfacesWidget::SetEnabled(int enabling)
{
  if ( ! this->Interactor )
    {
    vtkErrorMacro(<<"The interactor must be set prior to enabling/disabling widget");
    return;
    }
  
  if ( enabling ) 
    {
    vtkDebugMacro(<<"Enabling distance widget");
    if ( this->Enabled ) //already enabled, just return
      {
      return;
      }
    
    this->SetCurrentRenderer( 
      this->Interactor->FindPokedRenderer(
        this->Interactor->GetLastEventPosition()[0],
        this->Interactor->GetLastEventPosition()[1]));
    if (this->CurrentRenderer == NULL)
      {
      return;
      }
    
    this->Enabled = 1;

    this->InvokeEvent(vtkCommand::EnableEvent,NULL);
    }
  else //disabling------------------------------------------
    {
    vtkDebugMacro(<<"Disabling 3D Spline Surfaces widget");
    if ( ! this->Enabled ) //already disabled, just return
      {
      return;
      }
    this->Enabled = 0;

    this->InvokeEvent(vtkCommand::DisableEvent,NULL);
    }

  // Propagate Enabling/Disabling to all the existing spline curves
  Iterator splineSurface = this->SplineSurfaces.begin();
  Iterator endSpline   = this->SplineSurfaces.end();
  while( splineSurface != endSpline )
    {
    splineSurface->second->SetEnabled(this->Enabled);
    ++splineSurface;
    }

}


//----------------------------------------------------------------------------
unsigned int vtkKW3DSplineSurfacesWidget::GetNumberOfSplineSurfaces()
{
  return (unsigned int)this->SplineSurfaces.size();
}

//----------------------------------------------------------------------------
unsigned int vtkKW3DSplineSurfacesWidget::GetNumberOfPointsInASplineSurface(const char * id)
{
  if( !this->HasSplineSurface(id) )
    {
    return 0;
    }
  const unsigned int np = this->SplineSurfaces[id]->GetNumberOfSurfacePoints(); 
  return np;
}

//----------------------------------------------------------------------------
vtkSplineSurfaceWidget * 
vtkKW3DSplineSurfacesWidget::GetSplineSurfaceWidget(const char * id)
{
  if( !this->HasSplineSurface(id) )
    {
    return 0;
    }
  return this->SplineSurfaces[id];
}

//----------------------------------------------------------------------------
vtkPoints *
vtkKW3DSplineSurfacesWidget::GetPointsInASplineSurface(const char * id)
{
  if( !this->HasSplineSurface(id) )
    {
    return 0;
    }
  vtkPoints * points = this->SplineSurfaces[id]->GetSurfaceData()->GetPoints(); 
  return points;
}


//----------------------------------------------------------------------------
int vtkKW3DSplineSurfacesWidget::HasSplineSurface(const char * surfaceId)
{
  if( surfaceId == 0 )
    {
    return 0;
    }
  Iterator endSpline     = this->SplineSurfaces.end();
  Iterator itrSpline     = this->SplineSurfaces.find(surfaceId);
  if( itrSpline != endSpline )
    {
    return 1;
    }
  return 0;
}

//----------------------------------------------------------------------------
vtkProperty * 
vtkKW3DSplineSurfacesWidget::GetSplineSurfaceProperty(const char * surfaceId)
{
  if( !this->HasSplineSurface(surfaceId) )
    {
    return NULL;
    }
  return this->SplineSurfaces[surfaceId]->GetSurfaceProperty();
}

//----------------------------------------------------------------------------
void
vtkKW3DSplineSurfacesWidget::SetSplineSurfaceProperty(const char * surfaceId, vtkProperty * property)
{
  if( !this->HasSplineSurface(surfaceId) )
    {
    return;
    }
  this->SplineSurfaces[surfaceId]->SetSurfaceProperty(property);
}

//----------------------------------------------------------------------------
int
vtkKW3DSplineSurfacesWidget::GetSplineSurfaceVisibility(const char * surfaceId)
{
  if( !this->HasSplineSurface(surfaceId) )
    {
    return 0;
    }
  return this->SplineSurfaces[surfaceId]->GetEnabled();
}

//----------------------------------------------------------------------------
void
vtkKW3DSplineSurfacesWidget::SetSplineSurfaceVisibility(const char * surfaceId,int v)
{

  // This code is temporary: while we add support for multiple surfaces

  if( this->HasSplineSurface(surfaceId) )
    {
    this->SplineSurfaces[surfaceId]->SetEnabled(v);
    }
}

//----------------------------------------------------------------------------
void
vtkKW3DSplineSurfacesWidget::SetSplineSurfaceNumberOfHandles(const char * surfaceId,int nh)
{
  if( this->HasSplineSurface(surfaceId) )
    {
    vtkSplineSurfaceWidget * surface = this->SplineSurfaces[surfaceId];
    if( surface )
      {
      surface->SetNumberOfHandles(nh);
      }
    }
}


//----------------------------------------------------------------------------
int
vtkKW3DSplineSurfacesWidget::GetNumberOfHandles(const char * surfaceId)
{
  if( this->HasSplineSurface(surfaceId) )
    {
    vtkSplineSurfaceWidget * surface = this->SplineSurfaces[surfaceId];
    if( !surface )
      {
      return 0;
      }
    else
      {
      return surface->GetNumberOfHandles();
      }
    }
  return 0; 
}


//----------------------------------------------------------------------------
double *
vtkKW3DSplineSurfacesWidget::GetSplineSurfaceControlPoint(const char * surfaceId, vtkIdType pointId)
{
  if( this->HasSplineSurface(surfaceId) )
    {
    return this->SplineSurfaces[surfaceId]->GetHandlePosition(pointId);
    }
  return 0; 
}


//----------------------------------------------------------------------------
void
vtkKW3DSplineSurfacesWidget::SetSplineSurfaceControlPoint(const char * surfaceId, vtkIdType pointId, double * pointCoordinates)
{
  if( this->HasSplineSurface(surfaceId) )
    {
    this->SplineSurfaces[surfaceId]->SetHandlePosition(pointId, pointCoordinates);
    }
}

//----------------------------------------------------------------------------
void
vtkKW3DSplineSurfacesWidget::SetSplineSurfaceControlPoints(const char * surfaceId, float * allPointCoordinates)
{
  if( this->HasSplineSurface(surfaceId) )
    {
    this->SplineSurfaces[surfaceId]->SetHandlePositions(allPointCoordinates);
    }
}


//----------------------------------------------------------------------------
void
vtkKW3DSplineSurfacesWidget::ProcessEvents(vtkObject* object,
                                  unsigned long event,
                                  void* clientdata,
                                  void* vtkNotUsed(calldata))
{
  if( !clientdata )
    {
    return; 
    }
  vtkKW3DSplineSurfacesWidget * widget = (vtkKW3DSplineSurfacesWidget*)clientdata;

  if( !object )
    {
    return;
    }
  vtkSplineSurfaceWidget * surfaceWidget = (vtkSplineSurfaceWidget *)object;

  // Find the Id of the spline surface that changed
  int found = 0;
  Iterator itrSpline = widget->SplineSurfaces.begin();
  Iterator endSpline = widget->SplineSurfaces.end();
  while( itrSpline != endSpline )
    {
    if( surfaceWidget == itrSpline->second )
      {
      found=1;
      break;
      }
    ++itrSpline;
    }

  if( !found )
    {
    return; 
    }

  unsigned int numberOfHandles = surfaceWidget->GetNumberOfHandles();

  typedef struct { 
    char * surfaceId; 
    int numberOfPoints; // this covers the case of removal/insertion
    float * coordinates; 
                 } coordinatesCarrier;

  coordinatesCarrier carrier;

  const unsigned int numberOfCoordinates =  3 * numberOfHandles;

  carrier.numberOfPoints = numberOfHandles;
  carrier.coordinates    = new float[ numberOfCoordinates ];  
  carrier.surfaceId      = 
    vtksys::SystemTools::DuplicateString(itrSpline->first.c_str()); 

  // Store the surfaceId pointer to string in the first element of the array.
  // Then, store the handle coordinates as floats.
  surfaceWidget->GetHandlePositions( carrier.coordinates );
  
  widget->InvokeEvent( event, (void*)(&carrier) );
  
  delete [] carrier.surfaceId;
  delete [] carrier.coordinates;

}

//----------------------------------------------------------------------------
vtkKW3DSplineSurfacesWidget::Iterator
vtkKW3DSplineSurfacesWidget::Begin()
{
  return this->SplineSurfaces.begin();
}

//----------------------------------------------------------------------------
vtkKW3DSplineSurfacesWidget::Iterator
vtkKW3DSplineSurfacesWidget::End()
{
  return this->SplineSurfaces.end();
}


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

  os << indent << "Number of Spline Surfaces: " << this->GetNumberOfSplineSurfaces() << endl;
}

