#include "vtkInteractorStyle.h"

#define VTKIS_CAMERA 0
#define VTKIS_ACTOR  1
#define SPACE ' '  
#define VTKIS_STEREO  8 

#include "vtkObjectFactory.h"
#include "vtkCommand.h"


class eosInteractorMyStyle : public vtkInteractorStyle
{
public:
  static eosInteractorMyStyle *New();
  vtkTypeMacro(eosInteractorMyStyle, vtkInteractorStyle);

  void UpdateInternalState(int ctrl, int shift,int X, int Y); 
  void OnLeftButtonDown(int ctrl, int shift,int x, int y);
  void OnLeftButtonUp(int ctrl, int shift,int x, int y);

  void OnMouseMove(int ctrl, int shift, int x, int y);
  void OnChar   (int ctrl, int shift, char keycode, int repeatcount);

  void SetInteractor(vtkRenderWindowInteractor *iren);
  void SetAutoAdjustCameraClippingRange( int value );
  void ResetCameraClippingRange(void);
  void StartState(int);
  void StopState(int);
  void StartStereo(void);
  void EndStereo(void);  
  void SetRenderWindows(vtkRenderer*,vtkRenderer*,vtkRenderer*,vtkRenderWindowInteractor*,vtkRenderWindowInteractor*);
  void OnTimer();
  void StereoCamera(int,int);

protected:
  eosInteractorMyStyle();  
  ~eosInteractorMyStyle();  

  vtkRenderer *Ren,*RenLeft,*RenRight;
  vtkCamera  *Cam,*CamLeft,*CamRight,*cam;
  vtkRenderWindowInteractor *rwi1,*rwi2;
  int space;
};

//---------------------------------------------------------------------------
eosInteractorMyStyle *eosInteractorMyStyle::New() 
{
// First try to create the object from the vtkObjectFactory
  vtkObject* ret = vtkObjectFactory::CreateInstance("eosInteractorMyStyle");

  if(ret)
    {
    return (eosInteractorMyStyle*)ret;
    }
  // If the factory was unable to create the object, then create it here.
  return new eosInteractorMyStyle;
}


//----------------------------------------------------------------------------
eosInteractorMyStyle::eosInteractorMyStyle():vtkInteractorStyle() 
{
  this->space=0;
}

//----------------------------------------------------------------------------
eosInteractorMyStyle::~eosInteractorMyStyle() 
{
  if(space)
    {
      if ( this->OutlineActor ) 
	{
	  // if we change style when an object is selected, we must remove the
	  // actor from the renderer
	  if (this->CurrentRenderer) 
	    {
	      Ren->RemoveActor(this->OutlineActor);
	      RenLeft->RemoveActor(this->OutlineActor);
	      RenRight->RemoveActor(this->OutlineActor);
	    }
	  this->OutlineActor->Delete();
	}
      if ( this->OutlineMapper ) 
	{
	  this->OutlineMapper->Delete();
	}
      this->Outline->Delete();
      this->Outline = NULL;
      
      if ( this->CurrentRenderer)
	{
	  Ren->UnRegister(this);
	  Ren = NULL;
	  RenLeft->UnRegister(this);
	  RenLeft = NULL;
	  RenRight->UnRegister(this);
	  RenRight = NULL;
	}
    }

  else
    {
      if ( this->OutlineActor ) 
	{
	  // if we change style when an object is selected, we must remove the
	  // actor from the renderer
	  if (this->CurrentRenderer) 
	    {
	      this->CurrentRenderer->RemoveActor(this->OutlineActor);
	    }
	  this->OutlineActor->Delete();
	}
      if ( this->OutlineMapper ) 
	{
	  this->OutlineMapper->Delete();
	}
      this->Outline->Delete();
      this->Outline = NULL;
      
      if ( this->CurrentRenderer)
	{
	  this->CurrentRenderer->UnRegister(this);
	  this->CurrentRenderer = NULL;
	}     
      
    }
}

//----------------------------------------------------------------------------
void eosInteractorMyStyle::UpdateInternalState(int ctrl, int shift, 
                                             int X, int Y) 
{
  vtkInteractorStyle::UpdateInternalState(ctrl, shift , X, Y) ;

}

//----------------------------------------------------------------------------

void eosInteractorMyStyle::SetAutoAdjustCameraClippingRange( int value )
{
  if ( value == this->AutoAdjustCameraClippingRange )
    {
    return;
    }
  
  if ( value < 0 || value > 1 )
    {
    vtkErrorMacro("Value must be between 0 and 1 for" <<
                  " SetAutoAdjustCameraClippingRange");
    return;
    }
  
  this->AutoAdjustCameraClippingRange = value;

  this->Modified();
}

