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

  Program:   Visualization Toolkit
  Module:    vtkSimpleElevationFilter.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.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 "vtkSimpleElevationFilter.h"

#include "vtkCellData.h"
#include "vtkPointSet.h"
#include "vtkFloatArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkSMPTools.h"

vtkStandardNewMacro(vtkSimpleElevationFilter);

// The heart of the algorithm plus interface to the SMP tools. Double templated
// over point and scalar types.
template <class TP>
class vtkSimpleElevationAlgorithm
{
public:
  vtkIdType NumPts;
  double Vector[3];
  const TP *Points;
  float *Scalars;

  // Constructor
  vtkSimpleElevationAlgorithm();

  // Interface between VTK and templated functions
  static void Elevate(vtkSimpleElevationFilter *self, vtkIdType numPts,
                      const TP *points, float *scalars);

  // Interface implicit function computation to SMP tools.
  template <class T> class ElevationOp
  {
    public:
      ElevationOp(vtkSimpleElevationAlgorithm<T> *algo)
        { this->Algo = algo;}
      vtkSimpleElevationAlgorithm *Algo;
      void  operator() (vtkIdType k, vtkIdType end)
      {
        const double *v = this->Algo->Vector;
        const TP *p = this->Algo->Points + 3*k;
        float *s = this->Algo->Scalars + k;
        for ( ; k < end; ++k)
        {
          *s = v[0]*p[0] + v[1]*p[1] + v[2]*p[2];
          p+=3;
          ++s;
        }
      }
  };
};

//----------------------------------------------------------------------------
// Initialized mainly to eliminate compiler warnings.
template <class TP> vtkSimpleElevationAlgorithm<TP>::
vtkSimpleElevationAlgorithm():Points(nullptr),Scalars(nullptr)
{
  this->Vector[0] = this->Vector[1] = this->Vector[2] = 0.0;
}

//----------------------------------------------------------------------------
// Templated class is glue between VTK and templated algorithms.
template <class TP> void vtkSimpleElevationAlgorithm<TP>::
Elevate(vtkSimpleElevationFilter *self, vtkIdType numPts,
        const TP *points, float *scalars)
{
  // Populate data into local storage
  vtkSimpleElevationAlgorithm<TP> algo;
  algo.NumPts = numPts;
  self->GetVector(algo.Vector);
  algo.Points = points;
  algo.Scalars = scalars;

  // Okay now generate samples using SMP tools
  ElevationOp<TP> values(&algo);
  vtkSMPTools::For(0,algo.NumPts, values);
}


// Okay begin the class proper
//----------------------------------------------------------------------------
// Construct object with Vector=(0,0,1).
vtkSimpleElevationFilter::vtkSimpleElevationFilter()
{
  this->Vector[0] = 0.0;
  this->Vector[1] = 0.0;
  this->Vector[2] = 1.0;
}

//----------------------------------------------------------------------------
// Convert position along the ray into scalar value.  Example use includes
// coloring terrain by elevation.
//
int vtkSimpleElevationFilter::RequestData(
  vtkInformation *vtkNotUsed(request),
  vtkInformationVector **inputVector,
  vtkInformationVector *outputVector)
{
  // get the info objects
  vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation *outInfo = outputVector->GetInformationObject(0);

  // get the input and output
  vtkDataSet *input = vtkDataSet::SafeDownCast(
    inInfo->Get(vtkDataObject::DATA_OBJECT()));
  vtkDataSet *output = vtkDataSet::SafeDownCast(
    outInfo->Get(vtkDataObject::DATA_OBJECT()));

  vtkIdType i, numPts;
  vtkFloatArray *newScalars;
  double s, x[3];

  // Initialize
  //
  vtkDebugMacro(<<"Generating elevation scalars!");

  // First, copy the input to the output as a starting point
  output->CopyStructure( input );

  if ( ((numPts=input->GetNumberOfPoints()) < 1) )
  {
    vtkDebugMacro(<< "No input!");
    return 1;
  }

  // Allocate
  //
  newScalars = vtkFloatArray::New();
  newScalars->SetNumberOfTuples(numPts);

  // Ensure that there is a valid vector
  //
  if ( vtkMath::Dot(this->Vector,this->Vector) == 0.0)
  {
    vtkErrorMacro(<< "Bad vector, using (0,0,1)");
    this->Vector[0] = this->Vector[1] = 0.0; this->Vector[2] = 1.0;
  }

  // Create a fast path for point set input
  //
  vtkPointSet *ps = vtkPointSet::SafeDownCast(input);
  if ( ps )
  {
    float *scalars =
      static_cast<float*>(newScalars->GetVoidPointer(0));
    vtkPoints *points = ps->GetPoints();
    void *pts = points->GetData()->GetVoidPointer(0);
    switch ( points->GetDataType() )
    {
      vtkTemplateMacro(vtkSimpleElevationAlgorithm<VTK_TT>::
                       Elevate(this,numPts,(VTK_TT *)pts,scalars));
    }
  }//fast path

  else
  {
    // Too bad, got to take the scenic route.
    // Compute dot product.
    //
    int abort=0;
    vtkIdType progressInterval=numPts/20 + 1;
    for (i=0; i<numPts && !abort; i++)
    {
      if ( ! (i % progressInterval) )
      {
        this->UpdateProgress ((double)i/numPts);
        abort = this->GetAbortExecute();
      }

      input->GetPoint(i,x);
      s = vtkMath::Dot(this->Vector,x);
      newScalars->SetComponent(i,0,s);
    }
  }

  // Update self
  //
  output->GetPointData()->CopyScalarsOff();
  output->GetPointData()->PassData(input->GetPointData());

  output->GetCellData()->PassData(input->GetCellData());

  newScalars->SetName("Elevation");
  output->GetPointData()->AddArray(newScalars);
  output->GetPointData()->SetActiveScalars(newScalars->GetName());
  newScalars->Delete();

  return 1;
}

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

  os << indent << "Vector: (" << this->Vector[0] << ", "
     << this->Vector[1] << ", " << this->Vector[2] << ")\n";
}
