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

  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 "vtk3DCursorAnnotation.h"
#include "vtkObjectFactory.h"

#include "vtkCellArray.h"
#include "vtkDataSet.h"
#include "vtkFloatArray.h"
#include "vtkKWVolumeWidget.h"
#include "vtkLookupTable.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderer.h"
#include "vtkScalarsToColors.h"

vtkStandardNewMacro(vtk3DCursorAnnotation);
vtkCxxRevisionMacro(vtk3DCursorAnnotation, "$Revision: 1.19 $");

vtkCxxSetObjectMacro(vtk3DCursorAnnotation, Input, vtkDataSet);

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXML3DCursorAnnotationReader.h"
#include "XML/vtkXML3DCursorAnnotationWriter.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtk3DCursorAnnotation, vtkXML3DCursorAnnotationReader, vtkXML3DCursorAnnotationWriter);

vtk3DCursorAnnotation::vtk3DCursorAnnotation()
{
  this->Input = NULL;
  this->RenderWidget = NULL;
  
  this->CursorType = vtk3DCursorAnnotation::CURSOR_TYPE_CROSSHAIR;
  this->CursorPosition[0] = 0;
  this->CursorPosition[1] = 0;
  this->CursorPosition[2] = 0;
  
  this->Cursor = vtkPolyData::New();
  vtkFloatArray *cScalars = vtkFloatArray::New();
  this->Cursor->GetPointData()->SetScalars(cScalars);
  cScalars->Delete();
  vtkPoints *cpts = vtkPoints::New();
  this->Cursor->SetPoints(cpts);
  cpts->Delete();
  vtkCellArray *cca = vtkCellArray::New();
  this->Cursor->SetLines(cca);
  cca->Delete();
  vtkCellArray *cca2 = vtkCellArray::New();
  this->Cursor->SetPolys(cca2);
  cca2->Delete();
  
  this->CursorXAxisColor[0] = 1.0;
  this->CursorXAxisColor[1] = 0.0;
  this->CursorXAxisColor[2] = 0.0;

  this->CursorYAxisColor[0] = 0.0;
  this->CursorYAxisColor[1] = 1.0;
  this->CursorYAxisColor[2] = 0.0;

  this->CursorZAxisColor[0] = 0.0;
  this->CursorZAxisColor[1] = 0.0;
  this->CursorZAxisColor[2] = 1.0;

  this->GetProperty()->SetAmbient(1.0);
  this->GetProperty()->SetDiffuse(0.0);
  this->GetProperty()->SetSpecular(0.0);
  this->GetProperty()->SetLineWidth(2.0);
  
  vtkLookupTable *lut = vtkLookupTable::New();
  lut->SetRange(0.0, 1.0);
  lut->SetNumberOfTableValues(3);
  this->UpdateLookupTableColors();

  vtkPolyDataMapper *mapper = vtkPolyDataMapper::New();
  mapper->SetInput(this->Cursor);
  mapper->SetLookupTable(lut);
  lut->Delete();
  this->SetMapper(mapper);
  mapper->Delete();
}

//----------------------------------------------------------------------------
vtk3DCursorAnnotation::~vtk3DCursorAnnotation()
{
  this->Cursor->Delete();
  this->SetInput(NULL);
  this->SetRenderWidget(NULL);
}

//----------------------------------------------------------------------------
void vtk3DCursorAnnotation::SetCursorType(int type)
{
  if (this->CursorType != type)
    {
    this->CursorType = type;
    this->UpdateGeometry();
    }
}

//----------------------------------------------------------------------------
void vtk3DCursorAnnotation::UpdateLookupTableColors()
{
  if (!this->GetMapper())
    {
    return;
    }

  vtkLookupTable *lut = 
    vtkLookupTable::SafeDownCast(this->GetMapper()->GetLookupTable());
  if (!lut)
    {
    return;
    }

  lut->SetTableValue(0, 
                     this->CursorXAxisColor[0], 
                     this->CursorXAxisColor[1], 
                     this->CursorXAxisColor[2]);

  lut->SetTableValue(1, 
                     this->CursorYAxisColor[0], 
                     this->CursorYAxisColor[1], 
                     this->CursorYAxisColor[2]);

  lut->SetTableValue(2, 
                     this->CursorZAxisColor[0], 
                     this->CursorZAxisColor[1], 
                     this->CursorZAxisColor[2]);
}

//----------------------------------------------------------------------------
void vtk3DCursorAnnotation::SetCursorXAxisColor(double r, double g, double b)
{
  if (this->CursorXAxisColor[0] == r &&
      this->CursorXAxisColor[1] == g &&
      this->CursorXAxisColor[2] == b)
    {
    return;
    }
      
  this->CursorXAxisColor[0] = r;
  this->CursorXAxisColor[1] = g;
  this->CursorXAxisColor[2] = b;

  this->Modified();

  this->UpdateLookupTableColors();
}

