// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   vtkAOSDataArrayTemplate
 * @brief   Array-Of-Structs implementation of
 * vtkGenericDataArray.
 *
 *
 * vtkGenericDataArray specialization that stores data array in the traditional
 * VTK memory layout where a 3 component is stored in contiguous memory as
 * \c A1A2A3B1B2B3C1C2C3 ... where A,B,C,... are tuples.
 *
 * This replaces vtkDataArrayTemplate.
 *
 * @sa
 * vtkGenericDataArray vtkSOADataArrayTemplate
 */

#ifndef vtkAOSDataArrayTemplate_h
#define vtkAOSDataArrayTemplate_h

#include "vtkBuffer.h"           // For storage buffer.
#include "vtkBuild.h"            // For VTK_BUILD_SHARED_LIBS
#include "vtkCommonCoreModule.h" // For export macro
#include "vtkCompiler.h"         // for VTK_USE_EXTERN_TEMPLATE
#include "vtkDataArrayMeta.h"    // For vtkDataArrayMeta::ComponentType
#include "vtkGenericDataArray.h"

namespace vtk
{
namespace detail
{
VTK_ABI_NAMESPACE_BEGIN
template <typename ArrayType, ComponentIdType TupleSize>
struct TupleRange;
template <typename ArrayType, ComponentIdType TupleSize,
  typename ForceValueTypeForVtkDataArray = double>
struct ValueRange;
VTK_ABI_NAMESPACE_END
} // namespace detail
} // namespace vtk

