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

#include "vtkDataObject.h"
#include "vtkExecutive.h"
#include "vtkFieldData.h"
#include "vtkImageData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMolecule.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkPeriodicTable.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkSimpleBondPerceiver.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkTransform.h"
#include "vtksys/FStream.hxx"

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkGaussianCubeReader2);

//------------------------------------------------------------------------------
vtkGaussianCubeReader2::vtkGaussianCubeReader2()
  : FileName(nullptr)
{
  this->SetNumberOfInputPorts(0);
  this->SetNumberOfOutputPorts(2);

  // Add the second output for the grid data

  vtkImageData* grid;
  grid = vtkImageData::New();
  grid->ReleaseData();
  this->GetExecutive()->SetOutputData(1, grid);
  grid->Delete();
}

//------------------------------------------------------------------------------
vtkGaussianCubeReader2::~vtkGaussianCubeReader2()
{
  this->SetFileName(nullptr);
}

//------------------------------------------------------------------------------
vtkMolecule* vtkGaussianCubeReader2::GetOutput()
{
  return vtkMolecule::SafeDownCast(this->GetOutputDataObject(0));
}

//------------------------------------------------------------------------------
void vtkGaussianCubeReader2::SetOutput(vtkMolecule* output)
{
  this->GetExecutive()->SetOutputData(0, output);
}

//------------------------------------------------------------------------------
vtkImageData* vtkGaussianCubeReader2::GetGridOutput()
{
  if (this->GetNumberOfOutputPorts() < 2)
  {
    return nullptr;
  }
  return vtkImageData::SafeDownCast(this->GetOutputDataObject(1));
}

int vtkGaussianCubeReader2::RequestInformation(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* vtkNotUsed(outputVector))
{
  // the set the information for the imagedata output
  vtkInformation* gridInfo = this->GetExecutive()->GetOutputInformation(1);

  char title[256];

  if (!this->FileName)
  {
    return 0;
  }

  vtksys::ifstream file_in(this->FileName);

  if (!file_in.is_open())
  {
    vtkErrorMacro("GaussianCubeReader2 error opening file: " << this->FileName);
    return 0;
  }

  file_in.getline(title, 256); // first title line
  file_in.getline(title, 256); // second title line with name of scalar field?

  // Read in number of atoms, x-origin, y-origin z-origin
  double tmpd;
  int n1, n2, n3;
  if (!(file_in >> n1 >> tmpd >> tmpd >> tmpd))
  {
    vtkErrorMacro("GaussianCubeReader error reading file: " << this->FileName
                                                            << " Premature EOF while grid size.");
    file_in.close();
    return 0;
  }

  if (!(file_in >> n2 >> tmpd >> tmpd >> tmpd))
  {
    vtkErrorMacro("GaussianCubeReader error reading file: " << this->FileName
                                                            << " Premature EOF while grid size.");
    file_in.close();
    return 0;
  }
  if (!(file_in >> n3 >> tmpd >> tmpd >> tmpd))
  {
    vtkErrorMacro("GaussianCubeReader error reading file: " << this->FileName
                                                            << " Premature EOF while grid size.");
    file_in.close();
    return 0;
  }

  vtkDebugMacro(<< "Grid Size " << n1 << " " << n2 << " " << n3);
  gridInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), 0, n1 - 1, 0, n2 - 1, 0, n3 - 1);
  gridInfo->Set(vtkDataObject::ORIGIN(), 0, 0, 0);
  gridInfo->Set(vtkDataObject::SPACING(), 1, 1, 1);

  file_in.close();

  vtkDataObject::SetPointDataActiveScalarInfo(gridInfo, VTK_FLOAT, -1);
  return 1;
}

