// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause

#include "vtkGenericAttributeCollection.h"

#include "vtkGenericAttribute.h"
#include "vtkObjectFactory.h"

#include <cassert>
#include <vector>

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkGenericAttributeCollection);

class vtkGenericAttributeInternalVector
{
public:
  typedef std::vector<vtkGenericAttribute*> VectorType;
  VectorType Vector;
};

class vtkIntInternalVector
{
public:
  typedef std::vector<int> VectorType;
  VectorType Vector;
};

//------------------------------------------------------------------------------
// Description:
// Default constructor: empty collection
vtkGenericAttributeCollection::vtkGenericAttributeCollection()
{
  this->AttributeInternalVector = new vtkGenericAttributeInternalVector;
  this->AttributeIndices = new vtkIntInternalVector;
  this->ActiveAttribute = 0;
  this->ActiveComponent = 0;
  this->NumberOfAttributesToInterpolate = 0;
  this->NumberOfComponents = 0;
  this->NumberOfPointCenteredComponents = 0;
  this->MaxNumberOfComponents = 0; // cache
  this->ActualMemorySize = 0;
}

//------------------------------------------------------------------------------
vtkGenericAttributeCollection::~vtkGenericAttributeCollection()
{
  for (unsigned int i = 0; i < this->AttributeInternalVector->Vector.size(); ++i)
  {
    this->AttributeInternalVector->Vector[i]->Delete();
  }
  delete this->AttributeInternalVector;
  delete this->AttributeIndices;
}

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

  int i;
  int c = this->GetNumberOfAttributes();

  os << indent << "Number Of Attributes: " << this->GetNumberOfAttributes() << "\n";
  for (i = 0; i < c; ++i)
  {
    os << indent << "Attribute #" << i << ":\n";
    this->GetAttribute(i)->PrintSelf(os, indent.GetNextIndent());
  }

  c = this->GetNumberOfAttributesToInterpolate();
  os << indent << "Number Of Attributes to interpolate: " << c << endl;

  os << indent << "Attributes to interpolate:";

  for (i = 0; i < c; ++i)
  {
    os << ' ' << this->AttributesToInterpolate[i];
  }
  os << endl;

  os << indent << "Active Attribute: " << this->ActiveAttribute << endl;

  os << indent << "Active Component" << this->ActiveComponent << endl;
}

//------------------------------------------------------------------------------
// Description:
// Number of attributes.
int vtkGenericAttributeCollection::GetNumberOfAttributes()
{
  int result = static_cast<int>(this->AttributeInternalVector->Vector.size());
  // assert("post: positive_result" && result>=0); size() is an unsigned type according to STL
  // vector definition
  return result;
}

//------------------------------------------------------------------------------
int vtkGenericAttributeCollection::GetNumberOfComponents()
{
  this->ComputeNumbers();

  return this->NumberOfComponents;
}

//------------------------------------------------------------------------------
// Description:
// Return the number of components. This is the sum of all components
// found in all point centered attributes.
// \post positive_result: result>=0
int vtkGenericAttributeCollection::GetNumberOfPointCenteredComponents()
{
  this->ComputeNumbers();

  return this->NumberOfPointCenteredComponents;
}

//------------------------------------------------------------------------------
// Description:
// Maximum number of components encountered among all attributes.
// \post positive_result: result>=0
// \post valid_result: result<=GetNumberOfComponents()
int vtkGenericAttributeCollection::GetMaxNumberOfComponents()
{
  this->ComputeNumbers();

  assert("post: positive_result" && this->MaxNumberOfComponents >= 0);
  assert("post: valid_result" && this->MaxNumberOfComponents <= GetNumberOfComponents());
  return this->MaxNumberOfComponents;
}

//------------------------------------------------------------------------------
// Description:
// Actual size of the data in kibibytes (1024 bytes); only valid after the pipeline has
// updated. It is guaranteed to be greater than or equal to the memory
// required to represent the data.
unsigned long vtkGenericAttributeCollection::GetActualMemorySize()
{
  this->ComputeNumbers();
  return this->ActualMemorySize;
}

//------------------------------------------------------------------------------
// Description:
// Does `this' have no attribute?
int vtkGenericAttributeCollection::IsEmpty()
{
  return this->GetNumberOfAttributes() == 0;
}