// The export macro below makes no sense, but is necessary for older compilers
// when we export instantiations of this class from vtkCommonCore.
VTK_ABI_NAMESPACE_BEGIN
template <class ValueTypeT>
class VTKCOMMONCORE_EXPORT vtkAOSDataArrayTemplate
  : public vtkGenericDataArray<vtkAOSDataArrayTemplate<ValueTypeT>, ValueTypeT>
{
  typedef vtkGenericDataArray<vtkAOSDataArrayTemplate<ValueTypeT>, ValueTypeT> GenericDataArrayType;

  // Friendship required by vtkDataArray(Value/Tuple)Range so that it can access the memory buffer
  // which is required to avoid accessing raw pointers that might no longer be valid.
  template <typename ArrayType, vtk::ComponentIdType TupleSize>
  friend struct vtk::detail::TupleRange;
  template <typename ArrayType, vtk::ComponentIdType TupleSize,
    typename ForceValueTypeForVtkDataArray>
  friend struct vtk::detail::ValueRange;

public:
  typedef vtkAOSDataArrayTemplate<ValueTypeT> SelfType;
  vtkTemplateTypeMacro(SelfType, GenericDataArrayType);
  typedef typename Superclass::ValueType ValueType;

  enum DeleteMethod
  {
    VTK_DATA_ARRAY_FREE = vtkAbstractArray::VTK_DATA_ARRAY_FREE,
    VTK_DATA_ARRAY_DELETE = vtkAbstractArray::VTK_DATA_ARRAY_DELETE,
    VTK_DATA_ARRAY_ALIGNED_FREE = vtkAbstractArray::VTK_DATA_ARRAY_ALIGNED_FREE,
    VTK_DATA_ARRAY_USER_DEFINED = vtkAbstractArray::VTK_DATA_ARRAY_USER_DEFINED
  };

  static vtkAOSDataArrayTemplate* New();

  /**
   * Get the value at @a valueIdx. @a valueIdx assumes AOS ordering.
   */
  ValueType GetValue(vtkIdType valueIdx) const
    VTK_EXPECTS(0 <= valueIdx && valueIdx < GetNumberOfValues())
  {
    return this->Buffer->GetBuffer()[valueIdx];
  }

  /**
   * Set the value at @a valueIdx to @a value. @a valueIdx assumes AOS ordering.
   */
  void SetValue(vtkIdType valueIdx, ValueType value)
    VTK_EXPECTS(0 <= valueIdx && valueIdx < GetNumberOfValues())
  {
    this->Buffer->GetBuffer()[valueIdx] = value;
  }

  ///@{
  /**
   * Copy the tuple at @a tupleIdx into @a tuple.
   */
  void GetTypedTuple(vtkIdType tupleIdx, ValueType* tuple) const
    VTK_EXPECTS(0 <= tupleIdx && tupleIdx < GetNumberOfTuples())
  {
    const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents;
    std::copy(this->Buffer->GetBuffer() + valueIdx,
      this->Buffer->GetBuffer() + valueIdx + this->NumberOfComponents, tuple);
  }
  ///@}

  ///@{
  /**
   * Set this array's tuple at @a tupleIdx to the values in @a tuple.
   */
  void SetTypedTuple(vtkIdType tupleIdx, const ValueType* tuple)
    VTK_EXPECTS(0 <= tupleIdx && tupleIdx < GetNumberOfTuples())
  {
    const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents;
    std::copy(tuple, tuple + this->NumberOfComponents, this->Buffer->GetBuffer() + valueIdx);
  }
  ///@}

  void GetIntegerTuple(vtkIdType tupleIdx, vtkTypeInt64* tuple)
    VTK_EXPECTS(0 <= tupleIdx && tupleIdx < GetNumberOfTuples()) override
  {
    const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents;
    for (vtkIdType ii = 0; ii < this->NumberOfComponents; ++ii)
    {
      tuple[ii] = static_cast<vtkTypeInt64>((this->Buffer->GetBuffer())[valueIdx + ii]);
    }
  }

  void SetIntegerTuple(vtkIdType tupleIdx, vtkTypeInt64* tuple)
    VTK_EXPECTS(0 <= tupleIdx && tupleIdx < GetNumberOfTuples()) override
  {
    const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents;
    for (vtkIdType ii = 0; ii < this->NumberOfComponents; ++ii)
    {
      this->Buffer->GetBuffer()[valueIdx + ii] = static_cast<ValueType>(tuple[ii]);
    }
  }

  void GetUnsignedTuple(vtkIdType tupleIdx, vtkTypeUInt64* tuple)
    VTK_EXPECTS(0 <= tupleIdx && tupleIdx < GetNumberOfTuples()) override
  {
    const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents;
    for (vtkIdType ii = 0; ii < this->NumberOfComponents; ++ii)
    {
      tuple[ii] = static_cast<vtkTypeUInt64>((this->Buffer->GetBuffer())[valueIdx + ii]);
    }
  }

  void SetUnsignedTuple(vtkIdType tupleIdx, vtkTypeUInt64* tuple)
    VTK_EXPECTS(0 <= tupleIdx && tupleIdx < GetNumberOfTuples()) override
  {
    const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents;
    for (vtkIdType ii = 0; ii < this->NumberOfComponents; ++ii)
    {
      this->Buffer->GetBuffer()[valueIdx + ii] = static_cast<ValueType>(tuple[ii]);
    }
  }

  /**
   * Get component @a comp of the tuple at @a tupleIdx.
   */
  ValueType GetTypedComponent(vtkIdType tupleIdx, int comp) const
    VTK_EXPECTS(0 <= tupleIdx && GetNumberOfComponents() * tupleIdx + comp < GetNumberOfValues())
      VTK_EXPECTS(0 <= comp && comp < GetNumberOfComponents())
  {
    return this->Buffer->GetBuffer()[this->NumberOfComponents * tupleIdx + comp];
  }

  ///@{
  /**
   * Set component @a comp of the tuple at @a tupleIdx to @a value.
   */
  void SetTypedComponent(vtkIdType tupleIdx, int comp, ValueType value)
    VTK_EXPECTS(0 <= tupleIdx && GetNumberOfComponents() * tupleIdx + comp < GetNumberOfValues())
      VTK_EXPECTS(0 <= comp && comp < GetNumberOfComponents())
  {
    const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents + comp;
    this->SetValue(valueIdx, value);
  }
  ///@}

  ///@{
  /**
   * Set component @a comp of all tuples to @a value.
   */
  void FillTypedComponent(int compIdx, ValueType value) override;
  ///@}

  ///@{
  /**
   * Set all the values in array to @a value.
   */
  void FillValue(ValueType value) override;
  void Fill(double value) override;
  ///@}

  ///@{
  /**
   * Get the address of a particular data index. Make sure data is allocated
   * for the number of items requested. Set MaxId according to the number of
   * data values requested.
   */
  ValueType* WritePointer(vtkIdType valueIdx, vtkIdType numValues);
  void* WriteVoidPointer(vtkIdType valueIdx, vtkIdType numValues) override;
  ///@}

  ///@{
  /**
   * Get the address of a particular data index. Performs no checks
   * to verify that the memory has been allocated etc.
   * Use of this method is discouraged, as newer arrays require a deep-copy of
   * the array data in order to return a suitable pointer. See vtkArrayDispatch
   * for a safer alternative for fast data access.
   */
  ValueType* GetPointer(vtkIdType valueIdx);
  void* GetVoidPointer(vtkIdType valueIdx) override;
  ///@}

  ///@{
  /**
   * This method lets the user specify data to be held by the array.  The
   * array argument is a pointer to the data.  size is the size of the
   * array supplied by the user (as number of values, not in bytes).
   * Set save to 1 to prevent the class from
   * deleting the array when it cleans up or reallocates memory.  The class
   * uses the actual array provided; it does not copy the data from the
   * suppled array. If specified, the delete method determines how the data
   * array will be deallocated. If the delete method is
   * VTK_DATA_ARRAY_FREE, free() will be used. If the delete method is
   * VTK_DATA_ARRAY_DELETE, delete[] will be used. If the delete method is
   * VTK_DATA_ARRAY_ALIGNED_FREE _aligned_free() will be used on Windows, while
   * free() will be used everywhere else. The default is FREE.
   */
  void SetArray(VTK_ZEROCOPY ValueType* array, vtkIdType size, int save, int deleteMethod);
  void SetArray(VTK_ZEROCOPY ValueType* array, vtkIdType size, int save);
  void SetVoidArray(void* array, vtkIdType size, int save) override;
  void SetVoidArray(void* array, vtkIdType size, int save, int deleteMethod) override;
  ///@}

  /**
   * This method allows the user to specify a custom free function to be
   * called when the array is deallocated. Calling this method will implicitly
   * mean that the given free function will be called when the class
   * cleans up or reallocates memory.
   **/
  void SetArrayFreeFunction(void (*callback)(void*)) override;

  // Overridden for optimized implementations:
  void SetTuple(vtkIdType tupleIdx, const float* tuple) override;
  void SetTuple(vtkIdType tupleIdx, const double* tuple) override;
  // MSVC doesn't like 'using' here (error C2487). Just forward instead:
  // using Superclass::SetTuple;
  void SetTuple(vtkIdType dstTupleIdx, vtkIdType srcTupleIdx, vtkAbstractArray* source) override
  {
    this->Superclass::SetTuple(dstTupleIdx, srcTupleIdx, source);
  }
  void InsertTuple(vtkIdType tupleIdx, const float* source) override;
  void InsertTuple(vtkIdType tupleIdx, const double* source) override;
  // MSVC doesn't like 'using' here (error C2487). Just forward instead:
  // using Superclass::InsertTuple;
  void InsertTuple(vtkIdType dstTupleIdx, vtkIdType srcTupleIdx, vtkAbstractArray* source) override
  {
    this->Superclass::InsertTuple(dstTupleIdx, srcTupleIdx, source);
  }
  void InsertComponent(vtkIdType tupleIdx, int compIdx, double value) override;
  vtkIdType InsertNextTuple(const float* tuple) override;
  vtkIdType InsertNextTuple(const double* tuple) override;
  // MSVC doesn't like 'using' here (error C2487). Just forward instead:
  // using Superclass::InsertNextTuple;
  vtkIdType InsertNextTuple(vtkIdType srcTupleIdx, vtkAbstractArray* source) override
  {
    return this->Superclass::InsertNextTuple(srcTupleIdx, source);
  }
  void GetTuple(vtkIdType tupleIdx, double* tuple) override;
  double* GetTuple(vtkIdType tupleIdx) override;

  /**
   * Tell the array explicitly that a single data element has
   * changed. Like DataChanged(), then is only necessary when you
   * modify the array contents without using the array's API.
   * @note This is a legacy method from vtkDataArrayTemplate, and is only
   * implemented for array-of-struct arrays. It currently just calls
   * DataChanged() and does nothing clever.
   * TODO this is only defined for AOS (vtkDataArrayTemplate leftover).
   * Deprecate to favor DataChanged?
   */
  void DataElementChanged(vtkIdType) { this->DataChanged(); }

  /**
   * Legacy support for array-of-structs value iteration.
   * TODO Deprecate?
   */
  typedef ValueType* Iterator;
  Iterator Begin() { return Iterator(this->GetVoidPointer(0)); }
  Iterator End() { return Iterator(this->GetVoidPointer(this->MaxId + 1)); }

  ///@{
  /**
   * Perform a fast, safe cast from a vtkAbstractArray to a
   * vtkAOSDataArrayTemplate.
   * This method checks if source->GetArrayType() returns AOSDataArrayTemplate
   * or a more derived type, checks the data types, and performs a static_cast
   * to return source as a vtkDataArray pointer. Otherwise, nullptr is returned.
   */
  static vtkAOSDataArrayTemplate<ValueType>* FastDownCast(vtkAbstractArray* source);
  ///@}

  int GetArrayType() const override { return vtkAbstractArray::AoSDataArrayTemplate; }
  VTK_NEWINSTANCE vtkArrayIterator* NewIterator() override;
  bool HasStandardMemoryLayout() const override { return true; }
  void ShallowCopy(vtkDataArray* other) override;

  // Reimplemented for efficiency:
  void InsertTuples(
    vtkIdType dstStart, vtkIdType n, vtkIdType srcStart, vtkAbstractArray* source) override;
  // MSVC doesn't like 'using' here (error C2487). Just forward instead:
  // using Superclass::InsertTuples;
  void InsertTuples(vtkIdList* dstIds, vtkIdList* srcIds, vtkAbstractArray* source) override
  {
    this->Superclass::InsertTuples(dstIds, srcIds, source);
  }
  void InsertTuplesStartingAt(
    vtkIdType dstStart, vtkIdList* srcIds, vtkAbstractArray* source) override
  {
    this->Superclass::InsertTuplesStartingAt(dstStart, srcIds, source);
  }

protected:
  vtkAOSDataArrayTemplate();
  ~vtkAOSDataArrayTemplate() override;

  /**
   * Allocate space for numTuples. Old data is not preserved. If numTuples == 0,
   * all data is freed.
   */
  bool AllocateTuples(vtkIdType numTuples);

  /**
   * Allocate space for numTuples. Old data is preserved. If numTuples == 0,
   * all data is freed.
   */
  bool ReallocateTuples(vtkIdType numTuples);

  vtkBuffer<ValueType>* Buffer;

private:
  vtkAOSDataArrayTemplate(const vtkAOSDataArrayTemplate&) = delete;
  void operator=(const vtkAOSDataArrayTemplate&) = delete;

  friend class vtkGenericDataArray<vtkAOSDataArrayTemplate<ValueTypeT>, ValueTypeT>;
};

