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

  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.

=========================================================================*/
// .NAME vtkKWOrientationWidget - widget for manipulating an orientation cube
// .SECTION Description
// Widget to represent a cube with Orientation. Multiple types are available, 
// for either a more physic oriented, or a more medical oriented.
// By design this is supposed to be working a in a 3D, scene therewore the
// two camera one from the parent scene and one associated with the object are 
// synchronized.
// A versy special case has to be done when using an oblique plane (which has a 
// 3D orientation), but since renderer in 2D does not have a camera, you can break
// the synchronization in between the two camera. You will need instead to use
// UpdateCamera and pass the user specified orientation (angle + axis of rotation)

#ifndef __vtkKWOrientationWidget_h
#define __vtkKWOrientationWidget_h

#include "vtkKW3DWidget.h"
#include "XML/vtkXMLIOBaseMacros.h" // Needed for XML reader/writer macros

class vtkPolyData;
class vtkActor;
class vtkPolyDataMapper;
class vtkImageData;
class vtkTransform;

class VTK_EXPORT vtkKWOrientationWidget : public vtkKW3DWidget
{
public:
  static vtkKWOrientationWidget *New();
  vtkTypeRevisionMacro(vtkKWOrientationWidget, vtkKW3DWidget);
  void PrintSelf(ostream& os, vtkIndent indent);
  //BTX
  vtkKWGetXMLReaderWriterObjectsMacro();
  //ETX
  
  // Description:
  // Enable/disable the widget
  virtual void SetEnabled(int enabling);
  
  // Description:
  // Set/Get the color of the widget
  virtual void SetColor(double r, double g, double b);
  virtual void SetColor(double rgb[3])
    { this->SetColor(rgb[0], rgb[1], rgb[2]); }
  virtual double* GetColor();

  // Description:
  // Set/Get the color of the outline
  virtual void SetOutlineColor(double r, double g, double b);
  virtual void SetOutlineColor(double rgb[3])
    { this->SetOutlineColor(rgb[0], rgb[1], rgb[2]); }
  virtual double* GetOutlineColor();

  // Description:
  // Set/Get the annotation type
  //BTX  
  enum 
  {
    ANNOTATION_TYPE_GENERAL = 0,
    ANNOTATION_TYPE_MEDICAL = 1,
    ANNOTATION_TYPE_UNKNOWN
  };
  //ETX
  virtual int GetAnnotationType();
  virtual void SetAnnotationType(int type);
  virtual void SetAnnotationTypeToGeneral() 
    { this->SetAnnotationType(ANNOTATION_TYPE_GENERAL); }
  virtual void SetAnnotationTypeToMedical() 
    { this->SetAnnotationType(ANNOTATION_TYPE_MEDICAL); }
  
  // Description:
  // Make the camera position, focal point, and view up match that of the
  // active camera of the main renderer of the composite containing this
  // annotation
  virtual void UpdateCamera();

  // Description:
  // Use this method only when you break synchronisation in between 
  // ParentRender and actual Render. This allow for example to manipulate
  // the camera using a user-specified transform
  void UpdateCamera(double theta, const double axis[3]);
  
  virtual void SetParentRenderer(vtkRenderer *);
  
  // Description:
  // Place/Adjust widget within bounds
  //BTX
  virtual void PlaceWidget(double vtkNotUsed(bounds)[6]) {};
  void PlaceWidget()
    {this->Superclass::PlaceWidget();}
  void PlaceWidget(double xmin, double xmax, double ymin, double ymax, 
                   double zmin, double zmax)
    {this->Superclass::PlaceWidget(xmin,xmax,ymin,ymax,zmin,zmax);}
  //ETX

  // Description:
  // Set/Get the viewport for this widget
  virtual double* GetViewport();
  virtual void SetViewport(double, double, double, double);
  virtual void SetViewport(double vp[4])
    { this->SetViewport(vp[0], vp[1], vp[2], vp[3]); };