//----------------------------------------------------------------------------
void vtk3DCursorAnnotation::SetCursorYAxisColor(double r, double g, double b)
{
  if (this->CursorYAxisColor[0] == r &&
      this->CursorYAxisColor[1] == g &&
      this->CursorYAxisColor[2] == b)
    {
    return;
    }
      
  this->CursorYAxisColor[0] = r;
  this->CursorYAxisColor[1] = g;
  this->CursorYAxisColor[2] = b;

  this->Modified();

  this->UpdateLookupTableColors();
}

//----------------------------------------------------------------------------
void vtk3DCursorAnnotation::SetCursorZAxisColor(double r, double g, double b)
{
  if (this->CursorZAxisColor[0] == r &&
      this->CursorZAxisColor[1] == g &&
      this->CursorZAxisColor[2] == b)
    {
    return;
    }
      
  this->CursorZAxisColor[0] = r;
  this->CursorZAxisColor[1] = g;
  this->CursorZAxisColor[2] = b;

  this->Modified();

  this->UpdateLookupTableColors();
}

//----------------------------------------------------------------------------
void vtk3DCursorAnnotation::SetCursorPosition(double x, double y, double z)
{
  if (!this->Input)
    {
    return;
    }

  double *bounds = this->Input->GetBounds();
  
  if (x < bounds[0])
    {
    x = bounds[0];
    }
  else if (x > bounds[1])
    {
    x = bounds[1];
    }
  if (y < bounds[2])
    {
    y = bounds[2];
    }
  else if (y > bounds[3])
    {
    y = bounds[3];
    }
  if (z < bounds[4])
    {
    z = bounds[4];
    }
  else if (z > bounds[5])
    {
    z = bounds[5];
    }
  
  this->CursorPosition[0] = x;
  this->CursorPosition[1] = y;
  this->CursorPosition[2] = z;
  
  this->UpdateGeometry();
}

//----------------------------------------------------------------------------
int vtk3DCursorAnnotation::IsCursorPositionInBounds(double bounds[6])
{
  return (bounds && 
          this->CursorPosition &&
          this->CursorPosition[0] >= bounds[0] &&
          this->CursorPosition[0] <= bounds[1] &&
          this->CursorPosition[1] >= bounds[2] &&
          this->CursorPosition[1] <= bounds[3] &&
          this->CursorPosition[2] >= bounds[4] &&
          this->CursorPosition[2] <= bounds[5]);
}

//----------------------------------------------------------------------------
void vtk3DCursorAnnotation::SetVisibility(int state)
{
  if (state == this->Visibility)
    {
    return;
    }
  
  this->Visibility = state;
  vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(this->RenderWidget);
  if (vw)
    {
    if (state)
      {
      vw->StartUsingCursor3D();
      this->UpdateGeometry();
      this->VisibilityOn();
      }
    else
      {
      vw->StopUsingCursor3D();
      }
    }
}