// Declare vtkArrayDownCast implementations for AoS containers:
vtkArrayDownCast_TemplateFastCastMacro(vtkAOSDataArrayTemplate);

VTK_ABI_NAMESPACE_END

// This macro is used by the subclasses to create dummy
// declarations for these functions such that the wrapper
// can see them. The wrappers ignore vtkAOSDataArrayTemplate.
#define vtkCreateWrappedArrayInterface(T)                                                          \
  vtkCreateReadOnlyWrappedArrayInterface(T) void SetTypedTuple(vtkIdType i, const T* tuple)        \
    VTK_EXPECTS(0 <= i && i < GetNumberOfTuples());                                                \
  void InsertTypedTuple(vtkIdType i, const T* tuple) VTK_EXPECTS(0 <= i);                          \
  vtkIdType InsertNextTypedTuple(const T* tuple);                                                  \
  void SetValue(vtkIdType id, T value) VTK_EXPECTS(0 <= id && id < GetNumberOfValues());           \
  bool SetNumberOfValues(vtkIdType number) override;                                               \
  void InsertValue(vtkIdType id, T f) VTK_EXPECTS(0 <= id);                                        \
  vtkIdType InsertNextValue(T f);                                                                  \
  T* WritePointer(vtkIdType id, vtkIdType number);                                                 \
  T* GetPointer(vtkIdType id);                                                                     \
  void SetArray(VTK_ZEROCOPY T* array, vtkIdType size, int save);                                  \
  void SetArray(VTK_ZEROCOPY T* array, vtkIdType size, int save, int deleteMethod)

