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

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

#include "vtkActor.h"
#include "vtkCallbackCommand.h"
#include "vtkCamera.h"
#include "vtkCubeSource.h"
#include "vtkImageData.h"
#include "vtkKWIcon.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkOpenGLSmoothPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkTexture.h"
#include "vtkTransform.h"
#include "Resources/vtkKWLettersTexture.h"

vtkCxxRevisionMacro(vtkKWOrientationWidget, "$Revision: 1.40 $");
vtkStandardNewMacro(vtkKWOrientationWidget);

vtkSetObjectImplementationMacro(vtkKWOrientationWidget,Cube, vtkPolyData);
vtkSetObjectImplementationMacro(vtkKWOrientationWidget,
                                ParentRenderer, vtkRenderer);

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLKWOrientationWidgetReader.h"
#include "XML/vtkXMLKWOrientationWidgetWriter.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtkKWOrientationWidget, vtkXMLKWOrientationWidgetReader, vtkXMLKWOrientationWidgetWriter);

namespace
{
  const float textureCoords[13][2] 
    = {{0, 1}, {0.25, 1}, {0.5, 1}, {0.75, 1}, {1, 1},
       {0, 0.5}, {0.25, 0.5}, {0.5, 0.5}, {0.75, 0.5},
       {1, 0.5}, {0, 0}, {0.25, 0}, {0.5, 0}};
}

//----------------------------------------------------------------------------
class vtkKWOrientationWidgetCameraCallback : public vtkCommand
{
public:
  static vtkKWOrientationWidgetCameraCallback *New() {
    return new vtkKWOrientationWidgetCameraCallback; }
  
  void Execute(vtkObject *vtkNotUsed(caller), unsigned long vtkNotUsed(event),
               void *vtkNotUsed(callData))
    {
      this->Self->UpdateCamera();
    }
  vtkKWOrientationWidget *Self;
};

//----------------------------------------------------------------------------
vtkKWOrientationWidget::vtkKWOrientationWidget()
{
  this->EventCallbackCommand->SetCallback(
    vtkKWOrientationWidget::ProcessEvents);
  
  this->Cube = NULL;
  this->ParentRenderer = NULL;

  this->LettersTextureGeneral = 0;
  this->LettersTextureMedical = 0;
  
  vtkCubeSource *cube = vtkCubeSource::New();
  cube->Update();
  this->SetCube(cube->GetOutput());  
  cube->Delete();

  this->Moving = 0;
  this->MouseCursorState = vtkKWOrientationWidget::Outside;
  
  vtkTexture *texture = vtkTexture::New();
  texture->InterpolateOn();
  
  this->CubeMapper = vtkOpenGLSmoothPolyDataMapper::New();
  this->CubeMapper->SetInput(this->Cube);

  this->CubeActor = vtkActor::New();
  this->CubeActor->SetMapper(this->CubeMapper);
  this->CubeActor->SetTexture(texture);
  this->CubeActor->GetProperty()->BackfaceCullingOn();
  
  this->CubeOutlineMapper = vtkOpenGLSmoothPolyDataMapper::New();
  this->CubeOutlineMapper->SetInput(this->Cube);

  this->CubeOutlineActor = vtkActor::New();
  this->CubeOutlineActor->SetMapper(this->CubeOutlineMapper);
  this->CubeOutlineActor->GetProperty()->SetRepresentationToWireframe();
  this->CubeOutlineActor->GetProperty()->SetLineWidth(2);
  this->CubeOutlineActor->GetProperty()->SetColor( .2, .4, .8 );

  texture->Delete();

  this->SetAnnotationTypeToGeneral();
  
  this->Renderer = vtkRenderer::New();
  this->Renderer->SetLayer(1);
  this->SetViewport(0.83, 0.05, 0.98, 0.20);
  
  this->CameraCallbackTag = 0;
  
  this->Initialized = 0;
  this->SynchronizeRenderers = 1;
  this->AsynchronousTransform = vtkTransform::New();
  this->Repositionable = 1;
  this->Resizeable     = 1;
}

