/*=========================================================================
 *
 *  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.
 *
 *=========================================================================*/
#include "itkGEAdwImageIO.h"
#include "itksys/SystemTools.hxx"

#include <iostream>
#include <fstream>

// From uiig library "The University of Iowa Imaging Group-UIIG"

namespace itk
{
// Default constructor
GEAdwImageIO::GEAdwImageIO()
{
  // Purposefully left blank
}

GEAdwImageIO::~GEAdwImageIO()
{
  // Purposefully left blank
}

bool
GEAdwImageIO::CanReadFile(const char * FileNameToRead)
{
  size_t imageSize;
  short  matrixX;
  short  matrixY;
  int    varHdrSize;

  //
  // Can you open it?
  std::ifstream f;
  try
  {
    this->OpenFileForReading(f, FileNameToRead);
  }
  catch (const ExceptionObject &)
  {
    return false;
  }

  //
  // This test basically snoops out the image dimensions, and the
  // length of the variable-length part of the header, and computes
  // the size the file should be and compares it with the actual size.
  // if it's not reading a GEAdw file, chances are overwhelmingly good
  // that this operation will fail somewhere along the line.
  if (this->GetShortAt(f, GE_ADW_IM_IMATRIX_X, &matrixX, false) != 0)
  {
    f.close();
    return false;
  }

  if (this->GetShortAt(f, GE_ADW_IM_IMATRIX_Y, &matrixY, false) != 0)
  {
    f.close();
    return false;
  }

  if (this->GetIntAt(f, GE_ADW_VARIABLE_HDR_LENGTH, &varHdrSize, false) != 0)
  {
    f.close();
    return false;
  }

  imageSize = varHdrSize + GE_ADW_FIXED_HDR_LENGTH + (matrixX * matrixY * sizeof(short));

  if (imageSize != itksys::SystemTools::FileLength(FileNameToRead))
  {
    f.close();
    return false;
  }
  f.close();
  return true;
}

GEImageHeader *
GEAdwImageIO::ReadHeader(const char * FileNameToRead)
{
  char tmpbuf[1024];

  if (!this->CanReadFile(FileNameToRead))
  {
    RAISE_EXCEPTION();
  }
  auto * hdr = new GEImageHeader;
  if (hdr == nullptr)
  {
    RAISE_EXCEPTION();
  }
  //
  // Next, can you open it?
  std::ifstream f;
  this->OpenFileForReading(f, FileNameToRead);

  snprintf(hdr->scanner, sizeof(hdr->scanner), "GE-ADW");
  this->GetStringAt(f, GE_ADW_EX_PATID, tmpbuf, 12);
  tmpbuf[12] = '\0';
  hdr->patientId[0] = '\0';
  for (char * ptr = strtok(tmpbuf, "-"); ptr != nullptr; ptr = strtok(nullptr, "-"))
  {
    strcat(hdr->patientId, ptr);
  }

  this->GetStringAt(f, GE_ADW_EX_TYP, hdr->modality, GE_ADW_EX_TYP_LEN);
  hdr->modality[GE_ADW_EX_TYP_LEN] = '\0';

  this->GetStringAt(f, GE_ADW_EX_PATNAME, hdr->name, GE_ADW_EX_PATNAME_LEN);
  hdr->name[GE_ADW_EX_PATNAME_LEN] = '\0';

  this->GetStringAt(f, GE_ADW_EX_HOSPNAME, hdr->hospital, 34);
  hdr->hospital[33] = '\0';

  int timeStamp;
  this->GetIntAt(f, GE_ADW_EX_DATETIME, &timeStamp);
  this->statTimeToAscii(&timeStamp, hdr->date, sizeof(hdr->date));

  this->GetStringAt(f, GE_ADW_SU_PRODID, hdr->scanner, 13);
  hdr->scanner[13] = '\0';

  this->GetShortAt(f, GE_ADW_SE_NO, &(hdr->seriesNumber));

  this->GetShortAt(f, GE_ADW_IM_NO, &(hdr->imageNumber));

  this->GetShortAt(f, GE_ADW_IM_CPHASENUM, &(hdr->imagesPerSlice));

  this->GetShortAt(f, GE_ADW_IM_CPHASENUM, &(hdr->turboFactor));

  this->GetFloatAt(f, GE_ADW_IM_SLTHICK, &(hdr->sliceThickness));
  hdr->sliceGap = 0.0f;

  this->GetShortAt(f, GE_ADW_IM_IMATRIX_X, &(hdr->imageXsize));

  this->GetShortAt(f, GE_ADW_IM_IMATRIX_Y, &(hdr->imageYsize));

  hdr->acqXsize = hdr->imageXsize;
  hdr->acqYsize = hdr->imageYsize;

  this->GetFloatAt(f, GE_ADW_IM_DFOV, &hdr->xFOV);
  hdr->yFOV = hdr->xFOV;

  this->GetFloatAt(f, GE_ADW_IM_PIXSIZE_X, &hdr->imageXres);

  this->GetFloatAt(f, GE_ADW_IM_PIXSIZE_Y, &hdr->imageYres);

  short tmpShort;
  this->GetShortAt(f, GE_ADW_IM_PLANE, &tmpShort);
  switch (tmpShort)
  {
    case GE_CORONAL:
      // hdr->imagePlane = itk::IOCommon::ITK_ANALYZE_ORIENTATION_IRP_CORONAL;
      // hdr->origin = itk::IOCommon::ITK_ORIGIN_SLA;
      hdr->coordinateOrientation =
        itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP;
      break;
    case GE_SAGITTAL:
      // hdr->imagePlane =
      // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_SAGITTAL;
      // hdr->origin = itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ORIGIN_SLA;
      hdr->coordinateOrientation =
        itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR;
      break;
    case GE_AXIAL:
      // hdr->imagePlane =
      // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_TRANSVERSE;
      // hdr->origin = itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ORIGIN_SLA;
      hdr->coordinateOrientation =
        itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI;
      break;
    default:
      // hdr->imagePlane =
      // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_CORONAL;
      // hdr->origin = itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ORIGIN_SLA;
      hdr->coordinateOrientation =
        itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP;
      break;
  }
  this->GetFloatAt(f, GE_ADW_IM_LOC, &(hdr->sliceLocation));

  int tmpInt;
  this->GetIntAt(f, GE_ADW_IM_TR, &tmpInt);
  hdr->TR = static_cast<float>(tmpInt) / 1000.0f;

  this->GetIntAt(f, GE_ADW_IM_TI, &tmpInt);
  hdr->TI = static_cast<float>(tmpInt) / 1000.0f;

  this->GetIntAt(f, GE_ADW_IM_TE, &tmpInt);
  hdr->TE = static_cast<float>(tmpInt) / 1000.0f;

  this->GetShortAt(f, GE_ADW_IM_NUMECHO, &(hdr->numberOfEchoes));

  this->GetShortAt(f, GE_ADW_IM_ECHONUM, &(hdr->echoNumber));

  float tmpFloat;
  this->GetFloatAt(f, GE_ADW_IM_NEX, &tmpFloat);

  hdr->NEX = static_cast<int>(tmpFloat);

  this->GetShortAt(f, GE_ADW_IM_MR_FLIP, &hdr->flipAngle);

  this->GetStringAt(f, GE_ADW_IM_PSDNAME, hdr->pulseSequence, 31);
  hdr->pulseSequence[31] = '\0';

  this->GetShortAt(f, GE_ADW_IM_SLQUANT, &(hdr->numberOfSlices));

  this->GetIntAt(f, GE_ADW_VARIABLE_HDR_LENGTH, &tmpInt);
  hdr->offset = GE_ADW_FIXED_HDR_LENGTH + tmpInt;

  strncpy(hdr->filename, FileNameToRead, IOCommon::ITK_MAXPATHLEN);
  hdr->filename[IOCommon::ITK_MAXPATHLEN] = '\0';

  return hdr;
}
} // end namespace itk