#define vtkCreateReadOnlyWrappedArrayInterface(T)                                                  \
  int GetDataType() const override;                                                                \
  void GetTypedTuple(vtkIdType i, T* tuple) VTK_EXPECTS(0 <= i && i < GetNumberOfTuples());        \
  T GetValue(vtkIdType id) const VTK_EXPECTS(0 <= id && id < GetNumberOfValues());                 \
  T* GetValueRange(int comp) VTK_SIZEHINT(2);                                                      \
  T* GetValueRange() VTK_SIZEHINT(2);

#endif // header guard

// This portion must be OUTSIDE the include blockers. This is used to tell
// libraries other than vtkCommonCore that instantiations of
// vtkAOSDataArrayTemplate can be found externally. This prevents each library
// from instantiating these on their own.
#ifdef VTK_AOS_DATA_ARRAY_TEMPLATE_INSTANTIATING
#define VTK_AOS_DATA_ARRAY_TEMPLATE_INSTANTIATE(T)                                                 \
  namespace vtkDataArrayPrivate                                                                    \
  {                                                                                                \
  VTK_ABI_NAMESPACE_BEGIN                                                                          \
  VTK_INSTANTIATE_VALUERANGE_ARRAYTYPE(vtkAOSDataArrayTemplate<T>, double);                        \
  VTK_ABI_NAMESPACE_END                                                                            \
  }                                                                                                \
  VTK_ABI_NAMESPACE_BEGIN                                                                          \
  template class VTKCOMMONCORE_EXPORT vtkAOSDataArrayTemplate<T>;                                  \
  VTK_ABI_NAMESPACE_END

