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

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

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkDataArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"

vtkStandardNewMacro(vtkReverseSense);

// Construct object so that behavior is to reverse cell ordering and
// leave normal orientation as is.
vtkReverseSense::vtkReverseSense()
{
  this->ReverseCells = 1;
  this->ReverseNormals = 0;
}

int vtkReverseSense::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
  vtkPolyData* input = vtkPolyData::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
  vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));

  vtkDataArray* normals = input->GetPointData()->GetNormals();
  vtkDataArray* cellNormals = input->GetCellData()->GetNormals();

  vtkDebugMacro(<< "Reversing sense of poly data");

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

  // If specified, traverse all cells and reverse them
  int abort = 0;
  vtkIdType progressInterval;

  if (this->ReverseCells)
  {
    vtkIdType numCells = input->GetNumberOfCells();
    vtkCellArray *verts, *lines, *polys, *strips;

    // Instantiate necessary topology arrays
    verts = vtkCellArray::New();
    verts->DeepCopy(input->GetVerts());
    lines = vtkCellArray::New();
    lines->DeepCopy(input->GetLines());
    polys = vtkCellArray::New();
    polys->DeepCopy(input->GetPolys());
    strips = vtkCellArray::New();
    strips->DeepCopy(input->GetStrips());

    output->SetVerts(verts);
    verts->Delete();
    output->SetLines(lines);
    lines->Delete();
    output->SetPolys(polys);
    polys->Delete();
    output->SetStrips(strips);
    strips->Delete();

    progressInterval = numCells / 10 + 1;
    for (vtkIdType cellId = 0; cellId < numCells && !abort; cellId++)
    {
      if (!(cellId % progressInterval)) // manage progress / early abort
      {
        this->UpdateProgress(0.6 * cellId / numCells);
        abort = this->GetAbortExecute();
      }
      output->ReverseCell(cellId);
    }
  }

  // If specified and normals available, reverse orientation of normals.
  // Using NewInstance() creates normals of the same data type.
  if (this->ReverseNormals && normals)
  {
    // first do point normals
    vtkIdType numPoints = input->GetNumberOfPoints();
    vtkDataArray* outNormals = normals->NewInstance();
    outNormals->SetNumberOfComponents(normals->GetNumberOfComponents());
    outNormals->SetNumberOfTuples(numPoints);
    outNormals->SetName(normals->GetName());
    double n[3];

    progressInterval = numPoints / 5 + 1;
    for (vtkIdType ptId = 0; ptId < numPoints; ++ptId)
    {
      if (!(ptId % progressInterval)) // manage progress / early abort
      {
        this->UpdateProgress(0.6 + 0.2 * ptId / numPoints);
        abort = this->GetAbortExecute();
      }
      normals->GetTuple(ptId, n);
      n[0] = -n[0];
      n[1] = -n[1];
      n[2] = -n[2];
      outNormals->SetTuple(ptId, n);
    }

    output->GetPointData()->SetNormals(outNormals);
    outNormals->Delete();
  }

  // now do cell normals
  if (this->ReverseNormals && cellNormals)
  {
    vtkIdType numCells = input->GetNumberOfCells();
    vtkDataArray* outNormals = cellNormals->NewInstance();
    outNormals->SetNumberOfComponents(cellNormals->GetNumberOfComponents());
    outNormals->SetNumberOfTuples(numCells);
    outNormals->SetName(cellNormals->GetName());
    double n[3];

    progressInterval = numCells / 5 + 1;
    for (vtkIdType cellId = 0; cellId < numCells && !abort; cellId++)
    {
      if (!(cellId % progressInterval)) // manage progress / early abort
      {
        this->UpdateProgress(0.8 + 0.2 * cellId / numCells);
        abort = this->GetAbortExecute();
      }

      cellNormals->GetTuple(cellId, n);
      n[0] = -n[0];
      n[1] = -n[1];
      n[2] = -n[2];
      outNormals->SetTuple(cellId, n);
    }

    output->GetCellData()->SetNormals(outNormals);
    outNormals->Delete();
  }

  return 1;
}

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

  os << indent << "Reverse Cells: " << (this->ReverseCells ? "On\n" : "Off\n");
  os << indent << "Reverse Normals: " << (this->ReverseNormals ? "On\n" : "Off\n");
}
