#include <vtkVersion.h>
#include <vtkSmartPointer.h>
#include <vtkActor.h>
#include <vtkBorderRepresentation.h>
#include <vtkBorderWidget.h>
#include <vtkCommand.h>
#include <vtkImageActor.h>
#include <vtkImageClip.h>
#include <vtkImageData.h>
#include <vtkImageMapper3D.h>
#include <vtkInteractorStyleImage.h>
#include <vtkJPEGReader.h>
#include <vtkMath.h>
#include <vtkProperty2D.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkXMLPolyDataReader.h>

#include <vtkInformation.h>
#include <vtkStreamingDemandDrivenPipeline.h>

#include <string>

class vtkBorderCallback2 : public vtkCommand
{
public:
  vtkBorderCallback2(){}

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

  virtual void Execute(vtkObject *caller, unsigned long, void*)
  {

    vtkBorderWidget *borderWidget =
      reinterpret_cast<vtkBorderWidget*>(caller);

    // Get the world coordinates of the two corners of the box
    vtkCoordinate* lowerLeftCoordinate =
      static_cast<vtkBorderRepresentation*>
      (borderWidget->GetRepresentation())->GetPositionCoordinate();
    double* lowerLeft =
      lowerLeftCoordinate->GetComputedWorldValue(this->LeftRenderer);
    std::cout << "Lower left coordinate: "
              << lowerLeft[0] << ", "
              << lowerLeft[1] << ", "
              << lowerLeft[2] << std::endl;

    vtkCoordinate* upperRightCoordinate =
      static_cast<vtkBorderRepresentation*>
      (borderWidget->GetRepresentation())->GetPosition2Coordinate();
    double* upperRight =
      upperRightCoordinate ->GetComputedWorldValue(this->LeftRenderer);
    std::cout << "Upper right coordinate: "
              << upperRight[0] << ", "
              << upperRight[1] << ", "
              << upperRight[2] << std::endl;

    double* bounds = this->ImageActor->GetBounds();
    double xmin = bounds[0];
    double xmax = bounds[1];
    double ymin = bounds[2];
    double ymax = bounds[3];

    if( (lowerLeft[0] > xmin) &&
        (upperRight[0] < xmax) &&
        (lowerLeft[1] > ymin) &&
        (upperRight[1] < ymax) )
    {
      this->ClipFilter->SetOutputWholeExtent(
        vtkMath::Round(lowerLeft[0]),
        vtkMath::Round(upperRight[0]),
        vtkMath::Round(lowerLeft[1]),
        vtkMath::Round(upperRight[1]), 0, 1);
    }
    else
    {
      std::cout << "box is NOT inside image" << std::endl;
    }
  }

  void SetLeftRenderer(vtkSmartPointer<vtkRenderer> renderer)
    {this->LeftRenderer = renderer;}
  void SetImageActor(vtkSmartPointer<vtkImageActor> actor)
    {this->ImageActor   = actor;}
  void SetClipFilter(vtkSmartPointer<vtkImageClip> clip)
    {this->ClipFilter   = clip;}

private:
  vtkSmartPointer<vtkRenderer>   LeftRenderer;
  vtkSmartPointer<vtkImageActor> ImageActor;
  vtkSmartPointer<vtkImageClip>  ClipFilter;
};

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

  std::string inputFilename = argv[1];

  // Read the image
  vtkSmartPointer<vtkJPEGReader> jPEGReader =
    vtkSmartPointer<vtkJPEGReader>::New();

  if( !jPEGReader->CanReadFile( inputFilename.c_str() ) )
  {
    std::cout << "Error: cannot read " << inputFilename << std::endl;
    return EXIT_FAILURE;
  }

  jPEGReader->SetFileName ( inputFilename.c_str() );
  jPEGReader->Update();

  int extent[6];
  jPEGReader->GetOutput()->GetExtent(extent);
  //xmin, xmax, ymin, ymax
  //std::cout << "extent: " << extent[0] << " " << extent[1] << " " <<
  //extent[2] << " " <<  extent[3] << " " <<  extent[4] << " " <<
  //extent[5] << std::endl;

  vtkSmartPointer<vtkImageActor> imageActor =
    vtkSmartPointer<vtkImageActor>::New();
  imageActor->GetMapper()->SetInputConnection(jPEGReader->GetOutputPort());

  vtkSmartPointer<vtkRenderWindow> renderWindow =
    vtkSmartPointer<vtkRenderWindow>::New();

  vtkSmartPointer<vtkRenderWindowInteractor> interactor =
    vtkSmartPointer<vtkRenderWindowInteractor>::New();

  vtkSmartPointer<vtkInteractorStyleImage> style =
    vtkSmartPointer<vtkInteractorStyleImage>::New();
  interactor->SetInteractorStyle( style );

  vtkSmartPointer<vtkBorderWidget> borderWidget =
    vtkSmartPointer<vtkBorderWidget>::New();
  borderWidget->SetInteractor(interactor);
  static_cast<vtkBorderRepresentation*>
    (borderWidget->GetRepresentation())->GetBorderProperty()->SetColor(0,1,0);
  borderWidget->SelectableOff();

  interactor->SetRenderWindow(renderWindow);


  // Define viewport ranges in normalized coordinates
  // (xmin, ymin, xmax, ymax)
  double leftViewport[4] = {0.0, 0.0, 0.5, 1.0};
  double rightViewport[4] = {0.5, 0.0, 1.0, 1.0};

  // Setup both renderers
  vtkSmartPointer<vtkRenderer> leftRenderer =
    vtkSmartPointer<vtkRenderer>::New();
  leftRenderer->SetBackground(1,0,0);
  renderWindow->AddRenderer(leftRenderer);
  leftRenderer->SetViewport(leftViewport);

  vtkSmartPointer<vtkRenderer> rightRenderer =
    vtkSmartPointer<vtkRenderer>::New();
  renderWindow->AddRenderer(rightRenderer);
  rightRenderer->SetViewport(rightViewport);

  leftRenderer->AddActor(imageActor);

  leftRenderer->ResetCamera();
  rightRenderer->ResetCamera();

  vtkSmartPointer<vtkImageClip> imageClip =
    vtkSmartPointer<vtkImageClip>::New();
  imageClip->SetInputConnection(jPEGReader->GetOutputPort());
#if VTK_MAJOR_VERSION <= 5
  imageClip->SetOutputWholeExtent(jPEGReader->GetOutput()->GetWholeExtent());
#else
  jPEGReader->UpdateInformation();
  imageClip->SetOutputWholeExtent(
    jPEGReader->GetOutputInformation(0)->Get(
      vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()));
#endif
  imageClip->ClipDataOn();

  vtkSmartPointer<vtkImageActor> clipActor =
    vtkSmartPointer<vtkImageActor>::New();
  clipActor->GetMapper()->SetInputConnection(
    imageClip->GetOutputPort());

  rightRenderer->AddActor(clipActor);

  vtkSmartPointer<vtkBorderCallback2> borderCallback =
    vtkSmartPointer<vtkBorderCallback2>::New();
  borderCallback->SetLeftRenderer(leftRenderer);
  borderCallback->SetImageActor(imageActor);
  borderCallback->SetClipFilter(imageClip);

  borderWidget->AddObserver(vtkCommand::InteractionEvent,borderCallback);

  renderWindow->Render();
  borderWidget->On();
  interactor->Start();

  return EXIT_SUCCESS;
}
