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

  Program:   Visualization Toolkit
  Module:    vtkInterpolateDataSetAttributes.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 "vtkInterpolateDataSetAttributes.h"

#include "vtkCellData.h"
#include "vtkDataSetCollection.h"
#include "vtkExecutive.h"
#include "vtkGarbageCollector.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkRectilinearGrid.h"
#include "vtkStructuredGrid.h"
#include "vtkStructuredPoints.h"
#include "vtkUnstructuredGrid.h"

vtkStandardNewMacro(vtkInterpolateDataSetAttributes);

// Create object with no input or output.
vtkInterpolateDataSetAttributes::vtkInterpolateDataSetAttributes()
{
  this->InputList = vtkDataSetCollection::New();

  this->T = 0.0;
}

vtkInterpolateDataSetAttributes::~vtkInterpolateDataSetAttributes()
{
  if (this->InputList)
  {
    this->InputList->Delete();
    this->InputList = nullptr;
  }
}

vtkDataSetCollection* vtkInterpolateDataSetAttributes::GetInputList()
{
  int i;
  this->InputList->RemoveAllItems();

  for (i = 0; i < this->GetNumberOfInputConnections(0); i++)
  {
    this->InputList->AddItem(static_cast<vtkDataSet*>(this->GetExecutive()->GetInputData(0, i)));
  }
  return this->InputList;
}

// Interpolate the data
int vtkInterpolateDataSetAttributes::RequestData(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  // get the info object
  vtkInformation* outInfo = outputVector->GetInformationObject(0);

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

  vtkIdType numPts, numCells, i;
  int numInputs = this->GetNumberOfInputConnections(0);
  int lowDS, highDS;
  vtkDataSet *ds, *ds2;
  vtkPointData* outputPD = output->GetPointData();
  vtkCellData* outputCD = output->GetCellData();
  vtkPointData *inputPD, *input2PD;
  vtkCellData *inputCD, *input2CD;
  double t;

  if (numInputs < 2)
  {
    vtkErrorMacro(<< "Need at least two inputs to interpolate!");
    return 1;
  }

  vtkDebugMacro(<< "Interpolating data...");

  // Check input and determine between which data sets the interpolation
  // is to occur.
  if (this->T > static_cast<double>(numInputs))
  {
    vtkErrorMacro(<< "Bad interpolation parameter");
    return 1;
  }

  lowDS = static_cast<int>(this->T);
  if (lowDS >= (numInputs - 1))
  {
    lowDS = numInputs - 2;
  }

  highDS = lowDS + 1;
  t = this->T - static_cast<double>(lowDS);
  if (t > 1.0)
  {
    t = 1.0;
  }

  vtkInformation* dsInfo = inputVector[0]->GetInformationObject(lowDS);
  vtkInformation* ds2Info = inputVector[0]->GetInformationObject(highDS);
  ds = vtkDataSet::SafeDownCast(dsInfo->Get(vtkDataObject::DATA_OBJECT()));
  ds2 = vtkDataSet::SafeDownCast(ds2Info->Get(vtkDataObject::DATA_OBJECT()));

  numPts = ds->GetNumberOfPoints();
  numCells = ds->GetNumberOfCells();

  if (numPts != ds2->GetNumberOfPoints() || numCells != ds2->GetNumberOfCells())
  {
    vtkErrorMacro(<< "Data sets not consistent!");
    return 1;
  }

  output->CopyStructure(ds);
  inputPD = ds->GetPointData();
  inputCD = ds->GetCellData();
  input2PD = ds2->GetPointData();
  input2CD = ds2->GetCellData();

  // Allocate the data set attributes
  outputPD->CopyAllOff();
  if (inputPD->GetScalars() && input2PD->GetScalars())
  {
    outputPD->CopyScalarsOn();
  }
  if (inputPD->GetVectors() && input2PD->GetVectors())
  {
    outputPD->CopyVectorsOn();
  }
  if (inputPD->GetNormals() && input2PD->GetNormals())
  {
    outputPD->CopyNormalsOn();
  }
  if (inputPD->GetTCoords() && input2PD->GetTCoords())
  {
    outputPD->CopyTCoordsOn();
  }
  if (inputPD->GetTensors() && input2PD->GetTensors())
  {
    outputPD->CopyTensorsOn();
  }
  // *TODO* Fix
  // if ( inputPD->GetFieldData() && input2PD->GetFieldData() )
  //{
  // outputPD->CopyFieldDataOn();
  //}
  outputPD->InterpolateAllocate(inputPD);

  outputCD->CopyAllOff();
  if (inputCD->GetScalars() && input2CD->GetScalars())
  {
    outputCD->CopyScalarsOn();
  }
  if (inputCD->GetVectors() && input2CD->GetVectors())
  {
    outputCD->CopyVectorsOn();
  }
  if (inputCD->GetNormals() && input2CD->GetNormals())
  {
    outputCD->CopyNormalsOn();
  }
  if (inputCD->GetTCoords() && input2CD->GetTCoords())
  {
    outputCD->CopyTCoordsOn();
  }
  if (inputCD->GetTensors() && input2CD->GetTensors())
  {
    outputCD->CopyTensorsOn();
  }
  // *TODO* Fix
  // if ( inputCD->GetFieldData() && input2CD->GetFieldData() )
  //{
  // outputCD->CopyFieldDataOn();
  //  }
  outputCD->InterpolateAllocate(inputCD);

  // Interpolate point data. We'll assume that it takes 50% of the time
  for (i = 0; i < numPts; i++)
  {
    if (!(i % 10000))
    {
      this->UpdateProgress(static_cast<double>(i) / numPts * 0.50);
      if (this->GetAbortExecute())
      {
        break;
      }
    }

    outputPD->InterpolateTime(inputPD, input2PD, i, t);
  }

  // Interpolate cell data. We'll assume that it takes 50% of the time
  for (i = 0; i < numCells; i++)
  {
    if (!(i % 10000))
    {
      this->UpdateProgress(0.5 + static_cast<double>(i) / numCells * 0.50);
      if (this->GetAbortExecute())
      {
        break;
      }
    }

    outputCD->InterpolateTime(inputCD, input2CD, i, t);
  }

  return 1;
}

int vtkInterpolateDataSetAttributes::FillInputPortInformation(int port, vtkInformation* info)
{
  if (!this->Superclass::FillInputPortInformation(port, info))
  {
    return 0;
  }
  info->Set(vtkAlgorithm::INPUT_IS_REPEATABLE(), 1);
  return 1;
}

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

  os << indent << "T: " << this->T << endl;
}

//----------------------------------------------------------------------------
void vtkInterpolateDataSetAttributes ::ReportReferences(vtkGarbageCollector* collector)
{
  this->Superclass::ReportReferences(collector);
  vtkGarbageCollectorReport(collector, this->InputList, "InputList");
}