//------------------------------------------------------------------------------
// Description:
// Attribute `i'.
vtkGenericAttribute* vtkGenericAttributeCollection::GetAttribute(int i)
{
  assert("pre: not_empty" && !IsEmpty());
  assert("pre: valid_i" && (i >= 0) && (i < this->GetNumberOfAttributes()));
  vtkGenericAttribute* result = this->AttributeInternalVector->Vector[i];
  assert("post: result_exists" && result != nullptr);
  return result;
}

//------------------------------------------------------------------------------
// Description:
// Return the index of attribute `name', if found. Return -1 otherwise.
int vtkGenericAttributeCollection::FindAttribute(const char* name)
{
  assert("pre: name_exists:" && name != nullptr);

  int result = -1;

  const char* attributeName;
  int numAtt = this->GetNumberOfAttributes();
  int i = 0;

  while ((i < numAtt) && (result == -1))
  {
    attributeName = this->GetAttribute(i)->GetName();
    if (attributeName != nullptr)
    {
      if (strcmp(attributeName, name) == 0)
      {
        result = i;
      }
    }
    ++i;
  }

  assert("post: valid_result" &&
    ((result == -1) || ((result >= 0) && (result <= this->GetNumberOfAttributes()))));

  return result;
}
//------------------------------------------------------------------------------
// Description:
// Return the index of the first component of attribute `i' in an array of
// format attrib0comp0 attrib0comp1 ... attrib4comp0 ...
// \pre valid_i: i>=0 && i<GetNumberOfAttributes()
// \pre is_point_centered: GetAttribute(i)->GetCentering()==vtkPointCentered
int vtkGenericAttributeCollection::GetAttributeIndex(int i)
{
  assert("pre: valid_i" && i >= 0 && i < this->GetNumberOfAttributes());
  assert("pre: is_point_centered" && this->GetAttribute(i)->GetCentering() == vtkPointCentered);
  this->ComputeNumbers();
  return this->AttributeIndices->Vector[i];
}

//------------------------------------------------------------------------------
// Description:
// Add attribute `a' at the end.
void vtkGenericAttributeCollection::InsertNextAttribute(vtkGenericAttribute* a)
{
  assert("pre: a_exists" && a != nullptr);
#ifndef NDEBUG
  int oldnumber = this->GetNumberOfAttributes();
#endif

  this->AttributeInternalVector->Vector.push_back(a);
  this->AttributeIndices->Vector.push_back(0); // a dummy default value
  a->Register(this);
  this->Modified();

  assert("post: more_items" && this->GetNumberOfAttributes() == oldnumber + 1);
  assert("post: a_is_set" && this->GetAttribute(this->GetNumberOfAttributes() - 1) == a);
}

//------------------------------------------------------------------------------
// Description:
// Replace attribute at index `i' by `a'.
void vtkGenericAttributeCollection::InsertAttribute(int i, vtkGenericAttribute* a)
{
  assert("pre: not_empty" && !this->IsEmpty());
  assert("pre: a_exists" && a != nullptr);
  assert("pre: valid_i" && (i >= 0) && (i < this->GetNumberOfAttributes()));

#ifndef NDEBUG
  int oldnumber = this->GetNumberOfAttributes();
#endif

  if (this->AttributeInternalVector->Vector[i] != nullptr)
  {
    this->AttributeInternalVector->Vector[i]->Delete();
  }
  this->AttributeInternalVector->Vector[i] = a;
  a->Register(this);
  this->Modified();

  assert("post: more_items" && this->GetNumberOfAttributes() == oldnumber);
  assert("post: a_is_set" && this->GetAttribute(i) == a);
}

//------------------------------------------------------------------------------
// Description:
// Remove Attribute at `i'.
void vtkGenericAttributeCollection::RemoveAttribute(int i)
{
  assert("pre: not_empty" && !this->IsEmpty());
  assert("pre: valid_i" && (i >= 0) && (i < this->GetNumberOfAttributes()));

#ifndef NDEBUG
  int oldnumber = this->GetNumberOfAttributes();
#endif

  this->AttributeInternalVector->Vector[i]->UnRegister(this);
  this->AttributeInternalVector->Vector.erase(this->AttributeInternalVector->Vector.begin() + i);

  this->AttributeIndices->Vector.erase(this->AttributeIndices->Vector.begin() + i);

  this->Modified();

  assert("post: fewer_items" && this->GetNumberOfAttributes() == (oldnumber - 1));
}