//----------------------------------------------------------------------------
void vtk3DCursorAnnotation::UpdateGeometry()
{
  if (!this->Input)
    {
    return;
    }

  vtkPoints *pts = this->Cursor->GetPoints();
  vtkDataArray *scalars = this->Cursor->GetPointData()->GetScalars();
  scalars->Reset();

  double bounds[6];
  this->Input->GetBounds(bounds);
  
  if (this->CursorType == vtk3DCursorAnnotation::CURSOR_TYPE_CROSSHAIR)
    {
    this->Cursor->GetPolys()->Reset();
    vtkCellArray *lines = this->Cursor->GetLines();
    lines->Reset();
    
    scalars->InsertTuple1(0, 0.0);
    scalars->InsertTuple1(1, 0.0);
    scalars->InsertTuple1(2, 0.5);
    scalars->InsertTuple1(3, 0.5);
    scalars->InsertTuple1(4, 1.0);
    scalars->InsertTuple1(5, 1.0);
    
    pts->InsertPoint(0, bounds[0], this->CursorPosition[1], this->CursorPosition[2]);
    pts->InsertPoint(1, bounds[1], this->CursorPosition[1], this->CursorPosition[2]);
    pts->InsertPoint(2, this->CursorPosition[0], bounds[2], this->CursorPosition[2]);
    pts->InsertPoint(3, this->CursorPosition[0], bounds[3], this->CursorPosition[2]);
    pts->InsertPoint(4, this->CursorPosition[0], this->CursorPosition[1], bounds[4]);
    pts->InsertPoint(5, this->CursorPosition[0], this->CursorPosition[1], bounds[5]);
    
    lines->InsertNextCell(2);
    lines->InsertCellPoint(0);
    lines->InsertCellPoint(1);
    lines->InsertNextCell(2);
    lines->InsertCellPoint(2);
    lines->InsertCellPoint(3);
    lines->InsertNextCell(2);
    lines->InsertCellPoint(4);
    lines->InsertCellPoint(5);
    }
  else if (this->CursorType == vtk3DCursorAnnotation::CURSOR_TYPE_PLANE)
    {
    this->Cursor->GetLines()->Reset();
    vtkCellArray *planes = this->Cursor->GetPolys();
    planes->Reset();
    
    scalars->InsertTuple1(0, 0.0);
    scalars->InsertTuple1(1, 0.0);
    scalars->InsertTuple1(2, 0.0);
    scalars->InsertTuple1(3, 0.0);
    scalars->InsertTuple1(4, 0.5);
    scalars->InsertTuple1(5, 0.5);
    scalars->InsertTuple1(6, 0.5);
    scalars->InsertTuple1(7, 0.5);
    scalars->InsertTuple1(8, 1.0);
    scalars->InsertTuple1(9, 1.0);
    scalars->InsertTuple1(10, 1.0);
    scalars->InsertTuple1(11, 1.0);
    
    pts->InsertPoint(0, this->CursorPosition[0], bounds[2], bounds[4]);
    pts->InsertPoint(1, this->CursorPosition[0], bounds[3], bounds[4]);
    pts->InsertPoint(2, this->CursorPosition[0], bounds[3], bounds[5]);
    pts->InsertPoint(3, this->CursorPosition[0], bounds[2], bounds[5]);
    
    pts->InsertPoint(4, bounds[0], this->CursorPosition[1], bounds[4]);
    pts->InsertPoint(5, bounds[1], this->CursorPosition[1], bounds[4]);
    pts->InsertPoint(6, bounds[1], this->CursorPosition[1], bounds[5]);
    pts->InsertPoint(7, bounds[0], this->CursorPosition[1], bounds[5]);
    
    pts->InsertPoint(8, bounds[0], bounds[2], this->CursorPosition[2]);
    pts->InsertPoint(9, bounds[1], bounds[2], this->CursorPosition[2]);
    pts->InsertPoint(10, bounds[1], bounds[3], this->CursorPosition[2]);
    pts->InsertPoint(11, bounds[0], bounds[3], this->CursorPosition[2]);
    
    planes->InsertNextCell(4);
    planes->InsertCellPoint(0);
    planes->InsertCellPoint(1);
    planes->InsertCellPoint(2);
    planes->InsertCellPoint(3);
    
    planes->InsertNextCell(4);
    planes->InsertCellPoint(4);
    planes->InsertCellPoint(5);
    planes->InsertCellPoint(6);
    planes->InsertCellPoint(7);
    
    planes->InsertNextCell(4);
    planes->InsertCellPoint(8);
    planes->InsertCellPoint(9);
    planes->InsertCellPoint(10);
    planes->InsertCellPoint(11);
    }
  else
    {
    vtkWarningMacro(<< "Unknown cursor type:" << this->CursorType);
    }
  this->Cursor->Modified();
}

//----------------------------------------------------------------------------
void vtk3DCursorAnnotation::Render(vtkRenderer *ren, vtkMapper *m)
{
  if ( ! this->Property)
    {
    this->GetProperty();
    }
  this->Property->Render(this, ren);
  m->Render(ren, this);
}

//----------------------------------------------------------------------------
void vtk3DCursorAnnotation::SetRenderWidget(vtkKWRenderWidget *widget)
{
  // avoiding a reference-counting loop
  this->RenderWidget = widget;
}

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

  os << indent << "CursorType: " << this->CursorType << endl;
  os << indent << "Input: " << this->Input << endl;
  os << indent << "RenderWidget: " << this->RenderWidget << endl;
  os << indent << "CursorPosition: (" 
     << this->CursorPosition[0] << ", "
     << this->CursorPosition[1] << ", "
     << this->CursorPosition[2] << ")" << endl;
  os << indent << "CursorXAxisColor: (" 
     << this->CursorXAxisColor[0] << ", "
     << this->CursorXAxisColor[1] << ", "
     << this->CursorXAxisColor[2] << ")" << endl;
  os << indent << "CursorYAxisColor: (" 
     << this->CursorYAxisColor[0] << ", "
     << this->CursorYAxisColor[1] << ", "
     << this->CursorYAxisColor[2] << ")" << endl;
  os << indent << "CursorZAxisColor: (" 
     << this->CursorZAxisColor[0] << ", "
     << this->CursorZAxisColor[1] << ", "
     << this->CursorZAxisColor[2] << ")" << endl;
}
