#include <vtkSmartPointer.h>

#include <vtkImageReader2Factory.h>
#include <vtkImageReader2.h>
#include <vtkAssemblyPath.h>
#include <vtkCell.h>
#include <vtkCommand.h>
#include <vtkCornerAnnotation.h>
#include <vtkDataArray.h>
#include <vtkImageActor.h>
#include <vtkImageData.h>
#include <vtkImageViewer2.h>
#include <vtkInteractorStyleImage.h>
#include <vtkPointData.h>
#include <vtkPropPicker.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkTextProperty.h>

// The mouse motion callback, to pick the image and recover pixel values
class vtkImageInteractionCallback1 : public vtkCommand
{
public:

  static vtkImageInteractionCallback1 *New()
  {
    return new vtkImageInteractionCallback1;
  }

  vtkImageInteractionCallback1()
  {
    this->Viewer = 0;
    this->Picker = 0;
    this->Annotation = 0;
    this->PointData = vtkPointData::New();
  }

  ~vtkImageInteractionCallback1()
  {
     this->Viewer = 0;
     this->Picker = 0;
     this->Annotation = 0;
     this->PointData->Delete();
  }

  void SetPicker(vtkPropPicker *picker)
  {
    this->Picker = picker;
  }

  void SetAnnotation(vtkCornerAnnotation *annotation)
  {
    this->Annotation = annotation;
  }

  void SetViewer(vtkImageViewer2 *viewer)
  {
    this->Viewer = viewer;
  }

    virtual void Execute(vtkObject *, unsigned long vtkNotUsed(event), void *)
    {
      //this->Viewer;
      vtkRenderWindowInteractor *interactor =
        this->Viewer->GetRenderWindow()->GetInteractor();
      vtkRenderer* renderer = this->Viewer->GetRenderer();
      vtkImageActor* actor = this->Viewer->GetImageActor();
      vtkImageData* image = this->Viewer->GetInput();
      vtkInteractorStyle *style = vtkInteractorStyle::SafeDownCast(
          interactor->GetInteractorStyle());

      // Pick at the mouse location provided by the interactor
      this->Picker->Pick( interactor->GetEventPosition()[0],
                          interactor->GetEventPosition()[1],
                          0.0, renderer );

      // There could be other props assigned to this picker, so
      // make sure we picked the image actor
      vtkAssemblyPath* path = this->Picker->GetPath();
      bool validPick = false;

      if( path )
      {
        vtkCollectionSimpleIterator sit;
        path->InitTraversal( sit );
        vtkAssemblyNode *node;
        for( int i = 0; i < path->GetNumberOfItems() && !validPick; ++i )
        {
          node = path->GetNextNode( sit );
          if( actor == vtkImageActor::SafeDownCast( node->GetViewProp() ) )
          {
            validPick = true;
          }
        }
      }

      if( !validPick )
      {
        this->Annotation->SetText( 0, "Off Image" );
        interactor->Render();
        // Pass the event further on
        style->OnMouseMove();
        return;
      }

      // Get the world coordinates of the pick
      double pos[3];
      this->Picker->GetPickPosition( pos );
      // Fixes some numerical problems with the picking
      double *bounds = actor->GetDisplayBounds();
      int axis = this->Viewer->GetSliceOrientation();
      pos[axis] = bounds[2*axis];

      vtkPointData* pd = image->GetPointData();
      if( !pd )
      {
        return;
      }

      this->PointData->InterpolateAllocate( pd, 1, 1 );

      // Use tolerance as a function of size of source data
      double tol2 = image->GetLength();
      tol2 = tol2 ? tol2*tol2 / 1000.0 : 0.001;

      // Find the cell that contains pos
      int subId;
      double pcoords[3], weights[8];
      vtkCell* cell = image->FindAndGetCell(
          pos, NULL, -1, tol2, subId, pcoords, weights );
      if( cell )
      {
        // Interpolate the point data
        this->PointData->InterpolatePoint( pd, 0, cell->PointIds, weights );
        int components =
          this->PointData->GetScalars()->GetNumberOfComponents();
        double* tuple = this->PointData->GetScalars()->GetTuple( 0 );

        std::string message = "Location: ( ";
        message += vtkVariant( pos[0] ).ToString();
        message += ", ";
        message += vtkVariant( pos[1] ).ToString();
        message += ", ";
        message += vtkVariant( pos[2] ).ToString();
        message += " )\nValue: ( ";

        for( int c = 0; c < components; ++c )
        {
          message += vtkVariant( tuple[ c ] ).ToString();
          if( c != components - 1 )
          {
            message += ", ";
          }
        }
        message += " )";
        this->Annotation->SetText( 0, message.c_str() );
        interactor->Render();
        style->OnMouseMove();
      }
    }

private:

