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

  Program:   Visualization Toolkit
  Module:    vtkCellTypes.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 "vtkCellTypes.h"
#include "vtkObjectFactory.h"

vtkStandardNewMacro(vtkCellTypes);

// This list should contain the cell class names in
// the same order as the enums in vtkCellType.h. Make sure
// this list is nullptr terminated.
static const char* vtkCellTypesStrings[] = { "vtkEmptyCell", "vtkVertex", "vtkPolyVertex",
  "vtkLine", "vtkPolyLine", "vtkTriangle", "vtkTriangleStrip", "vtkPolygon", "vtkPixel", "vtkQuad",
  "vtkTetra", "vtkVoxel", "vtkHexahedron", "vtkWedge", "vtkPyramid", "vtkPentagonalPrism",
  "vtkHexagonalPrism", "UnknownClass", "UnknownClass", "UnknownClass", "UnknownClass",
  "vtkQuadraticEdge", "vtkQuadraticTriangle", "vtkQuadraticQuad", "vtkQuadraticTetra",
  "vtkQuadraticHexahedron", "vtkQuadraticWedge", "vtkQuadraticPyramid", "vtkBiQuadraticQuad",
  "vtkTriQuadraticHexahedron", "vtkQuadraticLinearQuad", "vtkQuadraticLinearWedge",
  "vtkBiQuadraticQuadraticWedge", "vtkBiQuadraticQuadraticHexahedron", "vtkBiQuadraticTriangle",
  "vtkCubicLine", "vtkQuadraticPolygon", "UnknownClass", "UnknownClass", "UnknownClass",
  "UnknownClass", "vtkConvexPointSet", "vtkPolyhedron", "UnknownClass", "UnknownClass",
  "UnknownClass", "UnknownClass", "UnknownClass", "UnknownClass", "UnknownClass", "UnknownClass",
  "vtkParametricCurve", "vtkParametricSurface", "vtkParametricTriSurface",
  "vtkParametricQuadSurface", "vtkParametricTetraRegion", "vtkParametricHexRegion", "UnknownClass",
  "UnknownClass", "UnknownClass", "vtkHigherOrderEdge", "vtkHigherOrderTriangle",
  "vtkHigherOrderQuad", "vtkHigherOrderPolygon", "vtkHigherOrderTetrahedron", "vtkHigherOrderWedge",
  "vtkHigherOrderPyramid", "vtkHigherOrderHexahedron", "vtkLagrangeCurve",
  "vtkLagrangeQuadrilateral", "vtkLagrangeTriangle", "vtkLagrangeTetra", "vtkLagrangeHexahedron",
  "vtkLagrangeWedge", "vtkLagrangePyramid", "vtkBezierCurve", "vtkBezierQuadrilateral",
  "vtkBezierTriangle", "vtkBezierTetra", "vtkBezierHexahedron", "vtkBezierWedge",
  "vtkBezierPyramid", nullptr };

//----------------------------------------------------------------------------
const char* vtkCellTypes::GetClassNameFromTypeId(int type)
{
  static int numClasses = 0;

  // find length of table
  if (numClasses == 0)
  {
    while (vtkCellTypesStrings[numClasses] != nullptr)
    {
      numClasses++;
    }
  }

  if (type < numClasses)
  {
    return vtkCellTypesStrings[type];
  }
  else
  {
    return "UnknownClass";
  }
}

//----------------------------------------------------------------------------
int vtkCellTypes::GetTypeIdFromClassName(const char* classname)
{
  if (!classname)
  {
    return -1;
  }

  for (int idx = 0; vtkCellTypesStrings[idx] != nullptr; idx++)
  {
    if (strcmp(vtkCellTypesStrings[idx], classname) == 0)
    {
      return idx;
    }
  }

  return -1;
}

//----------------------------------------------------------------------------
vtkCellTypes::vtkCellTypes()
  : TypeArray(vtkUnsignedCharArray::New())
  , LocationArray(vtkIdTypeArray::New())
  , Size(0)
  , MaxId(-1)
  , Extend(1000)
{
  this->TypeArray->Register(this);
  this->TypeArray->Delete();

  this->LocationArray->Register(this);
  this->LocationArray->Delete();
}

//----------------------------------------------------------------------------
vtkCellTypes::~vtkCellTypes()
{
  if (this->TypeArray)
  {
    this->TypeArray->UnRegister(this);
  }

  if (this->LocationArray)
  {
    this->LocationArray->UnRegister(this);
  }
}

//----------------------------------------------------------------------------
// Allocate memory for this array. Delete old storage only if necessary.
int vtkCellTypes::Allocate(vtkIdType sz, vtkIdType ext)
{

  this->Size = (sz > 0 ? sz : 1);
  this->Extend = (ext > 0 ? ext : 1);
  this->MaxId = -1;

  if (this->TypeArray)
  {
    this->TypeArray->UnRegister(this);
  }
  this->TypeArray = vtkUnsignedCharArray::New();
  this->TypeArray->Allocate(sz, ext);
  this->TypeArray->Register(this);
  this->TypeArray->Delete();

  if (this->LocationArray)
  {
    this->LocationArray->UnRegister(this);
  }
  this->LocationArray = vtkIdTypeArray::New();
  this->LocationArray->Allocate(sz, ext);
  this->LocationArray->Register(this);
  this->LocationArray->Delete();

  return 1;
}