#elif defined(VTK_USE_EXTERN_TEMPLATE)
#ifndef VTK_AOS_DATA_ARRAY_TEMPLATE_EXTERN
#define VTK_AOS_DATA_ARRAY_TEMPLATE_EXTERN
#ifdef _MSC_VER
#pragma warning(push)
// The following is needed when the vtkAOSDataArrayTemplate is declared
// dllexport and is used from another class in vtkCommonCore
#pragma warning(disable : 4910) // extern and dllexport incompatible
#endif
VTK_ABI_NAMESPACE_BEGIN
vtkExternTemplateMacro(extern template class VTKCOMMONCORE_EXPORT vtkAOSDataArrayTemplate);
VTK_ABI_NAMESPACE_END
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // VTK_AOS_DATA_ARRAY_TEMPLATE_EXTERN

// The following clause is only for MSVC
#elif defined(_MSC_VER) && !defined(VTK_BUILD_SHARED_LIBS)
#pragma warning(push)

// C4091: 'extern ' : ignored on left of 'int' when no variable is declared
#pragma warning(disable : 4091)

// Compiler-specific extension warning.
#pragma warning(disable : 4231)

// We need to disable warning 4910 and do an extern dllexport
// anyway.  When deriving vtkCharArray and other types from an
// instantiation of this template the compiler does an explicit
// instantiation of the base class.  From outside the vtkCommon
// library we block this using an extern dllimport instantiation.
// For classes inside vtkCommon we should be able to just do an
// extern instantiation, but VS complains about missing
// definitions.  We cannot do an extern dllimport inside vtkCommon
// since the symbols are local to the dll.  An extern dllexport
// seems to be the only way to convince VS to do the right
// thing, so we just disable the warning.
#pragma warning(disable : 4910) // extern and dllexport incompatible

// Use an "extern explicit instantiation" to give the class a DLL
// interface.  This is a compiler-specific extension.
VTK_ABI_NAMESPACE_BEGIN
vtkInstantiateTemplateMacro(extern template class VTKCOMMONCORE_EXPORT vtkAOSDataArrayTemplate);
VTK_ABI_NAMESPACE_END

#pragma warning(pop)

#endif

// VTK-HeaderTest-Exclude: vtkAOSDataArrayTemplate.h