//----------------------------------------------------------------------------
void eosInteractorMyStyle::ResetCameraClippingRange()
{
  if (space)
    {
      if ( this->AutoAdjustCameraClippingRange )
	{
	  Ren->ResetCameraClippingRange();
	  RenLeft->ResetCameraClippingRange();
	  RenRight->ResetCameraClippingRange();
	}     
    }
  else
    {
      if ( this->AutoAdjustCameraClippingRange )
	{
	  this->CurrentRenderer->ResetCameraClippingRange();
	}
    }
}
//----------------------------------------------------------------------------
void eosInteractorMyStyle::OnChar(int ctrl, int shift, 
                                      char keycode,
                                      int repeatcount) 
{

  vtkInteractorStyle::OnChar(ctrl, shift, keycode, repeatcount);

  switch (keycode)
    {
    case SPACE:
      if(this->space==0)
	{
	  this->space=1;      
	  this->State = VTKIS_STEREO;
	}
      else              
	{
	  this->space=0;
	  printf("space=0\n");
	  this->State = VTKIS_START;
	}
      
      break;
    }
}



//----------------------------------------------------------------------------
void eosInteractorMyStyle::OnMouseMove(int ctrl, int shift, int x, int y)
{
  // Call the parent so the LastPos is set
  vtkInteractorStyle::OnMouseMove(ctrl, shift, x, y);
}

//----------------------------------------------------------------------------

void eosInteractorMyStyle::OnLeftButtonDown(int ctrl, int shift,
                                                          int x, int y) 
{
  //
  this->UpdateInternalState(ctrl, shift ,x, y);
  //
  this->FindPokedCamera(x, y);


  if (this->HasObserver(vtkCommand::LeftButtonPressEvent)) 
    {
    this->InvokeEvent(vtkCommand::LeftButtonPressEvent,NULL);
    }

  else 
    {
    if (this->ShiftKey) 
      { // I haven't got a Middle button !
      if (this->CtrlKey) 
        {
        this->StartDolly();
        }
      else 
        {
        this->StartPan();
        }
      } 
    else 
      {
      if (this->CtrlKey) 
        {
        this->StartSpin();
        }
      else 
        {
	  if(this->space)
	    {
	      this->StartStereo();
	    }
	  else
	    {
	      this->StartRotate();
	    }
	}
      }
    }



}

//----------------------------------------------------------------------------
void eosInteractorMyStyle::OnLeftButtonUp(int ctrl, int shift,
                                              int x, int y) 
{
  //
 this->UpdateInternalState(ctrl, shift, X, Y);

  //
  if (this->HasObserver(vtkCommand::LeftButtonReleaseEvent)) 
    {
    this->InvokeEvent(vtkCommand::LeftButtonReleaseEvent,NULL);
    }
  else 
    {
    if (this->ShiftKey) 
      {
      if (this->CtrlKey) 
        {
        this->EndDolly();
        }
      else
        {
        this->EndPan();
        }
      } 

    else 
      {
      if (this->CtrlKey) 
        {
        this->EndSpin();
        }
      else
        {
	  if(this->space) 
	    {
	      this->EndStereo();
	    }
	  else
	    {
	      this->EndRotate();
	    }
	}
      }
    }
}


//----------------------------------------------------------------------------
void eosInteractorMyStyle::SetInteractor(vtkRenderWindowInteractor *iren)
{
  this->vtkInteractorStyle::SetInteractor(iren);
}