  // Pointer to the viewer
  vtkImageViewer2 *Viewer;

  // Pointer to the picker
  vtkPropPicker *Picker;

  // Pointer to the annotation
  vtkCornerAnnotation *Annotation;

  // Interpolator
  vtkPointData* PointData;
};


int main ( int argc, char* argv[] )
{
//Verify input arguments
  if ( argc != 2 )
  {
    std::cout << "Usage: " << argv[0]
              << " Filename" << std::endl;
    return EXIT_FAILURE;
  }

  // Read the image
  vtkSmartPointer<vtkImageReader2Factory> readerFactory =
    vtkSmartPointer<vtkImageReader2Factory>::New();
  vtkImageReader2 *reader = readerFactory->CreateImageReader2(argv[1]);
  reader->SetFileName(argv[1]);

  // Picker to pick pixels
  vtkSmartPointer<vtkPropPicker> propPicker =
    vtkSmartPointer<vtkPropPicker>::New();
  propPicker->PickFromListOn();

  // Give the picker a prop to pick
  vtkSmartPointer<vtkImageViewer2> imageViewer =
    vtkSmartPointer<vtkImageViewer2>::New();
  propPicker->AddPickList( imageViewer->GetImageActor() );

  // Visualize
  vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
    vtkSmartPointer<vtkRenderWindowInteractor>::New();
  imageViewer->SetInputConnection( reader->GetOutputPort() );
  imageViewer->SetupInteractor( renderWindowInteractor );
  imageViewer->SetSize( 600, 600 );

  vtkRenderer* renderer = imageViewer->GetRenderer();
  renderer->ResetCamera();
  renderer->GradientBackgroundOn();
  renderer->SetBackground(0,0,0);
  renderer->SetBackground2(1,1,1);

  // Annotate the image with window/level and mouse over pixel information
  vtkSmartPointer<vtkCornerAnnotation> cornerAnnotation =
    vtkSmartPointer<vtkCornerAnnotation>::New();
  cornerAnnotation->SetLinearFontScaleFactor( 2 );
  cornerAnnotation->SetNonlinearFontScaleFactor( 1 );
  cornerAnnotation->SetMaximumFontSize( 20 );
  cornerAnnotation->SetText( 0, "Off Image" );
  cornerAnnotation->SetText( 3, "<window>\n<level>" );
  cornerAnnotation->GetTextProperty()->SetColor( 1,0,0);

  imageViewer->GetRenderer()->AddViewProp( cornerAnnotation );

  // Callback listens to MouseMoveEvents invoked by the interactor's style
  vtkSmartPointer<vtkImageInteractionCallback1> callback =
    vtkSmartPointer<vtkImageInteractionCallback1>::New();
  callback->SetViewer( imageViewer );
  callback->SetAnnotation( cornerAnnotation );
  callback->SetPicker( propPicker );

  // InteractorStyleImage allows for the following controls:
  // 1) middle mouse + move = camera pan
  // 2) left mouse + move = window/level
  // 3) right mouse + move = camera zoom
  // 4) middle mouse wheel scroll = zoom
  // 5) 'r' = reset window/level
  // 6) shift + 'r' = reset camera
  vtkInteractorStyleImage* imageStyle = imageViewer->GetInteractorStyle();
  imageStyle->AddObserver( vtkCommand::MouseMoveEvent, callback );

  renderWindowInteractor->Initialize();
  renderWindowInteractor->Start();

  reader->Delete();
  return EXIT_SUCCESS;
}