//------------------------------------------------------------------------------
// Description:
// Remove all attributes.
void vtkGenericAttributeCollection::Reset()
{
  for (unsigned int i = 0; i < this->AttributeInternalVector->Vector.size(); ++i)
  {
    this->AttributeInternalVector->Vector[i]->Delete();
  }
  this->AttributeInternalVector->Vector.clear();
  this->AttributeIndices->Vector.clear();
  this->Modified();

  assert("post: is_empty" && this->IsEmpty());
}

//------------------------------------------------------------------------------
// Description:
// Recursive duplication of `other' in `this'.
void vtkGenericAttributeCollection::DeepCopy(vtkGenericAttributeCollection* other)
{
  assert("pre: other_exists" && other != nullptr);
  assert("pre: not_self" && other != this);

  this->AttributeInternalVector->Vector.resize(other->AttributeInternalVector->Vector.size());

  this->AttributeIndices->Vector.resize(other->AttributeIndices->Vector.size());

  int c = static_cast<int>(this->AttributeInternalVector->Vector.size());
  for (int i = 0; i < c; i++)
  {
    if (this->AttributeInternalVector->Vector[i] == nullptr)
    {
      this->AttributeInternalVector->Vector[i] =
        other->AttributeInternalVector->Vector[i]->NewInstance();
    }
    this->AttributeInternalVector->Vector[i]->DeepCopy(other->AttributeInternalVector->Vector[i]);
    // Don't need to copy the contents of AttributeIndices: it will be
    // recomputed because of the following Moditied call.
  }
  this->Modified();

  assert("post: same_size" && this->GetNumberOfAttributes() == other->GetNumberOfAttributes());
}

//------------------------------------------------------------------------------
// Description:
// Update `this' using fields of `other'.
void vtkGenericAttributeCollection::ShallowCopy(vtkGenericAttributeCollection* other)
{
  assert("pre: other_exists" && other != nullptr);
  assert("pre: not_self" && other != this);

  this->AttributeInternalVector->Vector = other->AttributeInternalVector->Vector;
  this->AttributeIndices->Vector = other->AttributeIndices->Vector;
  int c = static_cast<int>(this->AttributeInternalVector->Vector.size());
  for (int i = 0; i < c; ++i)
  {
    if (this->AttributeInternalVector->Vector[i] != nullptr)
    {
      this->AttributeInternalVector->Vector[i]->Register(this);
    }
  }
  this->Modified();

  assert("post: same_size" && this->GetNumberOfAttributes() == other->GetNumberOfAttributes());
}

//------------------------------------------------------------------------------
// Description:
// Collection is composite object and need to check each part for MTime.
vtkMTimeType vtkGenericAttributeCollection::GetMTime()
{
  vtkMTimeType result;
  vtkMTimeType mtime;

  result = vtkObject::GetMTime();

  for (int i = 0; i < this->GetNumberOfAttributes(); ++i)
  {
    mtime = this->GetAttribute(i)->GetMTime();
    result = (mtime > result ? mtime : result);
  }

  return result;
}

//------------------------------------------------------------------------------
// Description:
// Compute number of components, max number of components and actual memory
// size.
void vtkGenericAttributeCollection::ComputeNumbers()
{
  if (this->GetMTime() > this->ComputeTime)
  {
    int nb = 0;
    int pnb = 0;
    int count;
    int maxNb = 0;
    unsigned long memory = 0;
    int firstComponentIndex = 0;

    int c = this->GetNumberOfAttributes();

    for (int i = 0; i < c; ++i)
    {
      count = this->GetAttribute(i)->GetNumberOfComponents();
      memory = memory + this->GetAttribute(i)->GetActualMemorySize();
      maxNb = std::max(count, maxNb);
      nb += count;
      if (this->GetAttribute(i)->GetCentering() == vtkPointCentered)
      {
        pnb += count;
        this->AttributeIndices->Vector[i] = firstComponentIndex;
        firstComponentIndex = firstComponentIndex + count;
      }
    }
    this->NumberOfComponents = nb;
    this->NumberOfPointCenteredComponents = pnb;
    this->MaxNumberOfComponents = maxNb;
    this->ActualMemorySize = memory;

    assert("check: positive_number" && this->NumberOfComponents >= 0);
    assert("check: positive_point_centered_number" && this->NumberOfPointCenteredComponents >= 0);
    assert("check: positiveMaxNumber" && this->MaxNumberOfComponents >= 0);
    assert("check: valid_number" && this->MaxNumberOfComponents <= this->NumberOfComponents);
    this->ComputeTime.Modified();
  }
}