//----------------------------------------------------------------------------
void  eosInteractorMyStyle::StartState(int newstate) 
{
  vtkRenderWindowInteractor *rwi = this->Interactor;
  this->State = newstate;

  if(this->State==VTKIS_STEREO)
    {
      if (this->AnimState == VTKIS_ANIM_OFF) 
	{	  
	  rwi1->GetRenderWindow()->SetDesiredUpdateRate(rwi1->GetDesiredUpdateRate());
	  rwi2->GetRenderWindow()->SetDesiredUpdateRate(rwi2->GetDesiredUpdateRate());

	  if ( !rwi1->CreateTimer(VTKI_TIMER_FIRST) || !rwi2->CreateTimer(VTKI_TIMER_FIRST)) 
	    {
	      vtkErrorMacro(<< "Timer start failed");
	      this->State = VTKIS_START;
	    }
	}
    }
  else if (this->AnimState == VTKIS_ANIM_OFF) 
    {
      rwi->GetRenderWindow()->SetDesiredUpdateRate(rwi->GetDesiredUpdateRate());
      if ( !rwi->CreateTimer(VTKI_TIMER_FIRST) ) 
	{
	  vtkErrorMacro(<< "Timer start failed");
	  this->State = VTKIS_START;
	}
    }  
}
//----------------------------------------------------------------------------
void  eosInteractorMyStyle::StopState(int a) 
{
  vtkRenderWindowInteractor *rwi = this->Interactor;
  this->State = VTKIS_START;

  if(this->State==VTKIS_STEREO)
    {
      if (this->AnimState == VTKIS_ANIM_OFF) 
	{   
	  rwi1->GetRenderWindow()->SetDesiredUpdateRate(rwi1->GetDesiredUpdateRate());
	  rwi2->GetRenderWindow()->SetDesiredUpdateRate(rwi2->GetDesiredUpdateRate());
	  Ren->GetRenderWindow()->Render();
	  RenLeft->GetRenderWindow()->Render();
	}   
    }
  else
    {
      if (this->AnimState == VTKIS_ANIM_OFF) 
	{   
	  rwi->GetRenderWindow()->SetDesiredUpdateRate(rwi->GetStillUpdateRate());
	  rwi->Render();
	  if ( !rwi->DestroyTimer() ) 
	    {
	      vtkErrorMacro(<< "Timer stop failed");
	    }
	}   
    }
  
}

//----------------------------------------------------------------------------
void  eosInteractorMyStyle::StartStereo()
{
  if (this->State != VTKIS_START) 
    {
      return;
    }
  this->StartState(VTKIS_STEREO);  
}
//----------------------------------------------------------------------------
void  eosInteractorMyStyle::EndStereo()
{
  if (this->State != VTKIS_STEREO) 
    {
    return;
    }
  this->StopState(2);
}

//----------------------------------------------------------------------------
void eosInteractorMyStyle::OnTimer(void) 
{

  vtkRenderWindowInteractor *rwi = this->Interactor;
  switch (this->State) 
    {
    //-----
    case VTKIS_START:
      // JCP Animation control
      if (this->AnimState == VTKIS_ANIM_ON)
        {
	  if(this->space)
	    {
	      rwi1->DestroyTimer();
	      rwi2->DestroyTimer();
	      rwi1->Render();
	      rwi2->Render();
	      rwi1->CreateTimer(VTKI_TIMER_FIRST);
	      rwi2->CreateTimer(VTKI_TIMER_FIRST);
	    }
	  else
	    {
	      rwi->DestroyTimer();
	      rwi->Render();
	      rwi->CreateTimer(VTKI_TIMER_FIRST);
	    }
	}
      // JCP Animation control 
      break;
      //-----
    case VTKIS_ROTATE:  // rotate with respect to an axis perp to look
      this->RotateCamera(this->LastPos[0], this->LastPos[1]);
      rwi->CreateTimer(VTKI_TIMER_UPDATE);
      break;
      //-----
    case VTKIS_PAN: // move perpendicular to camera's look vector
      this->PanCamera(this->LastPos[0], this->LastPos[1]);
      rwi->CreateTimer(VTKI_TIMER_UPDATE);
      break;
      //-----
    case VTKIS_ZOOM:
      this->DollyCamera(this->LastPos[0], this->LastPos[1]);
      rwi->CreateTimer(VTKI_TIMER_UPDATE);
      break;
      //-----
    case VTKIS_SPIN:
      this->SpinCamera(this->LastPos[0], this->LastPos[1]);
      rwi->CreateTimer(VTKI_TIMER_UPDATE);
      break;
      //-----
    case VTKIS_DOLLY:  // move along camera's view vector
      break;
      //-----
    case VTKIS_USCALE:
      break;
      //-----
    case VTKIS_STEREO:
      this->StereoCamera(this->LastPos[0],this->LastPos[1]);
      rwi1->CreateTimer(VTKI_TIMER_UPDATE);
      rwi2->CreateTimer(VTKI_TIMER_UPDATE);
      break;
      //-----
    case VTKIS_TIMER:
      rwi->Render();
      rwi->CreateTimer(VTKI_TIMER_UPDATE);
      break;
       //-----
    default :

      break;
    } 
}

