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

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

#include "vtkImageReslice.h"
#include "vtkKWEvent.h"
#include "vtkImageData.h"
#include "vtkKW3DMarkersWidget.h"
#include "vtkKWImageMapToWindowLevelColors.h"
#include "vtkKWImageWidget.h"
#include "vtkKWRenderWidget.h"
#include "vtkObjectFactory.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkCamera.h"
#include "vtkKWProbeImageWidget.h"
#include "vtkMath.h"

vtkCxxRevisionMacro(vtkKWInteractorStyleImageView, "$Revision: 1.46 $");
vtkStandardNewMacro(vtkKWInteractorStyleImageView);

//----------------------------------------------------------------------------
vtkKWInteractorStyleImageView::vtkKWInteractorStyleImageView()
{
}

//----------------------------------------------------------------------------
vtkKWInteractorStyleImageView::~vtkKWInteractorStyleImageView()
{
}

//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::Roll()
{
  vtkKWProbeImageWidget *widget = 
    vtkKWProbeImageWidget::SafeDownCast(this->Get2DRenderWidget());
  if (!widget)
    {
    return;
    }
  vtkRenderWindowInteractor *rwi = this->Interactor;

  double *center = this->CurrentRenderer->GetCenter();

  double newAngle = 
    atan2((double)rwi->GetEventPosition()[1] - (double)center[1],
          (double)rwi->GetEventPosition()[0] - (double)center[0]);

  double oldAngle = 
    atan2((double)rwi->GetLastEventPosition()[1] - (double)center[1],
          (double)rwi->GetLastEventPosition()[0] - (double)center[0]);
  
  newAngle *= vtkMath::RadiansToDegrees();
  oldAngle *= vtkMath::RadiansToDegrees();

  widget->RollPlane(newAngle - oldAngle);
}

//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::StopRoll()
{
}

