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

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

#include "vtkCommand.h"
#include "vtkCallbackCommand.h"
#include "vtkSplineSurface2DWidget.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 <vtkstd/algorithm>

vtkCxxRevisionMacro(vtkKW2DSplineSurfacesWidget, "$Revision: 1.19 $");
vtkStandardNewMacro(vtkKW2DSplineSurfacesWidget);

 
//----------------------------------------------------------------------------
vtkKW2DSplineSurfacesWidget::vtkKW2DSplineSurfacesWidget()
{
  this->EventCallbackCommand->SetCallback(this->ProcessEvents);
  this->EventCallbackCommand->SetClientData((void *)this);
}

//----------------------------------------------------------------------------
vtkKW2DSplineSurfacesWidget::~vtkKW2DSplineSurfacesWidget()
{
  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 vtkKW2DSplineSurfacesWidget::SetSplineSurfaces3D( vtkKW3DSplineSurfacesWidget * surfaces )
{
  if( !surfaces )
    {
    return;
    }

  this->SplineSurfaces3D = surfaces;

  vtkKW3DSplineSurfacesWidget::Iterator itr = this->SplineSurfaces3D->Begin();
  vtkKW3DSplineSurfacesWidget::Iterator end = this->SplineSurfaces3D->End();
  
  while(itr!=end)
    {
    this->AddSplineSurface( itr->first, itr->second );
    ++itr;
    }

  this->SplineSurfaces3D->AddObserver( vtkKW3DSplineSurfacesWidget::AddSplineSurfaceEvent, 
                                       this->EventCallbackCommand, 
                                       this->Priority );
  this->SplineSurfaces3D->AddObserver( vtkKW3DSplineSurfacesWidget::RemoveSplineSurfaceEvent, 
                                       this->EventCallbackCommand, 
                                       this->Priority );
  // Force enabling.
  this->SetEnabled( 1 );
}


//----------------------------------------------------------------------------
void vtkKW2DSplineSurfacesWidget::AddSplineSurface(const char * name, vtkSplineSurfaceWidget * spline )
{
  MapKeyType splinename = name;
  this->AddSplineSurface(splinename,spline);
}

//----------------------------------------------------------------------------
void vtkKW2DSplineSurfacesWidget::AddSplineSurface(const MapKeyType & splinename, vtkSplineSurfaceWidget * spline )
{
  // First check if it is not there already
  Iterator splineItr = this->SplineSurfaces.find( splinename );
  Iterator splineEnd = this->SplineSurfaces.end();
  if( splineItr != splineEnd )
    {
    // There is already a surface with this string Id.
    return;
    }
  
  vtkSplineSurface2DWidget*   splineSurface = vtkSplineSurface2DWidget::New();

  splineSurface->SetRemoteMode(1);

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

  splineSurface->AddObserver( vtkSplineSurface2DWidget::SplineSurface2DHandleChangedEvent, 
                              this->EventCallbackCommand, 
                              this->Priority );

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

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

  this->SplineSurfaces[splinename] = splineSurface;

  splineSurface->GetSurfaceProperty()->SetLineWidth(2.0);
  splineSurface->SetSplineSurfaceWidget( spline );
  splineSurface->SetNormal( this->Normal );
  splineSurface->SetOrigin( this->Origin );

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

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

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

//----------------------------------------------------------------------------
void vtkKW2DSplineSurfacesWidget::SetNormal( double normal[3] )
{
  this->Normal[0] = normal[0];
  this->Normal[1] = normal[1];
  this->Normal[2] = normal[2];
  Iterator splineSurface = this->SplineSurfaces.begin();
  Iterator endSpline     = this->SplineSurfaces.end();
  while( splineSurface != endSpline )
    {
    splineSurface->second->SetNormal( normal );
    ++splineSurface;
    }
}

//----------------------------------------------------------------------------
void vtkKW2DSplineSurfacesWidget::SetOrigin( double origin[3] )
{
  this->Origin[0] = origin[0];
  this->Origin[1] = origin[1];
  this->Origin[2] = origin[2];
  Iterator splineSurface = this->SplineSurfaces.begin();
  Iterator endSpline     = this->SplineSurfaces.end();
  while( splineSurface != endSpline )
    {
    splineSurface->second->SetOrigin( origin );
    ++splineSurface;
    }
}


//----------------------------------------------------------------------------
void vtkKW2DSplineSurfacesWidget::RemoveSplineSurface(vtkSplineSurfaceWidget * surface)
{
  // Remove it from the container
  Iterator splineSurface = this->SplineSurfaces.begin();
  Iterator splineEnd     = this->SplineSurfaces.end();
  while( splineSurface != splineEnd )
    {
    if( splineSurface->second->GetSplineSurfaceWidget() == surface )
      {
      splineSurface->second->Delete();
      this->SplineSurfaces.erase( splineSurface );
      // Then select another one as active.
      return;
      }
    ++splineSurface;
    }
}

//----------------------------------------------------------------------------
void vtkKW2DSplineSurfacesWidget::RemoveSplineSurfaceId(const char * surfaceId)
{
  if( this->HasSplineSurface(surfaceId) )
    {
    // Remove it from the container
    MapKeyType splinename = surfaceId;
    Iterator splineSurface = this->SplineSurfaces.find(splinename);
    splineSurface->second->Delete();
    this->SplineSurfaces.erase( splineSurface );

    // Then select another one as active.
    }
}

//----------------------------------------------------------------------------
void vtkKW2DSplineSurfacesWidget::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 vtkKW2DSplineSurfacesWidget::GetNumberOfSplineSurfaces()
{
  return (unsigned int)this->SplineSurfaces.size();
}

//----------------------------------------------------------------------------
vtkSplineSurface2DWidget * 
vtkKW2DSplineSurfacesWidget::GetSplineSurfaceWidget(const char * splineId)
{
  if( !this->HasSplineSurface(splineId) )
    {
    return 0;
    }
  return this->SplineSurfaces[splineId];
}

//----------------------------------------------------------------------------
int vtkKW2DSplineSurfacesWidget::HasSplineSurface(const char * surfaceId)
{
  MapKeyType splinename = surfaceId;
  Iterator iter = this->SplineSurfaces.find(splinename);
  Iterator end  = this->SplineSurfaces.end();
  if( iter != end )
    {
    return 1;
    }
  return 0;
}

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

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

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

//----------------------------------------------------------------------------
void
vtkKW2DSplineSurfacesWidget::SetSplineSurfaceVisibility(int v)
{
  // Propagate  Visibility to all the existing spline curves
  Iterator splineSurface = this->SplineSurfaces.begin();
  Iterator endSpline   = this->SplineSurfaces.end();
  while( splineSurface != endSpline )
    {
    splineSurface->second->SetEnabled(v);
    ++splineSurface;
    }
}

//----------------------------------------------------------------------------
void
vtkKW2DSplineSurfacesWidget::SetSplineSurfaceVisibility(const char * surfaceId,int v)
{
  if( this->HasSplineSurface(surfaceId) )
    {
    this->SplineSurfaces[surfaceId]->SetEnabled(v);
    }
}

//----------------------------------------------------------------------------
void vtkKW2DSplineSurfacesWidget::ProcessEvents(vtkObject* vtkNotUsed(object),
                                  unsigned long event,
                                  void* clientdata,
                                  void* calldata)
{
  vtkKW2DSplineSurfacesWidget* self = reinterpret_cast<vtkKW2DSplineSurfacesWidget *>( clientdata );

  switch(event)
    {
    case vtkKW3DSplineSurfacesWidget::AddSplineSurfaceEvent:
      {
      char ** indata = (char **) calldata;
      char * surfaceName = (char *)(indata[0]);
      vtkSplineSurfaceWidget * surfaceToAdd = (vtkSplineSurfaceWidget *)(indata[1]);
      self->AddSplineSurface( surfaceName, surfaceToAdd );
      break;
      }
    case vtkKW3DSplineSurfacesWidget::RemoveSplineSurfaceEvent:
      vtkSplineSurfaceWidget * surfaceToRemove = (vtkSplineSurfaceWidget *)(calldata);
      self->RemoveSplineSurface( surfaceToRemove );
      break;
    }
}


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

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

