/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         https://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkMetaArrayWriter_h
#define itkMetaArrayWriter_h
#include "ITKIOMetaExport.h"

#include "itkLightProcessObject.h"
#include "itkArray.h"
#include "itkCovariantVector.h"
#include "itkVariableLengthVector.h"
#include "metaArray.h"

namespace itk
{
class ITKIOMeta_EXPORT MetaArrayWriter : public LightProcessObject
{
public:
  /** SmartPointer type alias support */
  using Self = MetaArrayWriter;
  using Superclass = LightProcessObject;

  using Pointer = SmartPointer<Self>;
  using ConstPointer = SmartPointer<const Self>;

  /** Method for creation through the object factory. */
  itkNewMacro(Self);

  /** \see LightObject::GetNameOfClass() */
  itkOverrideGetNameOfClassMacro(MetaArrayWriter);

  /** Set/Get the filename. */
  itkSetStringMacro(FileName);
  itkGetStringMacro(FileName);

  /** Set/Get the filename to which the data is written.
   *  Optional: use if header and data should be in separate files. */
  itkSetStringMacro(DataFileName);
  itkGetStringMacro(DataFileName);

  /** Boolean to set binary mode.
   *  If set to On, data will be stored in the file in binary format; if set
   *  to Off, data will be stored in ascii format.
   *  Default is Off. */
  itkBooleanMacro(Binary);
  itkSetMacro(Binary, bool);
  itkGetConstMacro(Binary, bool);

  /** Set the input itk Array to write. */
  template <typename TValue>
  void
  SetInput(MET_ValueEnumType _metaElementType, const Array<TValue> * _array)
  {
    m_Buffer = (const void *)(_array->data_block());
    m_MetaArray.InitializeEssential(_array->Size(), _metaElementType);
  }

  /** Set the input itk FixedArray to write. */
  template <typename TValue, unsigned int VLength>
  void
  SetInput(MET_ValueEnumType _metaElementType, const FixedArray<TValue, VLength> * _array)
  {
    m_Buffer = (const void *)(_array->GetDataPointer());
    m_MetaArray.InitializeEssential(VLength, _metaElementType);
  }

  /** Set the input itk Vector to write. */
  template <typename TValue, unsigned int VLength>
  void
  SetInput(MET_ValueEnumType _metaElementType, const Vector<TValue, VLength> * _vector)
  {
    m_Buffer = (const void *)(_vector->GetDataPointer());
    m_MetaArray.InitializeEssential(VLength, _metaElementType);
  }

  /** Set the input itk CovariantVector to write. */
  template <typename TValue, unsigned int VLength>
  void
  SetInput(MET_ValueEnumType _metaElementType, const CovariantVector<TValue, VLength> * _vector)
  {
    m_Buffer = (const void *)(_vector->GetDataPointer());
    m_MetaArray.InitializeEssential(VLength, _metaElementType);
  }

  /** Set the input itk VariableLengthVector to write. */
  template <typename TValue>
  void
  SetInput(MET_ValueEnumType _metaElementType, const VariableLengthVector<TValue> * _vector)
  {
    m_Buffer = (const void *)(_vector->GetDataPointer());
    m_MetaArray.InitializeEssential(_vector->Size(), _metaElementType);
  }

  /** Copies the elements from an array of arrays into the output
   *  buffer. Requires all sub-arrays to have the same length.
   *  length of the major array is the "length" of the array, while
   *  the length of the sub-arrays is the "number of channels" at each
   *  array position. Expected form itk::Array< itk::Array< * > >.
   *  May work for other sub-array-types that define the [] operator and the
   *  GetSize() function. */
  template <typename TValue>
  void
  SetMultiChannelInput(MET_ValueEnumType _metaElementType, int, const Array<TValue> * _array)
  {
    int rows = _array->GetSize();
    int cols = (*_array)[0].GetSize();

    m_MetaArray.InitializeEssential(rows, _metaElementType, cols, nullptr, true, true);
    m_Buffer = m_MetaArray.ElementData();
    for (int i = 0; i < rows; ++i)
    {
      for (int j = 0; j < cols; ++j)
      {
        m_MetaArray.ElementData(i * cols + j, static_cast<double>((*_array)[i][j]));
      }
    }
  }

  /** Set/Get the precision of the writing. */
  itkSetMacro(Precision, unsigned int);
  itkGetConstMacro(Precision, unsigned int);

  /** Set the data type written to the file. */
  void
  ConvertTo(MET_ValueEnumType _metaElementType);

  /** Write out the array. */
  void
  Update();

protected:
  MetaArrayWriter();
  ~MetaArrayWriter() override;
  void
  PrintSelf(std::ostream & os, Indent indent) const override;

private:
  bool m_Binary{ false };

  unsigned int m_Precision{ 6 };

  std::string m_FileName{};
  std::string m_DataFileName{};

  MetaArray m_MetaArray{};

  const void * m_Buffer{ nullptr };
};
} // namespace itk

#endif // itkMetaArrayWriter_h
