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

 medInria

 Copyright (c) INRIA 2013 - 2020. All rights reserved.
 See LICENSE.txt for details.
 
  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.

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

#include "vtkInteractorStyleTrackballCamera2.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkTransform.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkCamera.h"
#include "vtkCallbackCommand.h"

vtkStandardNewMacro(vtkInteractorStyleTrackballCamera2)

vtkInteractorStyleTrackballCamera2::vtkInteractorStyleTrackballCamera2()
{  
  this->Transform = vtkTransform::New();

  (this->UpAxis)[0] = 0;
  (this->UpAxis)[1] = 0;
  (this->UpAxis)[2] = 1;
}

vtkInteractorStyleTrackballCamera2::~vtkInteractorStyleTrackballCamera2()
{
  this->Transform->Delete();
}

void vtkInteractorStyleTrackballCamera2::OnLeftButtonDown() 
{
  vtkInteractorStyleTrackballCamera::OnLeftButtonDown();

  if (!this->Interactor->GetShiftKey() && !this->Interactor->GetControlKey()) {

    vtkCamera *camera = this->CurrentRenderer->GetActiveCamera();
  
    double viewup[3]; camera->GetViewUp(viewup);
    double upaxis[3];
    upaxis[0] = 0;
    upaxis[1] = 0;
    upaxis[2] = 1;
      
    if(vtkMath::Dot(viewup, upaxis) < 0)
      (this->UpAxis)[2] = -1;
    else
      (this->UpAxis)[2] = 1;
  }
}

void vtkInteractorStyleTrackballCamera2::OnMouseWheelForward()
{
    vtkInteractorStyleTrackballCamera::OnMouseWheelForward();
    this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
}

void vtkInteractorStyleTrackballCamera2::OnMouseWheelBackward()
{
    vtkInteractorStyleTrackballCamera::OnMouseWheelBackward();
    this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
}

void vtkInteractorStyleTrackballCamera2::Rotate()
{
  if (this->CurrentRenderer == nullptr)
    return;
    
  vtkRenderWindowInteractor *rwi = this->Interactor;

  int dx = rwi->GetEventPosition()[0] - rwi->GetLastEventPosition()[0];
  int dy = rwi->GetEventPosition()[1] - rwi->GetLastEventPosition()[1];
  
  int *size = this->CurrentRenderer->GetRenderWindow()->GetSize();

  double delta_elevation = -20.0 / size[1];
  double delta_azimuth = -20.0 / size[0];
  
  double rxf = dx * delta_azimuth * this->MotionFactor;
  double ryf = dy * delta_elevation * this->MotionFactor;
  
  vtkCamera *camera = this->CurrentRenderer->GetActiveCamera();
  this->Azimuth(rxf);
  this->Elevation(ryf);
  camera->OrthogonalizeViewUp();

  if(this->AutoAdjustCameraClippingRange)
     this->CurrentRenderer->ResetCameraClippingRange();
  
  if(rwi->GetLightFollowCamera()) 
     this->CurrentRenderer->UpdateLightsGeometryToFollowCamera();

  rwi->Render();
}

void vtkInteractorStyleTrackballCamera2::Azimuth(double angle)
{
  vtkCamera *camera = this->CurrentRenderer->GetActiveCamera();

  double axis[3];
  double newPosition[3];
  double fp[3]; camera->GetFocalPoint(fp);
  double position[3]; camera->GetPosition(position);

  camera->GetViewUp(axis);

  this->Transform->Identity();
  this->Transform->Translate(+fp[0],+fp[1],+fp[2]);
  this->Transform->RotateWXYZ(angle, axis);
  this->Transform->Translate(-fp[0],-fp[1],-fp[2]);
  this->Transform->TransformPoint(position, newPosition);

  camera->SetPosition(newPosition);
  camera->SetViewUp(this->Transform->TransformVector(camera->GetViewUp()));
}

void vtkInteractorStyleTrackballCamera2::Elevation(double angle)
{
  vtkCamera *camera = this->CurrentRenderer->GetActiveCamera();

  double axis[3], newPosition[3];
  double fp[3]; camera->GetFocalPoint(fp);
  double position[3]; camera->GetPosition(position);

  this->Transform->Identity();

  axis[0] = -camera->GetViewTransformMatrix()->GetElement(0,0);
  axis[1] = -camera->GetViewTransformMatrix()->GetElement(0,1);
  axis[2] = -camera->GetViewTransformMatrix()->GetElement(0,2);

  this->Transform->Translate(+fp[0],+fp[1],+fp[2]);
  this->Transform->RotateWXYZ(angle, axis);
  this->Transform->Translate(-fp[0],-fp[1],-fp[2]);
  this->Transform->TransformPoint(position, newPosition);

  camera->SetPosition(newPosition);
  camera->SetViewUp(this->Transform->TransformVector(camera->GetViewUp()));
}