//----------------------------------------------------------------------------
void eosInteractorMyStyle::StereoCamera(int x,int y)
{
  vtkRenderWindowInteractor *rwi = this->Interactor;

  double rxf = (double)(x - this->Center[0]) * this->DeltaAzimuth;
  double ryf = (double)(y - this->Center[1]) * this->DeltaElevation;

  if(this->CurrentCamera==Cam)
    {
      cam = this->CurrentCamera;
      cam->Azimuth(rxf);      
      cam->Elevation(ryf);
      cam->OrthogonalizeViewUp();

      CamLeft->SetPosition(cam->GetPosition());
      CamLeft->SetFocalPoint(cam->GetFocalPoint());
      CamLeft->SetViewUp(cam->GetViewUp());
      CamLeft->Azimuth(3.5+rxf);
      CamLeft->Elevation(ryf);
      CamLeft->OrthogonalizeViewUp();
      
      CamRight->SetPosition(cam->GetPosition());
      CamRight->SetFocalPoint(cam->GetFocalPoint());
      CamRight->SetViewUp(cam->GetViewUp());
      CamRight->Azimuth(-3.5+rxf);
      CamRight->Elevation(ryf);      
      CamRight->OrthogonalizeViewUp();

      //cam=this->CurrentCamera;
      //cam->Azimuth(7.0);
      //RenLeft->ResetCamera();
      //RenLeft->SetActiveCamera(this->Cam);
      //RenRight->ResetCamera();
      //RenRight->SetActiveCamera(cam);          
    }
  else if(this->CurrentCamera==CamLeft)
    {
      cam = this->CurrentCamera;
      cam->Azimuth(rxf);      
      cam->Elevation(ryf);
      cam->OrthogonalizeViewUp();

      CamRight->SetPosition(cam->GetPosition());
      CamRight->SetFocalPoint(cam->GetFocalPoint());
      CamRight->SetViewUp(cam->GetViewUp());
      CamRight->Azimuth(-7.0+rxf);
      CamRight->Elevation(ryf);
      CamRight->OrthogonalizeViewUp();
      
      Cam->SetPosition(cam->GetPosition());
      Cam->SetFocalPoint(cam->GetFocalPoint());
      Cam->SetViewUp(cam->GetViewUp());
      Cam->Azimuth(-3.5+rxf);
      Cam->Elevation(ryf); 
      Cam->OrthogonalizeViewUp();
     
      //cam=this->CurrentCamera;
      //cam->Azimuth(7.0);
      //Ren->ResetCamera();
      //Ren->SetActiveCamera(this->CamLeft);
      //RenRight->ResetCamera();
      //RenRight->SetActiveCamera(cam);          
    }
  else if(this->CurrentCamera==CamRight)
    {
      cam = this->CurrentCamera;
      cam->Azimuth(rxf);      
      cam->Elevation(ryf);
      cam->OrthogonalizeViewUp();

      CamLeft->SetPosition(cam->GetPosition());
      CamLeft->SetFocalPoint(cam->GetFocalPoint());
      CamLeft->SetViewUp(cam->GetViewUp());
      CamLeft->Azimuth(7.0+rxf);
      CamLeft->Elevation(ryf);
      CamLeft->OrthogonalizeViewUp();
      
      Cam->SetPosition(cam->GetPosition());
      Cam->SetFocalPoint(cam->GetFocalPoint());
      Cam->SetViewUp(cam->GetViewUp());
      Cam->Azimuth(3.5+rxf);
      Cam->Elevation(ryf);
      Cam->OrthogonalizeViewUp();

      //Ren->ResetCamera();
      //Ren->SetActiveCamera(cam);
      //RenLeft->ResetCamera();
      //RenLeft->SetActiveCamera(cam);
    }



  this->ResetCameraClippingRange();

  if (rwi->GetLightFollowCamera())
    {
    this->CurrentLight->SetPosition(this->CurrentCamera->GetPosition());
    this->CurrentLight->SetFocalPoint(this->CurrentCamera->GetFocalPoint());
    }

  
  rwi1->Render();
  rwi2->Render(); 
}

//----------------------------------------------------------------------------
void eosInteractorMyStyle::SetRenderWindows(vtkRenderer* ren,vtkRenderer* renLeft,vtkRenderer* renRight,vtkRenderWindowInteractor* iren1,vtkRenderWindowInteractor* iren2)
{
  this->Cam=vtkCamera::New();
  this->CamLeft=vtkCamera::New();
  this->CamRight=vtkCamera::New();
  this->Cam = ren->GetActiveCamera();
  this->CamLeft = renLeft->GetActiveCamera();
  this->CamRight = renRight->GetActiveCamera();

  this->Ren=vtkRenderer::New();
  this->RenLeft=vtkRenderer::New();
  this->RenRight=vtkRenderer::New();
  this->Ren=ren;
  this->RenLeft=renLeft;
  this->RenRight=renRight;

  this->rwi1=vtkRenderWindowInteractor::New();
  this->rwi2=vtkRenderWindowInteractor::New();
  this->rwi1=iren1;
  this->rwi2=iren2;
}