int vtkGaussianCubeReader2::RequestData(
  vtkInformation*, vtkInformationVector**, vtkInformationVector* outputVector)
{
  char title[256];
  double elements[16];
  int NumberOfAtoms;
  int n1, n2, n3; // grid resolution
  float tmp, *cubedata;
  bool orbitalCubeFile = false;
  int numberOfOrbitals;

  vtkMolecule* output = vtkMolecule::SafeDownCast(vtkDataObject::GetData(outputVector));

  if (!output)
  {
    vtkErrorMacro(<< "vtkGaussianCubeReader2 does not have a vtkMolecule "
                     "as output.");
    return 1;
  }
  // Output 0 (the default is the vtkMolecule)
  // Output 1 will be the gridded Image data

  if (!this->FileName)
  {
    return 0;
  }

  vtksys::ifstream file_in(this->FileName);

  if (!file_in.is_open())
  {
    vtkErrorMacro("GaussianCubeReader2 error opening file: " << this->FileName);
    return 0;
  }

  file_in.getline(title, 256); // first title line
  file_in.getline(title, 256); // second title line with name of scalar field?

  // Read in number of atoms, x-origin, y-origin z-origin
  //
  if (!(file_in >> NumberOfAtoms >> elements[3] >> elements[7] >> elements[11]))
  {
    vtkErrorMacro("GaussianCubeReader error reading file: "
      << this->FileName << " Premature EOF while reading atoms, x-origin y-origin z-origin.");
    file_in.close();
    return 0;
  }
  if (NumberOfAtoms < 0)
  {
    NumberOfAtoms = -NumberOfAtoms;
    orbitalCubeFile = true;
  }
  if (!(file_in >> n1 >> elements[0] >> elements[4] >> elements[8]))
  {
    vtkErrorMacro("GaussianCubeReader error reading file: "
      << this->FileName << " Premature EOF while reading elements.");
    file_in.close();
    return 0;
  }
  if (!(file_in >> n2 >> elements[1] >> elements[5] >> elements[9]))
  {
    vtkErrorMacro("GaussianCubeReader error reading file: "
      << this->FileName << " Premature EOF while reading elements.");
    file_in.close();
    return 0;
  }
  if (!(file_in >> n3 >> elements[2] >> elements[6] >> elements[10]))
  {
    vtkErrorMacro("GaussianCubeReader error reading file: "
      << this->FileName << " Premature EOF while reading elements.");
    file_in.close();
    return 0;
  }
  elements[12] = 0;
  elements[13] = 0;
  elements[14] = 0;
  elements[15] = 1;

  vtkDebugMacro(<< "Grid Size " << n1 << " " << n2 << " " << n3);

  vtkTransform* Transform = vtkTransform::New();

  Transform->SetMatrix(elements);
  Transform->Inverse();
  // construct vtkMolecule
  int atomType;
  float xyz[3];
  float dummy;

  for (int i = 0; i < NumberOfAtoms; i++)
  {
    if (!(file_in >> atomType >> dummy >> xyz[0] >> xyz[1] >> xyz[2]))
    {
      vtkErrorMacro("GaussianCubeReader error reading file: "
        << this->FileName << " Premature EOF while reading molecule.");
      file_in.close();
      return 0;
    }
    Transform->TransformPoint(xyz, xyz);
    output->AppendAtom(atomType, xyz[0], xyz[1], xyz[2]);
  }
  Transform->Delete();
  // construct grid data

  vtkImageData* grid = this->GetGridOutput();
  if (orbitalCubeFile)
  {
    if (!(file_in >> numberOfOrbitals))
    {
      vtkErrorMacro("GaussianCubeReader error reading file: "
        << this->FileName << " Premature EOF while reading number of orbitals.");
      file_in.close();
      return 0;
    }
    for (int k = 0; k < numberOfOrbitals; k++)
    {
      if (!(file_in >> tmp))
      {
        vtkErrorMacro("GaussianCubeReader error reading file: "
          << this->FileName << " Premature EOF while reading orbitals.");
        file_in.close();
        return 0;
      }
    }
  }

  vtkInformation* gridInfo = this->GetExecutive()->GetOutputInformation(1);
  gridInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), 0, n1 - 1, 0, n2 - 1, 0, n3 - 1);
  gridInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(),
    gridInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()), 6);
  grid->SetExtent(gridInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()));

  grid->SetOrigin(0, 0, 0);
  grid->SetSpacing(1, 1, 1);
  grid->AllocateScalars(VTK_FLOAT, 1);

  grid->GetPointData()->GetScalars()->SetName(title);

  cubedata = (float*)grid->GetPointData()->GetScalars()->GetVoidPointer(0);
  int N1N2 = n1 * n2;

  for (int i = 0; i < n1; i++)
  {
    int JN1 = 0;
    for (int j = 0; j < n2; j++)
    {
      for (int k = 0; k < n3; k++)
      {
        if (!(file_in >> tmp))
        {
          vtkErrorMacro("GaussianCubeReader error reading file: "
            << this->FileName << " Premature EOF while reading scalars.");
          file_in.close();
          return 0;
        }
        cubedata[k * N1N2 + JN1 + i] = tmp;
      }
      JN1 += n1;
    }
  }
  file_in.close();

  return 1;
}

int vtkGaussianCubeReader2::FillOutputPortInformation(int port, vtkInformation* info)
{
  if (port == 0)
  {
    return this->Superclass::FillOutputPortInformation(port, info);
  }
  info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkImageData");
  return 1;
}

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