// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#ifdef _MSC_VER
#pragma warning(disable : 4018)
#endif

#include "vtkMetaImageWriter.h"

#include "vtkAlgorithmOutput.h"
#include "vtkCommand.h"
#include "vtkDataSetAttributes.h"
#include "vtkErrorCode.h"
#include "vtkImageData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkStreamingDemandDrivenPipeline.h"

#include "vtkMatrix3x3.h"
#include "vtkmetaio/metaEvent.h"
#include "vtkmetaio/metaImage.h"
#include "vtkmetaio/metaImageTypes.h"
#include "vtkmetaio/metaImageUtils.h"
#include "vtkmetaio/metaObject.h"
#include "vtkmetaio/metaTypes.h"
#include "vtkmetaio/metaUtils.h"
#include <string>

#include <sys/stat.h>

//------------------------------------------------------------------------------
VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkMetaImageWriter);

//------------------------------------------------------------------------------
vtkMetaImageWriter::vtkMetaImageWriter()
{
  this->MHDFileName = nullptr;
  this->FileLowerLeft = 1;

  this->MetaImagePtr = new vtkmetaio::MetaImage;
  this->Compress = true;
}

//------------------------------------------------------------------------------
vtkMetaImageWriter::~vtkMetaImageWriter()
{
  this->SetFileName(nullptr);
  delete this->MetaImagePtr;
}

//------------------------------------------------------------------------------
void vtkMetaImageWriter::SetFileName(const char* fname)
{
  this->SetMHDFileName(fname);
  this->Superclass::SetFileName(nullptr);
}

//------------------------------------------------------------------------------
void vtkMetaImageWriter::SetRAWFileName(const char* fname)
{
  this->Superclass::SetFileName(fname);
}

//------------------------------------------------------------------------------
VTK_FUTURE_CONST char* vtkMetaImageWriter::GetRAWFileName() VTK_FUTURE_CONST
{
  return this->Superclass::GetFileName();
}

//------------------------------------------------------------------------------
void vtkMetaImageWriter::Write()
{
  this->SetErrorCode(vtkErrorCode::NoError);

  vtkDemandDrivenPipeline::SafeDownCast(this->GetInputExecutive(0, 0))->UpdateInformation();

  // Error checking
  if (this->GetInput() == nullptr)
  {
    vtkErrorMacro(<< "Write:Please specify an input!");
    return;
  }

  if (!this->MHDFileName)
  {
    vtkErrorMacro("Output file name not specified");
    return;
  }

  int nDims = 3;
  const int* ext =
    this->GetInputInformation(0, 0)->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
  if (ext[4] == ext[5])
  {
    nDims = 2;
    if (ext[2] == ext[3])
    {
      nDims = 1;
    }
  }

  this->GetInputAlgorithm()->UpdateExtent(ext);

  double origin[3];
  double spacing[3];
  this->GetInput()->GetOrigin(origin);
  this->GetInput()->GetSpacing(spacing);

  double* direction = this->GetInput()->GetDirectionMatrix()->GetData();

  int dimSize[3];
  dimSize[0] = ext[1] - ext[0] + 1;
  dimSize[1] = ext[3] - ext[2] + 1;
  dimSize[2] = ext[5] - ext[4] + 1;

  vtkmetaio::MET_ValueEnumType elementType;

  int scalarType = this->GetInput()->GetScalarType();
  switch (scalarType)
  {
    case VTK_CHAR:
      elementType = vtkmetaio::MET_CHAR;
      break;
    case VTK_SIGNED_CHAR:
      elementType = vtkmetaio::MET_CHAR;
      break;
    case VTK_UNSIGNED_CHAR:
      elementType = vtkmetaio::MET_UCHAR;
      break;
    case VTK_SHORT:
      elementType = vtkmetaio::MET_SHORT;
      break;
    case VTK_UNSIGNED_SHORT:
      elementType = vtkmetaio::MET_USHORT;
      break;
    case VTK_INT:
      elementType = vtkmetaio::MET_INT;
      break;
    case VTK_UNSIGNED_INT:
      elementType = vtkmetaio::MET_UINT;
      break;
    case VTK_LONG:
      elementType = vtkmetaio::MET_LONG;
      break;
    case VTK_UNSIGNED_LONG:
      elementType = vtkmetaio::MET_ULONG;
      break;
    case VTK_FLOAT:
      elementType = vtkmetaio::MET_FLOAT;
      break;
    case VTK_DOUBLE:
      elementType = vtkmetaio::MET_DOUBLE;
      break;
    default:
      vtkErrorMacro("Unknown scalar type.");
      return;
  }

  origin[0] += ext[0] * spacing[0];
  origin[1] += ext[2] * spacing[1];
  origin[2] += ext[4] * spacing[2];

  int numberOfElements = this->GetInput()->GetNumberOfScalarComponents();

  this->MetaImagePtr->InitializeEssential(nDims, dimSize, spacing, elementType, numberOfElements,
    this->GetInput()->GetScalarPointer(ext[0], ext[2], ext[4]), false);
  this->MetaImagePtr->Position(origin);
  this->MetaImagePtr->TransformMatrix(direction);

  if (this->GetRAWFileName())
  {
    this->MetaImagePtr->ElementDataFileName(this->GetRAWFileName());
  }

  this->SetFileDimensionality(nDims);
  this->MetaImagePtr->CompressedData(Compress);

  this->InvokeEvent(vtkCommand::StartEvent);
  this->UpdateProgress(0.0);
  this->MetaImagePtr->Write(this->MHDFileName);
  this->UpdateProgress(1.0);
  this->InvokeEvent(vtkCommand::EndEvent);
}

//------------------------------------------------------------------------------
void vtkMetaImageWriter::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "MHDFileName: " << (this->MHDFileName ? this->MHDFileName : "(none)") << endl;
}
VTK_ABI_NAMESPACE_END