//----------------------------------------------------------------------------
vtkKWOrientationWidget::~vtkKWOrientationWidget()
{
  if (this->LettersTextureGeneral)
    {
    this->LettersTextureGeneral->Delete();
    }

  if (this->LettersTextureMedical)
    {
    this->LettersTextureMedical->Delete();
    }

  this->CubeMapper->Delete();
  this->CubeActor->Delete();
  this->CubeOutlineMapper->Delete();
  this->CubeOutlineActor->Delete();
  
  this->Renderer->Delete();
  
  this->SetCube(NULL);
  this->SetParentRenderer(NULL);
  this->AsynchronousTransform->Delete();
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::SetEnabled(int enabling)
{
  if ( ! this->Interactor )
    {
    vtkErrorMacro(
      <<"The interactor must be set prior to enabling/disabling widget");
    return;
    }
  
  if ( enabling ) 
    {
    vtkDebugMacro(<<"Enabling orientation 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;
    
    // listen for the following events
    vtkRenderWindowInteractor *i = this->Interactor;
    i->AddObserver(vtkCommand::MouseMoveEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::LeftButtonPressEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::LeftButtonReleaseEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::MiddleButtonPressEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::MiddleButtonReleaseEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::RightButtonPressEvent, 
                   this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::RightButtonReleaseEvent, 
                   this->EventCallbackCommand, this->Priority);

    this->CurrentRenderer->AddObserver(vtkCommand::StartEvent,
                                       this->EventCallbackCommand,
                                       this->Priority);
    
    // Add the orientation marker
    this->ParentRenderer->GetRenderWindow()->SetNumberOfLayers(2);
    this->ParentRenderer->SetLayer(0);
    this->Renderer->GetActiveCamera()->ParallelProjectionOn();
    this->Renderer->SetBackground(this->ParentRenderer->GetBackground());
    this->ParentRenderer->GetRenderWindow()->AddRenderer(this->Renderer);
    vtkKWOrientationWidgetCameraCallback *cbk =
      vtkKWOrientationWidgetCameraCallback::New();
    cbk->Self = this;
    this->CameraCallbackTag =
      this->ParentRenderer->AddObserver(vtkCommand::StartEvent, cbk);
    cbk->Delete();
    this->Renderer->AddActor(this->CubeActor);
    this->Renderer->AddActor(this->CubeOutlineActor);
    
    if ( ! this->Initialized)
      {
      this->Initialized = 1;
      }
    
    this->InvokeEvent(vtkCommand::EnableEvent,NULL);
    }
  else //disabling------------------------------------------
    {
    vtkDebugMacro(<<"Disabling orientation widget");
    if ( ! this->Enabled ) //already disabled, just return
      {
      return;
      }
    this->Enabled = 0;

    // don't listen for events any more
    this->Interactor->RemoveObserver(this->EventCallbackCommand);
    if (this->CurrentRenderer)
      {
      this->CurrentRenderer->RemoveObserver(this->EventCallbackCommand);
      }

    // turn off the orientation marker
    this->Renderer->RemoveActor(this->CubeActor);
    this->Renderer->RemoveActor(this->CubeOutlineActor);
    this->ParentRenderer->GetRenderWindow()->RemoveRenderer(this->Renderer);
    this->ParentRenderer->RemoveObserver(this->CameraCallbackTag);
    
    this->InvokeEvent(vtkCommand::DisableEvent,NULL);
    }
//  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::ProcessEvents(vtkObject* vtkNotUsed(object),
                                           unsigned long event,
                                           void* clientdata,
                                           void* vtkNotUsed(calldata))
{
  vtkKWOrientationWidget* self = 
    reinterpret_cast<vtkKWOrientationWidget *>(clientdata);
  
  switch (event)
    {
    case vtkCommand::LeftButtonPressEvent:
      self->OnButtonPress();
      break;
    case vtkCommand::MouseMoveEvent:
      self->OnMouseMove();
      break;
    case vtkCommand::LeftButtonReleaseEvent:
      self->OnButtonRelease();
      break;
    case vtkCommand::StartEvent:
      self->UpdateCamera();
      break;
    }  
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::OnButtonPress()
{
  if (this->MouseCursorState == vtkKWOrientationWidget::Outside)
    {
    return;
    }
  
  this->SetMouseCursor(this->MouseCursorState);

  this->StartPosition[0] = this->Interactor->GetEventPosition()[0];
  this->StartPosition[1] = this->Interactor->GetEventPosition()[1];
  
  this->Moving = 1;
  this->EventCallbackCommand->SetAbortFlag(1);
  this->StartInteraction();
  this->InvokeEvent(vtkCommand::StartInteractionEvent, NULL);
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::OnMouseMove()
{
  if (this->Moving)
    {
    bool hasMoved = false;
    switch (this->MouseCursorState)
      {
      case vtkKWOrientationWidget::Inside:
        if ( this->Repositionable )
          {
          this->MoveWidget();
          hasMoved = true;
          }
        break;
      case vtkKWOrientationWidget::TopLeft:
        if (this->Resizeable)
          {
          this->ResizeTopLeft();
          hasMoved = true;
          }
        break;
      case vtkKWOrientationWidget::TopRight:
        if (this->Resizeable)
          {
          this->ResizeTopRight();
          hasMoved = true;
          }
        break;
      case vtkKWOrientationWidget::BottomLeft:
        if (this->Resizeable)
          {
          this->ResizeBottomLeft();
          hasMoved = true;
          }
        break;
      case vtkKWOrientationWidget::BottomRight:
        if (this->Resizeable)
          {
          this->ResizeBottomRight();
          hasMoved = true;
          }
        break;
      }
    
    if (hasMoved)
      {
      this->UpdateCursorIcon();
      this->EventCallbackCommand->SetAbortFlag(1);
      this->InvokeEvent(vtkCommand::InteractionEvent, NULL);
      }
    
    }
  else
    {
    this->UpdateCursorIcon();
    }
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::OnButtonRelease()
{
  if (this->MouseCursorState == vtkKWOrientationWidget::Outside)
    {
    return;
    }
  
  this->Moving = 0;
  this->EventCallbackCommand->SetAbortFlag(1);
  this->EndInteraction();
  this->InvokeEvent(vtkCommand::EndInteractionEvent, NULL);

  this->MouseCursorState = vtkKWOrientationWidget::Outside;
  this->SetMouseCursor(this->MouseCursorState);
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::UpdateCursorIcon()
{
  if ( ! this->Enabled)
    {
    this->SetMouseCursor(vtkKWOrientationWidget::Outside);
    return;
    }
  
  if (this->Moving)
    {
    return;
    }
  
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];

  int *parentSize = this->ParentRenderer->GetSize();
  double xNorm = x / (double)parentSize[0];
  double yNorm = y / (double)parentSize[1];

  double pos[4];
  this->Renderer->GetViewport(pos);

  int pState = this->MouseCursorState;
  
  if (xNorm > pos[0] && xNorm < pos[2] && yNorm > pos[1] && yNorm < pos[3])
    {
    this->MouseCursorState = vtkKWOrientationWidget::Inside;
    }
  else if (fabs(xNorm-pos[0]) < .02 && fabs(yNorm-pos[3]) < .02)
    {
    this->MouseCursorState = vtkKWOrientationWidget::TopLeft;
    }
  else if (fabs(xNorm-pos[2]) < .02 && fabs(yNorm-pos[3]) < .02)
    {
    this->MouseCursorState = vtkKWOrientationWidget::TopRight;
    }
  else if (fabs(xNorm-pos[0]) < .02 && fabs(yNorm-pos[1]) < .02)
    {
    this->MouseCursorState = vtkKWOrientationWidget::BottomLeft;
    }
  else if (fabs(xNorm-pos[2]) < .02 && fabs(yNorm-pos[1]) < .02)
    {
    this->MouseCursorState = vtkKWOrientationWidget::BottomRight;
    }
  else
    {
    this->MouseCursorState = vtkKWOrientationWidget::Outside;
    }

  if (pState == this->MouseCursorState)
    {
    return;
    }
  
  this->SetMouseCursor(this->MouseCursorState);
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::SetMouseCursor(int cursorState)
{
  switch (cursorState)
    {
    case vtkKWOrientationWidget::Outside:
      this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_DEFAULT);
      break;
    case vtkKWOrientationWidget::Inside:
      if (this->Repositionable)
        {
        this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZEALL);
        }
      break;
    case vtkKWOrientationWidget::TopLeft:
      if (this->Resizeable)
        {
        this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZENW);
        }
      break;
    case vtkKWOrientationWidget::TopRight:
      if (this->Resizeable)
        {
        this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZENE);
        }
      break;
    case vtkKWOrientationWidget::BottomLeft:
      if (this->Resizeable)
        {
        this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZESW);
        }
      break;
    case vtkKWOrientationWidget::BottomRight:
      if (this->Resizeable)
        {
        this->Interactor->GetRenderWindow()->SetCurrentCursor(VTK_CURSOR_SIZESE);
        }
      break;
    }
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::MoveWidget()
{
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];
  
  int dx = x - this->StartPosition[0];
  int dy = y - this->StartPosition[1];

  double *vp = this->Renderer->GetViewport();
  int *size = this->ParentRenderer->GetSize();

  if (vp[0]*size[0]+dx < 0)
    {
    dx = (int)(-vp[0] * size[0]);
    }
  else if (vp[2]*size[0]+dx > size[0])
    {
    dx = size[0] - (int)(vp[2] * size[0]);
    }
  if (vp[1]*size[1]+dy < 0)
    {
    dy = (int)(-vp[1] * size[1]);
    }
  else if (vp[3]*size[1]+dy > size[1])
    {
    dy = size[1] - (int)(vp[3] * size[1]);
    }

  this->StartPosition[0] += dx;
  this->StartPosition[1] += dy;

  double dxNorm = dx / (double)size[0];
  double dyNorm = dy / (double)size[1];
  
  double newPos[4];
  newPos[0] = vp[0] + dxNorm;
  newPos[1] = vp[1] + dyNorm;
  newPos[2] = vp[2] + dxNorm;
  newPos[3] = vp[3] + dyNorm;

  this->SetViewport(newPos);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::ResizeTopLeft()
{
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];
  
  int dx = x - this->StartPosition[0];
  int dy = y - this->StartPosition[1];
  
  int *size = this->ParentRenderer->GetSize();
  double dxNorm = dx / (double)size[0];
  double dyNorm = dy / (double)size[1];
  
  int useX;
  double change;
  double absDx = fabs(dxNorm);
  double absDy = fabs(dyNorm);
  
  if (absDx > absDy)
    {
    change = dxNorm;
    useX = 1;
    }
  else
    {
    change = dyNorm;
    useX = 0;
    }
  
  double *vp = this->Renderer->GetViewport();
  
  this->StartPosition[0] = x;
  this->StartPosition[1] = y;
  
  double newPos[4];
  newPos[0] = useX ? vp[0] + change : vp[0] - change;
  newPos[1] = vp[1];
  newPos[2] = vp[2];
  newPos[3] = useX ? vp[3] - change : vp[3] + change;
  
  if (newPos[0] < 0)
    {
    this->StartPosition[0] = 0;
    newPos[0] = 0;
    }
  if (newPos[0] >= newPos[2]-0.01)
    {
    newPos[0] = newPos[2] - 0.01;
    }
  if (newPos[3] > 1)
    {
    this->StartPosition[1] = size[1];
    newPos[3] = 1;
    }
  if (newPos[3] <= newPos[1]+0.01)
    {
    newPos[3] = newPos[1] + 0.01;
    }
  
  this->SetViewport(newPos);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::ResizeTopRight()
{
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];
  
  int dx = x - this->StartPosition[0];
  int dy = y - this->StartPosition[1];
  
  int *size = this->ParentRenderer->GetSize();
  double dxNorm = dx / (double)size[0];
  double dyNorm = dy / (double)size[1];

  double change;
  double absDx = fabs(dxNorm);
  double absDy = fabs(dyNorm);
  
  if (absDx > absDy)
    {
    change = dxNorm;
    }
  else
    {
    change = dyNorm;
    }
  
  double *vp = this->Renderer->GetViewport();
  
  this->StartPosition[0] = x;
  this->StartPosition[1] = y;
  
  double newPos[4];
  newPos[0] = vp[0];
  newPos[1] = vp[1];
  newPos[2] = vp[2] + change;
  newPos[3] = vp[3] + change;
  
  if (newPos[2] > 1)
    {
    this->StartPosition[0] = size[0];
    newPos[2] = 1;
    }
  if (newPos[2] <= newPos[0]+0.01)
    {
    newPos[2] = newPos[0] + 0.01;
    }
  if (newPos[3] > 1)
    {
    this->StartPosition[1] = size[1];
    newPos[3] = 1;
    }
  if (newPos[3] <= newPos[1]+0.01)
    {
    newPos[3] = newPos[1] + 0.01;
    }
  
  this->SetViewport(newPos);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::ResizeBottomLeft()
{
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];
  
  int dx = x - this->StartPosition[0];
  int dy = y - this->StartPosition[1];
  
  int *size = this->ParentRenderer->GetSize();
  double dxNorm = dx / (double)size[0];
  double dyNorm = dy / (double)size[1];
  double *vp = this->Renderer->GetViewport();
  
  double change;
  double absDx = fabs(dxNorm);
  double absDy = fabs(dyNorm);
  
  if (absDx > absDy)
    {
    change = dxNorm;
    }
  else
    {
    change = dyNorm;
    }
  
  this->StartPosition[0] = x;
  this->StartPosition[1] = y;
  
  double newPos[4];
  newPos[0] = vp[0] + change;
  newPos[1] = vp[1] + change;
  newPos[2] = vp[2];
  newPos[3] = vp[3];
  
  if (newPos[0] < 0)
    {
    this->StartPosition[0] = 0;
    newPos[0] = 0;
    }
  if (newPos[0] >= newPos[2]-0.01)
    {
    newPos[0] = newPos[2] - 0.01;
    }
  if (newPos[1] < 0)
    {
    this->StartPosition[1] = 0;
    newPos[1] = 0;
    }
  if (newPos[1] >= newPos[3]-0.01)
    {
    newPos[1] = newPos[3] - 0.01;
    }
  
  this->SetViewport(newPos);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::ResizeBottomRight()
{
  int x = this->Interactor->GetEventPosition()[0];
  int y = this->Interactor->GetEventPosition()[1];
  
  int dx = x - this->StartPosition[0];
  int dy = y - this->StartPosition[1];
  
  int *size = this->ParentRenderer->GetSize();
  double dxNorm = dx / (double)size[0];
  double dyNorm = dy / (double)size[1];
  
  double *vp = this->Renderer->GetViewport();
  
  int useX;
  double change;
  double absDx = fabs(dxNorm);
  double absDy = fabs(dyNorm);
  
  if (absDx > absDy)
    {
    change = dxNorm;
    useX = 1;
    }
  else
    {
    change = dyNorm;
    useX = 0;
    }
  
  this->StartPosition[0] = x;
  this->StartPosition[1] = y;
  
  double newPos[4];
  newPos[0] = vp[0];
  newPos[1] = useX ? vp[1] - change : vp[1] + change;
  newPos[2] = useX ? vp[2] + change : vp[2] - change;
  newPos[3] = vp[3];
  
  if (newPos[2] > 1)
    {
    this->StartPosition[0] = size[0];
    newPos[2] = 1;
    }
  if (newPos[2] <= newPos[0]+0.01)
    {
    newPos[2] = newPos[0] + 0.01;
    }
  if (newPos[1] < 0)
    {
    this->StartPosition[1] = 0;
    newPos[1] = 0;
    }
  if (newPos[1] >= newPos[3]-0.01)
    {
    newPos[1] = newPos[3]-0.01;
    }
  
  this->SetViewport(newPos);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::SetColor(double r, double g, double b)
{
  this->CubeActor->GetProperty()->SetColor(r, g, b);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::SetOutlineColor(double r, double g, double b)
{
  this->CubeOutlineActor->GetProperty()->SetColor(r, g, b);
  this->Interactor->Render();
}

//----------------------------------------------------------------------------
double* vtkKWOrientationWidget::GetColor()
{
  return this->CubeActor->GetProperty()->GetColor();
}

//----------------------------------------------------------------------------
double* vtkKWOrientationWidget::GetOutlineColor()
{
  return this->CubeOutlineActor->GetProperty()->GetColor();
}

//----------------------------------------------------------------------------
int vtkKWOrientationWidget::GetAnnotationType()
{
  vtkImageData *tex = this->CubeActor->GetTexture()->GetInput();
  if (!tex)
    {
    return vtkKWOrientationWidget::ANNOTATION_TYPE_UNKNOWN;
    }

  if (this->LettersTextureGeneral && tex == this->LettersTextureGeneral)
    {
    return vtkKWOrientationWidget::ANNOTATION_TYPE_GENERAL;
    }

  if (this->LettersTextureMedical && tex == this->LettersTextureMedical)
    {
    return vtkKWOrientationWidget::ANNOTATION_TYPE_MEDICAL;
    }

  return vtkKWOrientationWidget::ANNOTATION_TYPE_UNKNOWN;
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::SetAnnotationType(int type)
{
  if (this->GetAnnotationType() == type)
    {
    return;
    }

  switch (type)
    {
    case vtkKWOrientationWidget::ANNOTATION_TYPE_GENERAL:
      if (!this->LettersTextureGeneral)
        {
        // Use the vtkKWIcon facility to decompress/decode the data
        vtkKWIcon *icon = vtkKWIcon::New();
        icon->SetImage(image_KWLettersTextureGeneral,
                       image_KWLettersTextureGeneral_width,
                       image_KWLettersTextureGeneral_height,
                       image_KWLettersTextureGeneral_pixel_size,
                       image_KWLettersTextureGeneral_length,
                       vtkKWIcon::ImageOptionFlipVertical);

        this->LettersTextureGeneral = vtkImageData::New();
        this->LettersTextureGeneral->SetDimensions(
          icon->GetWidth(), icon->GetHeight(), 1);
        this->LettersTextureGeneral->SetScalarType(VTK_UNSIGNED_CHAR);
        this->LettersTextureGeneral->SetNumberOfScalarComponents(
          icon->GetPixelSize());
        this->LettersTextureGeneral->AllocateScalars();

        memcpy(this->LettersTextureGeneral->GetPointData()->GetScalars()
               ->GetVoidPointer(0),
               icon->GetData(), 
               icon->GetWidth() * icon->GetHeight() * icon->GetPixelSize());

        icon->Delete();
        }

      this->CubeActor->GetTexture()->SetInput(this->LettersTextureGeneral);
      
      // use the general coordinate system.
      this->CoordinateSystem 
        = vtkKWOrientationWidget::COORDINATE_SYSTEM_GENERAL;
      this->SetTextureCoordsToGeneral();
      break;

    case vtkKWOrientationWidget::ANNOTATION_TYPE_MEDICAL:
      if (!this->LettersTextureMedical)
        {
        // Use the vtkKWIcon facility to decompress/decode the data
        vtkKWIcon *icon = vtkKWIcon::New();
        icon->SetImage(image_KWLettersTextureMedical,
                       image_KWLettersTextureMedical_width,
                       image_KWLettersTextureMedical_height,
                       image_KWLettersTextureMedical_pixel_size,
                       image_KWLettersTextureMedical_length,
                       vtkKWIcon::ImageOptionFlipVertical);

        this->LettersTextureMedical = vtkImageData::New();
        this->LettersTextureMedical->SetDimensions(
          icon->GetWidth(), icon->GetHeight(), 1);
        this->LettersTextureMedical->SetScalarType(VTK_UNSIGNED_CHAR);
        this->LettersTextureMedical->SetNumberOfScalarComponents(
          icon->GetPixelSize());
        this->LettersTextureMedical->AllocateScalars();

        memcpy(this->LettersTextureMedical->GetPointData()->GetScalars()
               ->GetVoidPointer(0),
               icon->GetData(), 
               icon->GetWidth() * icon->GetHeight() * icon->GetPixelSize());
      
        icon->Delete();
        }

      this->CubeActor->GetTexture()->SetInput(this->LettersTextureMedical);
      
      // if a medical coordinate system hasn't been set, default to LPS
      if ( this->CoordinateSystem 
          == vtkKWOrientationWidget::COORDINATE_SYSTEM_GENERAL )
        {
        this->SetCoordinateSystem( 
            vtkKWOrientationWidget::COORDINATE_SYSTEM_MEDICAL_LPS );
        }
      break;
    }
  
  this->CubeActor->GetTexture()->Modified();
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::SetCoordinateSystem(int type)
{
  if (this->GetCoordinateSystem() == type)
    {
    return;
    }
  
  if (this->GetAnnotationType() == 
      vtkKWOrientationWidget::ANNOTATION_TYPE_MEDICAL) 
    {
    switch (type)
      {
      case vtkKWOrientationWidget::COORDINATE_SYSTEM_MEDICAL_RAS:
        {
        this->SetTextureCoordsToRAS();
        break;
        }
      case vtkKWOrientationWidget::COORDINATE_SYSTEM_MEDICAL_LPS:
        {
        this->SetTextureCoordsToLPS();
        break;
        }
      }
    }
  
  if (this->GetAnnotationType() == 
      vtkKWOrientationWidget::ANNOTATION_TYPE_GENERAL) 
    {
    switch (type)
      {
      case vtkKWOrientationWidget::COORDINATE_SYSTEM_GENERAL:
        this->SetTextureCoordsToGeneral();
      }
    }

  this->CubeActor->GetTexture()->Modified();
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::UpdateCamera()
{
  if ( this->SynchronizeRenderers )
    {
    double pos[3], focalPt[3], viewUp[3];
    vtkCamera *camera =
      this->ParentRenderer->GetActiveCamera();
    camera->GetPosition(pos);
    camera->GetFocalPoint(focalPt);
    camera->GetViewUp(viewUp);

    vtkCamera *myCamera = this->Renderer->GetActiveCamera();
    myCamera->SetPosition(pos);
    myCamera->SetFocalPoint(focalPt);
    myCamera->SetViewUp(viewUp);
    this->Renderer->ResetCamera();

    int *size = this->Renderer->GetSize();
    if (size[0] < size[1])
      {
      myCamera->Zoom(size[0] / (double)size[1]);
      }
    // otherwise do nothing since the camera logic in vtkCamera already
    // constrains the aspect ratio based on y
    }
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::UpdateCamera(double theta, const double axis[3])
{
  if ( !this->SynchronizeRenderers )
    {
    vtkTransform *t = this->AsynchronousTransform;
    vtkCamera *myCamera = this->Renderer->GetActiveCamera();
    double viewup[3];
    myCamera->GetViewUp(viewup);
    double position[3];
    myCamera->GetPosition(position);
    double focalPt[3];
    myCamera->GetFocalPoint(focalPt);

    double direction[3];
    direction[0] = focalPt[0] - position[0];
    direction[1] = focalPt[1] - position[1];
    direction[2] = focalPt[2] - position[2];
    t->Identity();
    t->RotateWXYZ(theta,axis);

    double tdirection[3];
    t->TransformVector(direction, tdirection);
    double tviewup[3];
    t->TransformNormal(viewup, tviewup);
    double newPos[3];
    newPos[0] = focalPt[0] - tdirection[0];
    newPos[1] = focalPt[1] - tdirection[1];
    newPos[2] = focalPt[2] - tdirection[2];
    myCamera->SetPosition(newPos);
    myCamera->SetViewUp(tviewup);
    }
}

//----------------------------------------------------------------------------
double* vtkKWOrientationWidget::GetViewport()
{
  if (this->Renderer)
    {
    return this->Renderer->GetViewport();
    }
  return NULL;
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::SetViewport(double a, double b, double c, double d)
{
  if (this->Renderer)
    {
    this->Renderer->SetViewport(a, b, c, d);
    }
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::SetTextureCoordsToRAS()
{
  vtkDataArray *tcoords = this->Cube->GetPointData()->GetTCoords();
  tcoords->SetTuple(0, textureCoords[8]); // L   -X
  tcoords->SetTuple(1, textureCoords[3]); // L   -X
  tcoords->SetTuple(2, textureCoords[7]); // L   -X 
  tcoords->SetTuple(3, textureCoords[2]); // L   -X
  tcoords->SetTuple(4, textureCoords[5]); // R   +x
  tcoords->SetTuple(5, textureCoords[0]); // R   +x
  tcoords->SetTuple(6, textureCoords[6]); // R   +x
  tcoords->SetTuple(7, textureCoords[1]); // R   +x
  tcoords->SetTuple(8, textureCoords[8]); // P   -y
  tcoords->SetTuple(9, textureCoords[3]); // P   -y
  tcoords->SetTuple(10, textureCoords[9]); // P  -y
  tcoords->SetTuple(11, textureCoords[4]); // P  -y
  tcoords->SetTuple(12, textureCoords[7]); // A  +y
  tcoords->SetTuple(13, textureCoords[2]); // A  +y
  tcoords->SetTuple(14, textureCoords[6]); // A  +y
  tcoords->SetTuple(15, textureCoords[1]); // A  +y
  tcoords->SetTuple(16, textureCoords[12]); // I -z
  tcoords->SetTuple(17, textureCoords[11]); // I -z
  tcoords->SetTuple(18, textureCoords[7]);  // I -z
  tcoords->SetTuple(19, textureCoords[6]);  // I -z
  tcoords->SetTuple(20, textureCoords[10]); // S +z
  tcoords->SetTuple(21, textureCoords[11]); // S +z
  tcoords->SetTuple(22, textureCoords[5]);  // S +z
  tcoords->SetTuple(23, textureCoords[6]);  // S +z
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::SetTextureCoordsToLPS()
{
  vtkDataArray *tcoords = this->Cube->GetPointData()->GetTCoords();
  tcoords->SetTuple(0, textureCoords[6]); // R   -X
  tcoords->SetTuple(1, textureCoords[1]); // R   -X
  tcoords->SetTuple(2, textureCoords[5]); // R   -X 
  tcoords->SetTuple(3, textureCoords[0]); // R   -X
  tcoords->SetTuple(4, textureCoords[7]); // L   +x
  tcoords->SetTuple(5, textureCoords[2]); // L   +x
  tcoords->SetTuple(6, textureCoords[8]); // L   +x
  tcoords->SetTuple(7, textureCoords[3]); // L   +x
  tcoords->SetTuple(8, textureCoords[7]); // A   -y
  tcoords->SetTuple(9, textureCoords[2]); // A   -y
  tcoords->SetTuple(10, textureCoords[6]); // A  -y
  tcoords->SetTuple(11, textureCoords[1]); // A  -y
  tcoords->SetTuple(12, textureCoords[9]); // P  +y
  tcoords->SetTuple(13, textureCoords[4]); // P  +y
  tcoords->SetTuple(14, textureCoords[8]); // P  +y
  tcoords->SetTuple(15, textureCoords[3]); // P  +y
  tcoords->SetTuple(16, textureCoords[12]); // I -z
  tcoords->SetTuple(17, textureCoords[11]); // I -z
  tcoords->SetTuple(18, textureCoords[7]);  // I -z
  tcoords->SetTuple(19, textureCoords[6]);  // I -z
  tcoords->SetTuple(20, textureCoords[6]); // S +z
  tcoords->SetTuple(21, textureCoords[5]); // S +z
  tcoords->SetTuple(22, textureCoords[11]);  // S +z
  tcoords->SetTuple(23, textureCoords[10]);  // S +z
}

//----------------------------------------------------------------------------
void vtkKWOrientationWidget::SetTextureCoordsToGeneral()
{
  this->SetTextureCoordsToRAS();
}

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

  os << indent << "Renderer: " << this->Renderer << endl;;
  os << indent << "SynchronizeRenderers: " << (this->SynchronizeRenderers ? "On" : "Off" ) << endl;
  os << indent << "Repositionable: " << this->Repositionable << endl;
  os << indent << "Resizeable    : " << this->Resizeable     << endl;
  os << indent << "CoordinateSystem: " << this->CoordinateSystem << endl;
}
