// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkXMLUniformGridAMRWriter.h"

#include "vtkAMRBox.h"
#include "vtkCartesianGrid.h"
#include "vtkErrorCode.h"
#include "vtkInformation.h"
#include "vtkObjectFactory.h"
#include "vtkOverlappingAMR.h"
#include "vtkSmartPointer.h"
#include "vtkXMLDataElement.h"

#include <cassert>

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkXMLUniformGridAMRWriter);
//------------------------------------------------------------------------------
vtkXMLUniformGridAMRWriter::vtkXMLUniformGridAMRWriter() = default;

//------------------------------------------------------------------------------
vtkXMLUniformGridAMRWriter::~vtkXMLUniformGridAMRWriter() = default;

//------------------------------------------------------------------------------
int vtkXMLUniformGridAMRWriter::FillInputPortInformation(int vtkNotUsed(port), vtkInformation* info)
{
  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkAMRDataObject");
  return 1;
}

//------------------------------------------------------------------------------
int vtkXMLUniformGridAMRWriter::WriteComposite(
  vtkCompositeDataSet* compositeData, vtkXMLDataElement* parent, int& writerIdx)
{
  vtkAMRDataObject* amr = vtkAMRDataObject::SafeDownCast(compositeData);
  assert(amr != nullptr);

  vtkOverlappingAMR* oamr = vtkOverlappingAMR::SafeDownCast(amr);

  // For vtkOverlappingAMR, we need to add additional meta-data to the XML.
  if (oamr)
  {
    const double* origin = oamr->GetOrigin();
    // I cannot decide what case to use. The other VTK-XML format used mixed
    // case for attributes, but the composite files are using all lower case
    // attributes. For consistency, I'm sticking with that.
    parent->SetVectorAttribute("origin", 3, origin);
    const char* gridDescription = "";
    switch (oamr->GetGridDescription())
    {
      case vtkStructuredData::VTK_STRUCTURED_XY_PLANE:
        gridDescription = "XY";
        break;

      case vtkStructuredData::VTK_STRUCTURED_YZ_PLANE:
        gridDescription = "YZ";
        break;

      case vtkStructuredData::VTK_STRUCTURED_XZ_PLANE:
        gridDescription = "XZ";
        break;

      case vtkStructuredData::VTK_STRUCTURED_XYZ_GRID:
      default:
        gridDescription = "XYZ";
        break;
    }
    parent->SetAttribute("grid_description", gridDescription);
  }

  unsigned int numLevels = amr->GetNumberOfLevels();

  // Iterate over each level.
  for (unsigned int level = 0; level < numLevels; level++)
  {
    vtkSmartPointer<vtkXMLDataElement> block = vtkSmartPointer<vtkXMLDataElement>::New();
    block->SetName("Block");
    block->SetIntAttribute("level", level);
    if (oamr)
    {
      // save spacing for each level.
      double spacing[3];
      oamr->GetSpacing(level, spacing);
      block->SetVectorAttribute("spacing", 3, spacing);

      // we no longer save the refinement ratios since those can be deduced from
      // the spacing very easily.
    }

    unsigned int numDS = amr->GetNumberOfBlocks(level);
    for (unsigned int cc = 0; cc < numDS; cc++)
    {
      vtkCartesianGrid* cg = amr->GetDataSetAsCartesianGrid(level, cc);

      vtkSmartPointer<vtkXMLDataElement> datasetXML = vtkSmartPointer<vtkXMLDataElement>::New();
      datasetXML->SetName("DataSet");
      datasetXML->SetIntAttribute("index", cc);
      if (oamr)
      {
        // AMRBox meta-data is available only for vtkOverlappingAMR. Also this
        // meta-data is expected to be consistent (and available) on all
        // processes so we don't have to worry about missing amr-box
        // information.
        const vtkAMRBox& amrBox = oamr->GetAMRBox(level, cc);

        int box_buffer[6];
        box_buffer[0] = amrBox.GetLoCorner()[0];
        box_buffer[1] = amrBox.GetHiCorner()[0];
        box_buffer[2] = amrBox.GetLoCorner()[1];
        box_buffer[3] = amrBox.GetHiCorner()[1];
        box_buffer[4] = amrBox.GetLoCorner()[2];
        box_buffer[5] = amrBox.GetHiCorner()[2];
        // Don't use vtkAMRBox.Serialize() since it writes the box in a different
        // order than we wrote the box in traditionally. The expected order is
        // (xLo, xHi, yLo, yHi, zLo, zHi).
        datasetXML->SetVectorAttribute("amr_box", 6, box_buffer);
      }

      std::string fileName = this->CreatePieceFileName(writerIdx);
      if (!fileName.empty())
      {
        // if fileName is empty, it implies that no file is written out for this
        // node, so don't add a filename attribute for it.
        datasetXML->SetAttribute("file", fileName.c_str());
      }
      block->AddNestedElement(datasetXML);

      // if this->WriteNonCompositeData() returns 0, it doesn't meant it's an
      // error, it just means that it didn't write a file for the current node.
      this->WriteNonCompositeData(cg, datasetXML, writerIdx, fileName.c_str());

      if (this->GetErrorCode() != vtkErrorCode::NoError)
      {
        return 0;
      }
    }
    parent->AddNestedElement(block);
  }

  return 1;
}

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