//----------------------------------------------------------------------------
// Add a cell at specified id.
void vtkCellTypes::InsertCell(vtkIdType cellId, unsigned char type, vtkIdType loc)
{
  vtkDebugMacro(<< "Insert Cell id: " << cellId << " at location " << loc);
  TypeArray->InsertValue(cellId, type);

  LocationArray->InsertValue(cellId, loc);

  if (cellId > this->MaxId)
  {
    this->MaxId = cellId;
  }
}

//----------------------------------------------------------------------------
// Add a cell to the object in the next available slot.
vtkIdType vtkCellTypes::InsertNextCell(unsigned char type, vtkIdType loc)
{
  vtkDebugMacro(<< "Insert Next Cell " << type << " location " << loc);
  this->InsertCell(++this->MaxId, type, loc);
  return this->MaxId;
}

//----------------------------------------------------------------------------
// Specify a group of cell types.
void vtkCellTypes::SetCellTypes(
  vtkIdType ncells, vtkUnsignedCharArray* cellTypes, vtkIntArray* cellLocations)
{
  vtkIdTypeArray* cellLocations64 = vtkIdTypeArray::New();
  cellLocations64->SetName(cellLocations->GetName());
  cellLocations64->SetNumberOfComponents(cellLocations->GetNumberOfComponents());
  cellLocations64->SetNumberOfTuples(cellLocations->GetNumberOfTuples());
  for (vtkIdType i = 0, iend = cellLocations->GetNumberOfValues(); i < iend; ++i)
  {
    cellLocations64->SetValue(i, cellLocations->GetValue(i));
  }
  this->SetCellTypes(ncells, cellTypes, cellLocations64);
  cellLocations64->Delete();
}

//----------------------------------------------------------------------------
// Specify a group of cell types.
void vtkCellTypes::SetCellTypes(
  vtkIdType ncells, vtkUnsignedCharArray* cellTypes, vtkIdTypeArray* cellLocations)
{
  this->Size = ncells;

  if (this->TypeArray)
  {
    this->TypeArray->Delete();
  }

  this->TypeArray = cellTypes;
  cellTypes->Register(this);

  if (this->LocationArray)
  {
    this->LocationArray->Delete();
  }
  this->LocationArray = cellLocations;
  cellLocations->Register(this);

  this->MaxId = ncells - 1;
}

//----------------------------------------------------------------------------
// Reclaim any extra memory.
void vtkCellTypes::Squeeze()
{
  this->TypeArray->Squeeze();
  this->LocationArray->Squeeze();
}

//----------------------------------------------------------------------------
// Initialize object without releasing memory.
void vtkCellTypes::Reset()
{
  this->MaxId = -1;
}

//----------------------------------------------------------------------------
unsigned long vtkCellTypes::GetActualMemorySize()
{
  unsigned long size = 0;

  if (this->TypeArray)
  {
    size += this->TypeArray->GetActualMemorySize();
  }

  if (this->LocationArray)
  {
    size += this->LocationArray->GetActualMemorySize();
  }

  return static_cast<unsigned long>(ceil(size / 1024.0)); // kibibytes
}

//----------------------------------------------------------------------------
void vtkCellTypes::DeepCopy(vtkCellTypes* src)
{
  if (this->TypeArray)
  {
    this->TypeArray->UnRegister(this);
    this->TypeArray = nullptr;
  }
  if (src->TypeArray)
  {
    this->TypeArray = vtkUnsignedCharArray::New();
    this->TypeArray->DeepCopy(src->TypeArray);
    this->TypeArray->Register(this);
    this->TypeArray->Delete();
  }

  if (this->LocationArray)
  {
    this->LocationArray->UnRegister(this);
    this->LocationArray = nullptr;
  }
  if (src->LocationArray)
  {
    this->LocationArray = vtkIdTypeArray::New();
    this->LocationArray->DeepCopy(src->LocationArray);
    this->LocationArray->Register(this);
    this->LocationArray->Delete();
  }
  this->Size = src->Size;
  this->Extend = src->Extend;
  this->MaxId = src->MaxId;
}

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

  os << indent << "TypeArray:\n";
  this->TypeArray->PrintSelf(os, indent.GetNextIndent());
  os << indent << "LocationArray:\n";
  this->LocationArray->PrintSelf(os, indent.GetNextIndent());

  os << indent << "Size: " << this->Size << "\n";
  os << indent << "MaxId: " << this->MaxId << "\n";
  os << indent << "Extend: " << this->Extend << "\n";
}