//------------------------------------------------------------------------------
// *** ALL THE FOLLOWING METHODS SHOULD BE REMOVED WHEN vtkInformation
// will be ready.
// *** BEGIN

// Description:
// Set the scalar attribute to be processed. -1 means module.
void vtkGenericAttributeCollection::SetActiveAttribute(int attribute, int component)
{
  assert("pre: not_empty" && !IsEmpty());
  assert("pre: valid_attribute" && (attribute >= 0) && (attribute < this->GetNumberOfAttributes()));
  assert("pre: valid_component" && (component >= -1) &&
    (component < this->GetAttribute(attribute)->GetNumberOfComponents()));

  this->ActiveAttribute = attribute;
  this->ActiveComponent = component;

  assert("post: is_set" && (this->GetActiveAttribute() == attribute) &&
    (this->GetActiveComponent() == component));
}

//------------------------------------------------------------------------------
// Description:
// Indices of attributes to interpolate.
// \pre not_empty: !IsEmpty()
// \post valid_result: GetNumberOfAttributesToInterpolate()>0
int* vtkGenericAttributeCollection::GetAttributesToInterpolate()
{
  assert("pre: not_empty" && !IsEmpty());

  assert("post: valid_result" && !(this->NumberOfAttributesToInterpolate > 0));

  return this->AttributesToInterpolate;
}

//------------------------------------------------------------------------------
// Description
// Does the array `attributes' of size `size' have `attribute'?
vtkTypeBool vtkGenericAttributeCollection::HasAttribute(int size, int* attributes, int attribute)
{
  assert("pre: positive_size" && size >= 0);
  assert("pre: valid_attributes" &&
    ((!(size > 0)) || (attributes != nullptr))); // size>0 => attributes!=0 (A=>B: !A||B )

  int result = 0; // false
  int i;

  if (size != 0)
  {
    i = 0;
    while (!result && i++ < size)
    {
      result = attributes[i] == attribute;
    }
  }
  return result;
}

//------------------------------------------------------------------------------
// Description:
// Set the attributes to interpolate.
void vtkGenericAttributeCollection::SetAttributesToInterpolate(int size, int* attributes)
{
  assert("pre: not_empty" && !this->IsEmpty());
  assert("pre: positive_size" && size >= 0);
  assert("pre: magic_number" && size <= 10);
  assert("pre: valid_attributes" &&
    ((!(size > 0)) || (attributes != nullptr))); // size>0 => attributes!=0 (A=>B: !A||B )
  assert("pre: valid_attributes_contents" &&
    (!(attributes != nullptr) ||
      !(!this->HasAttribute(size, attributes,
        this
          ->GetActiveAttribute())))); // attributes!=0 =>
                                      // !this->HasAttribute(size,attributes,this->GetActiveAttribute())
                                      // (A=>B: !A||B )

  this->NumberOfAttributesToInterpolate = size;
  for (int i = 0; i < size; ++i)
  {
    this->AttributesToInterpolate[i] = attributes[i];
  }

  assert("post: is_set" && (this->GetNumberOfAttributesToInterpolate() == size));
}

//------------------------------------------------------------------------------
// Description:
// Set the attributes to interpolate.
void vtkGenericAttributeCollection::SetAttributesToInterpolateToAll()
{
  assert("pre: not_empty" && !this->IsEmpty());

  this->NumberOfAttributesToInterpolate = this->GetMaxNumberOfComponents();
  for (int i = 0; i < this->NumberOfAttributesToInterpolate; ++i)
  {
    this->AttributesToInterpolate[i] = i;
  }
}

//------------------------------------------------------------------------------
// *** ALL THE PREVIOUS METHODS SHOULD BE REMOVED WHEN vtkInformation
// will be ready.
// *** END
VTK_ABI_NAMESPACE_END