  // Description:
  // Give access to the renderer. Needed for the multiresolution 
  // texture mapping in the volume widget.
  vtkGetObjectMacro( Renderer, vtkRenderer );

  // Description:
  // Set / get whether this widget is displaying an image
  vtkSetMacro(SynchronizeRenderers, int);
  vtkGetMacro(SynchronizeRenderers, int);
  vtkBooleanMacro(SynchronizeRenderers, int);
 
  // Description:
  // Can the widget be repositioned or resized. On by default. If off, the 
  // widget cannot be moved around. 
  //
  // (PS: This should really be a flag in a superclass, say vtk3DWidget
  // and/or vtkAbstractWidget and/or vtkInteractorObserver. Widgets in VTK
  // subclass randomly from all three... )
  vtkSetMacro(     Repositionable, int );
  vtkGetMacro(     Repositionable, int );
  vtkBooleanMacro( Repositionable, int );
  vtkSetMacro(     Resizeable,     int );
  vtkGetMacro(     Resizeable,     int );
  vtkBooleanMacro( Resizeable,     int );

  // Description:
  // Set the co-ordinate system to RAS/LPS. All this does is to flip the marker
  // texture coords. So A and P are swapped, R and L are swapped.
  //BTX
  enum
  {
    COORDINATE_SYSTEM_MEDICAL_RAS = 0, 
    COORDINATE_SYSTEM_MEDICAL_LPS = 1,
    COORDINATE_SYSTEM_GENERAL = 2
  };
  //ETX
  vtkGetMacro(CoordinateSystem, int);
  virtual void SetCoordinateSystem(int type);
  virtual void SetCoordinateSystemMedicalToRAS()
    { this->SetCoordinateSystem(
        vtkKWOrientationWidget::COORDINATE_SYSTEM_MEDICAL_RAS); };
  virtual void SetCoordinateSystemMedicalToLPS()
    { this->SetCoordinateSystem(
        vtkKWOrientationWidget::COORDINATE_SYSTEM_MEDICAL_LPS); };

protected:
  vtkKWOrientationWidget();
  ~vtkKWOrientationWidget();
  
  int MouseCursorState;
  int Moving;
  int StartPosition[2];
  
  void UpdateCursorIcon();
  void SetMouseCursor(int cursorState);
  
  //BTX
  enum WidgetStates
  {
    Outside = 0,
    Inside,
    TopLeft,
    TopRight,
    BottomLeft,
    BottomRight
  };
  //ETX
  
  // Handles the events

  static void ProcessEvents(vtkObject* object,
                            unsigned long event,
                            void* clientdata,
                            void* calldata);
  
  void OnButtonPress();
  void OnMouseMove();
  void OnButtonRelease();

  void MoveWidget();
  void ResizeTopLeft();
  void ResizeTopRight();
  void ResizeBottomLeft();
  void ResizeBottomRight();

  // Internal methods.
  void SetTextureCoordsToGeneral();
  void SetTextureCoordsToRAS();
  void SetTextureCoordsToLPS();
  
  vtkActor          *CubeActor;
  vtkPolyDataMapper *CubeMapper;
  vtkPolyData       *Cube;

  vtkActor          *CubeOutlineActor;
  vtkPolyDataMapper *CubeOutlineMapper;

  vtkRenderer       *Renderer;
  vtkRenderer       *ParentRenderer;
  unsigned long     CameraCallbackTag;
  
  void SetCube(vtkPolyData*);
  
  void SetCubeTube(vtkPolyData*);
  
  int Initialized;
  int SynchronizeRenderers;

  vtkImageData *LettersTextureGeneral;
  vtkImageData *LettersTextureMedical;
  vtkTransform *AsynchronousTransform;
  
  int Repositionable;
  int Resizeable;
  int CoordinateSystem;

private:
  vtkKWOrientationWidget(const vtkKWOrientationWidget&);  //Not implemented
  void operator=(const vtkKWOrientationWidget&);  //Not implemented
};

#endif