//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::Reslice()
{
  // Copy from vtkInteractorStyleTrackballCamera::Rotate()
  vtkKWProbeImageWidget *widget = 
    vtkKWProbeImageWidget::SafeDownCast(this->Get2DRenderWidget());
  if (!widget)
    {
    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->GetSize();

  double delta_elevation = 6.0 / size[1];
  double delta_azimuth = -6.0 / size[0];
  
  double motionFactor = 0.20;
  double rxf = (double)dx * delta_azimuth * motionFactor;
  double ryf = (double)dy * delta_elevation * motionFactor;

  rxf *= vtkMath::RadiansToDegrees();
  ryf *= vtkMath::RadiansToDegrees();
  
  widget->TiltPlane(rxf, ryf);
  this->InvokeEvent(vtkKWEvent::ResliceChangingEvent, NULL);
}

//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::StopReslice()
{
  this->InvokeEvent(vtkKWEvent::ResliceChangedEvent, NULL);
}

//----------------------------------------------------------------------------
// This method is called when a Translate action happens. It moves the
// reslice plane in and out (along the plane normal) based on y motion
//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::InAndOut()
{
  // Make sure we have the right widget type - otherwise this
  // operation does not make sense
  vtkKWProbeImageWidget *widget = 
    vtkKWProbeImageWidget::SafeDownCast(this->Get2DRenderWidget());
  if (!widget)
    {
    return;
    }
  
  // Get the y motion
  vtkRenderWindowInteractor *rwi = this->Interactor;
  int dy = rwi->GetEventPosition()[1] - rwi->GetLastEventPosition()[1];
  
  // Get the window size
  int *size = this->CurrentRenderer->GetSize();

  // Convert motion into a fraction of the window size
  double motionFactor = 0.2;
  double factor = (double)dy * motionFactor / size[1];

  // Move the reslice plane
  widget->TranslatePlane(factor);
  
  // Cause other areas of the app to update themselves
  // due to this change
  this->InvokeEvent(vtkKWEvent::ResliceChangingEvent, NULL);
}

//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::StopInAndOut()
{
  this->InvokeEvent(vtkKWEvent::ResliceChangedEvent, NULL);
}

//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::StartWindowLevel()
{
  if (!this->Interactor || !this->ImageMapToRGBA)
    {
    return;
    }
  
  this->InitialWindow = this->ImageMapToRGBA->GetWindow();
  this->InitialLevel = this->ImageMapToRGBA->GetLevel();
  
  this->InitialX = this->Interactor->GetEventPosition()[0];
  this->InitialY = this->Interactor->GetEventPosition()[1];
}

//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::WindowLevel()
{
  if (!this->ImageMapToRGBA)
    {
    vtkErrorMacro(
      "Trying to change window/level before setting ImageMapToRGBA");
    return;
    }

  if (!this->ImageMapToRGBA->GetInput())
    {
    return;
    }
  
  vtkRenderWindowInteractor *rwi = this->Interactor;
  if (!rwi || !this->CurrentRenderer)
    {
    return;
    }

  int *size = this->CurrentRenderer->GetSize();
  
  int x = rwi->GetEventPosition()[0];
  int y = rwi->GetEventPosition()[1];

  // compute normalized delta

  double dx = 4.0 * (x - this->InitialX) / size[0];
  double dy = 4.0 * (y - this->InitialY) / size[1];

  // scale by current values

  double min_window = 0.000001;

  if (fabs(this->InitialWindow) > min_window)
    {
    dx *= this->InitialWindow;
    }
  else
    {
    dx = dx * (this->InitialWindow < 0 ? -min_window : min_window);
    }

  double min_level = 0.000001;

  if (fabs(this->InitialLevel) > min_level)
    {
    dy *= this->InitialLevel;
    }
  else
    {
    dy = dy * (this->InitialLevel < 0 ? -min_level : min_level);
    }
 
  // abs so that direction does not flip

  if (this->InitialWindow < 0.0) 
    {
    dx *= -1;
    }
  if (this->InitialLevel < 0.0) 
    {
    dy *= -1;
    }
  
  // compute new window level

  double newWindow = dx + this->InitialWindow;
  double newLevel = this->InitialLevel - dy;

  // stay away from zero and really

  double near_zero = 0.000001;

  if (fabs(newWindow) < near_zero)
    {
    newWindow = near_zero * (newWindow < 0 ? -1 : 1);
    }

  if (fabs(newLevel) < near_zero)
    {
    newLevel = near_zero * (newLevel < 0 ? -1 : 1);
    }

  vtkImageData *input = vtkImageData::SafeDownCast(
    this->ImageMapToRGBA->GetInput());
  if (input && 
      input->GetScalarType() != VTK_FLOAT &&
      input->GetScalarType() != VTK_DOUBLE)
    {
    newWindow = (long)newWindow;
    newLevel = (long)newLevel;
    }

  this->SetWindowLevel(newWindow, newLevel);

  double args[3];
  args[0] = newWindow;
  args[1] = newLevel;
  args[2] = this->EventIdentifier;
  this->InvokeEvent(vtkKWEvent::WindowLevelChangingEvent, args);
}

//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::StopWindowLevel()
{
  if (!this->ImageMapToRGBA)
    {
    return;
    }

  double args[3];
  args[0] = this->ImageMapToRGBA->GetWindow();
  args[1] = this->ImageMapToRGBA->GetLevel();
  args[2] = this->EventIdentifier;

  // Give two option to process this action
  // WindowLevelChangingEndEvent: since during interaction WindowLevel() was
  // invoking WindowLevelChangingEvent, let's invoke something telling that
  // the interaction is done.
  // WindowLevelChangedEvent: the usual one (for compatibility). 

  this->InvokeEvent(vtkKWEvent::WindowLevelChangingEndEvent, args);
  this->InvokeEvent(vtkKWEvent::WindowLevelChangedEvent, args);
}

//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::StopPan()
{
  vtkKWRenderWidget *widget = this->GetRenderWidget();
  if (!widget)
    {
    return;
    }

  vtkRenderer *ren = widget->GetRenderer();
  if (!ren)
    {
    return;
    }

  vtkCamera *cam = ren->GetActiveCamera();
  if (!cam)
    {
    return;
    }

  double FPoint[3];
  cam->GetFocalPoint(FPoint);

  double PPoint[3];
  cam->GetPosition(PPoint);

  double args[7];
  args[0] = FPoint[0];
  args[1] = FPoint[1];
  args[2] = FPoint[2];
  args[3] = PPoint[0];
  args[4] = PPoint[1];
  args[5] = PPoint[2];

  args[6] = this->EventIdentifier;

  this->InvokeEvent(vtkKWInteractorStyleImageView::
      ImageCameraFocalPointAndPositionChangedEndEvent, args);
}

//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::StopZoom()
{
  vtkKWRenderWidget *widget = this->GetRenderWidget();
  if (!widget)
    {
    return;
    }

  vtkRenderer *ren = widget->GetRenderer();
  if (!ren)
    {
    return;
    }
  vtkCamera *cam = ren->GetActiveCamera();
  if (!cam)
    {
    return;
    }

  double FPoint[3];
  cam->GetFocalPoint(FPoint);

  double PPoint[3];
  cam->GetPosition(PPoint);

  double args[8];
  args[0] = FPoint[0];
  args[1] = FPoint[1];
  args[2] = FPoint[2];
  args[3] = PPoint[0];
  args[4] = PPoint[1];
  args[5] = PPoint[2];

  if (cam->GetParallelProjection()) 
    {
    args[6] = cam->GetParallelScale();
    }
  else
  {
    args[6] = 0;
  }
    
  args[7] = this->EventIdentifier;

  this->InvokeEvent(vtkKWInteractorStyleImageView::
      ZoomFactorChangedEndEvent, args);
}

//----------------------------------------------------------------------------
void vtkKWInteractorStyleImageView::PlaceMarker3D()
{
  vtkKWImageWidget *widget = 
    vtkKWImageWidget::SafeDownCast(this->Get2DRenderWidget());
  if (!widget)
    {
    return;
    }

  int *event_pos = this->Interactor->GetEventPosition();
  double RPoint[3];
  
  if (widget->GetMarkers3D()->GetEnabled() && 
      widget->ComputeWorldCoordinate(event_pos[0], event_pos[1], RPoint))
    {
    double args[3];
    args[0] = RPoint[0];
    args[1] = RPoint[1];
    args[2] = RPoint[2];
    this->InvokeEvent(vtkKWEvent::Marker3DAddMarkerEvent, args);
    }
}

//----------------------------------------------------------------------------
int vtkKWInteractorStyleImageView::PerformAction(const char* action)
{
  if (!action)
    {
    return 0;
    }
 
  if (!strcmp(action, "PlaceMarker3D"))
    {
    this->PlaceMarker3D();
    return 1;
    }
  else if (!strcmp(action, "Roll"))
    {
    this->Roll();
    return 1;
    }
  else if (!strcmp(action, "Reslice"))
    {
    this->Reslice();
    return 1;
    }
  else if (!strcmp(action, "Translate"))
    {
    this->InAndOut();
    return 1;
    }
  return this->Superclass::PerformAction(action);
}

//----------------------------------------------------------------------------
int vtkKWInteractorStyleImageView::StopAction(const char* action)
{
  int res = 0;
  if (!action)
    {
    return res;
    }
 
  if (this->Superclass::StopAction(action))
    {
    res = 1;
    }
  else
    {
    if (!strcmp(action, "Roll"))
      {
      this->StopRoll();
      res = 1;
      }
    else if (!strcmp(action, "Reslice"))
      {
      this->StopReslice();
      res = 1;
      }
    else if (!strcmp(action, "Translate"))
      {
      this->StopInAndOut();
      res = 1;
      }
    }

  return res;
}

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