/*
!===============================================================================
!
! ildg_lemon.c - read/write configuration in ILDG lime format with LEMON
!
!-------------------------------------------------------------------------------
!
! Copyright (C) 2010 Yoshifumi Nakamura
!
! This file is part of BQCD -- Berlin Quantum ChromoDynamics program
!
! BQCD is free software: you can redistribute it and/or modify
! it under the terms of the GNU General Public License as published by
! the Free Software Foundation, either version 3 of the License, or
! (at your option) any later version.
!
! BQCD is distributed in the hope that it will be useful,
! but WITHOUT ANY WARRANTY; without even the implied warranty of
! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
! GNU General Public License for more details.
!
! You should have received a copy of the GNU General Public License
! along with BQCD.  If not, see <http://www.gnu.org/licenses/>.
!
!-------------------------------------------------------------------------------
*/
# include <sys/types.h>
# include <stdio.h>
# include <string.h>
# include <mpi.h>
# include "c_defs.h"
# include "c_util.h"
# include "cksum.h"
# include "ildg.h"
# include "lemon.h"

static MPI_File fp;
static LemonReader *r;
static LemonWriter *w;
static LemonRecordHeader *h;

static char xmldoc[1024];
static int len_xmldoc = 1023;

int status;
uint64_t nbytes;
/*----------------------------------------------------------------------------*/
void ILDG_OPEN_R_LEMON(char *filename, 
                 int *Lx, int *Ly, int *Lz, int *Lt, 
                 int npe[4], int *precision,
                 INT8 *bytes_total)
{
  /*  filename         (input)
   *  Lx, Ly, Lz, Lt   lattice size (output)
   *  precision        32|64 (output)
   *  bytes_total     bytes of configuration (output)
   */
  int latDist[] = {2, 1, 2, 1};
  int periods[] = {1, 1, 1, 1};

  latDist[0]=npe[3];
  latDist[1]=npe[2];
  latDist[2]=npe[1];
  latDist[3]=npe[0];

  MPI_Comm cartesian;
  MPI_Cart_create(MPI_COMM_WORLD, 4, latDist, periods, 1, &cartesian);
  MPI_File_open(cartesian, filename, MPI_MODE_RDONLY, MPI_INFO_NULL, &fp);
  r = lemonCreateReader(&fp, cartesian);

  ildg_find_record_lemon("ildg-format");
  ildg_read_string_lemon("ildg-format", xmldoc, len_xmldoc);

  ildg_xml_check_value(xmldoc, "<version>", "1.0");
  ildg_xml_check_value(xmldoc, "<field>", "su3gauge");
  ildg_xml_get_int(xmldoc, "<precision>", precision);
  ildg_xml_get_int(xmldoc, "<lx>", Lx);
  ildg_xml_get_int(xmldoc, "<ly>", Ly);
  ildg_xml_get_int(xmldoc, "<lz>", Lz);
  ildg_xml_get_int(xmldoc, "<lt>", Lt);

  ildg_find_record_lemon("ildg-binary");

  *bytes_total = lemonReaderBytes(r);

  CKSUM_INIT();
}

/*----------------------------------------------------------------------------*/
void ILDG_OPEN_W_LEMON(char *filename, 
                       int *Lx, int *Ly, int *Lz, int *Lt,
                       int npe[4], int *precision,
                       INT8 *bytes_total)
{
  /*  filename         (input)
   *  Lx, Ly, Lz, Lt   lattice size (input)
   *  precision        32|64 (input)
   *  bytes_total     bytes of configuration (input)
   */

  int latDist[] = {2, 1, 2, 1};
  int periods[] = {1, 1, 1, 1};

  latDist[0]=npe[3];
  latDist[1]=npe[2];
  latDist[2]=npe[1];
  latDist[3]=npe[0];

  MPI_Comm cartesian;
  //  MPI_Comm_size(MPI_COMM_WORLD, &mpisize);
  //  MPI_Dims_create(mpisize, 4, latDist);
  //  printf("latDist %d %d %d %d\n",latDist[0],latDist[1],latDist[2],latDist[3]);

  MPI_Cart_create(MPI_COMM_WORLD, 4, latDist, periods, 1, &cartesian);
  MPI_File_open(cartesian, filename, MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL, &fp);
  MPI_File_set_size(fp, 0);
  w = lemonCreateWriter(&fp, cartesian);

  sprintf(xmldoc, 
          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
          "<ildgFormat>\n"
          "  <version>1.0</version>\n"
          "  <field>su3gauge</field>\n"
          "  <precision>%d</precision>\n"
          "  <lx>%d</lx> <ly>%d</ly> <lz>%d</lz> <lt>%d</lt>\n"
          "</ildgFormat>\n",
          *precision, *Lx, *Ly, *Lz, *Lt);
  nbytes = strlen(xmldoc);
  
  h = lemonCreateHeader(1, 0, "ildg-format", nbytes);

  status=lemonWriteRecordHeader(h, w);
  if (status != LEMON_SUCCESS)
    die("failed to lemonWriteRecordHeader (status=%d)\n", status);

  lemonDestroyHeader(h);

  status=lemonWriteRecordData(xmldoc, &nbytes, w);
  if (status != LEMON_SUCCESS)
    die("failed to lemonWriteRecordData (status=%d)\n", status);

  lemonWriterCloseRecord(w);

  nbytes = *bytes_total;
  h = lemonCreateHeader(0, 1, "ildg-binary-data", nbytes);

  status=lemonWriteRecordHeader(h, w);
  if (status != LEMON_SUCCESS)
    die("failed to lemonWriteRecordData (status=%d)\n", status);

  lemonDestroyHeader(h);

  CKSUM_INIT();
}

/*----------------------------------------------------------------------------*/
void ILDG_READ_LEMON(void *buffer, int L[4], int *vol, int *precision)
{
  /*  buffer        (input)
   *  bytes         (input)
   */

  int const mapping[] = {0, 1, 2, 3};
  uint64_t siteSize=sizeof(double) * 2* 3*3*4;
  if (*precision == 32) siteSize/=2;
  //  INT8 bytes=siteSize * (INT8)*vol;

  status=lemonReadLatticeParallelMapped(r, buffer, siteSize, L, mapping);
  if (status != LEMON_SUCCESS)
    die("failed lemonReadLatticeParallelMapped (status=%d\n)", status);
  lemonReaderCloseRecord(r);
}

/*----------------------------------------------------------------------------*/
void ILDG_WRITE_LEMON(void *buffer, int L[4], int *vol, int *precision)
{
  /*  buffer        (output)
   *  bytes         (input)
   */

  int const mapping[] = {0, 1, 2, 3};
  uint64_t siteSize=sizeof(double) * 2* 3*3*4;
  if (*precision == 32) siteSize/=2;
  //  INT8 bytes=siteSize * (INT8)&vol;

  //  printf("ILDG_WRITE_LEMON siteSize:%llu\n",siteSize);
  //status=lemonWriteLatticeParallel(w, buffer, siteSize, latSizes);

  status=lemonWriteLatticeParallelMapped(w, buffer, siteSize, L, mapping);
  if (status != LEMON_SUCCESS)
    die("failed lemonWriteLatticeParallelMapped (status=%d\n)", status);
  lemonWriterCloseRecord(w);
}

/*----------------------------------------------------------------------------*/
void ILDG_CLOSE_R_LEMON(INT8 *cksum, INT8 *bytes_total, char *lfn, int *len)
{
  /*  cksum         CRC check sum (output)
   *  bytes_total   (output) (from cksum())
   *  lfn           logical file name (output)
   *  len           input: max. length of "lfn"
   *                output: actual length of "lfn"
   */

  //  fseek(fp, 0, SEEK_SET);  how should I change?
  do {
      status = lemonReaderNextRecord(r);
      if (status == LEMON_EOF)
          break;
  } while (strncmp(lemonReaderType(r), "ildg-data-lfn", 13) != 0);


  if (status == LEMON_EOF)
      strcpy(lfn, "UNDEFINED");
  else
      ildg_read_string_lemon("ildg-data-lfn", lfn, *len);

  *len = strlen(lfn);

  lemonDestroyReader(r);
  MPI_File_close(&fp);
  CKSUM_GET(cksum, bytes_total);
}

/*----------------------------------------------------------------------------*/
void ILDG_CLOSE_W_LEMON(INT8 *cksum, INT8 *bytes_total, char *lfn)
{
  /*  cksum         CRC check sum (output)
   *  bytes_total   (output) (from cksum())
   *  lfn           logical file name (input)
   */

  nbytes = strlen(lfn);

  h = lemonCreateHeader(1, 1, "ildg-data-lfn", nbytes);

  status=lemonWriteRecordHeader(h, w);
  if (status != LEMON_SUCCESS)
    die("failed lemonWriteRecordHeader (status=%d)\n", status);

  lemonDestroyHeader(h);

  status=lemonWriteRecordData(lfn, &nbytes, w);
  if (status != LEMON_SUCCESS)
    die("failed lemonWriteRecordData (status=%d)\n", status);

  lemonDestroyWriter(w);
  MPI_File_close(&fp);
  CKSUM_GET(cksum, bytes_total);

}

/*----------------------------------------------------------------------------*/
void ildg_find_record_lemon(char *record)
{
  /*  record         (input)
   */

  //  fseek(fp, 0, SEEK_SET); how should I change?
  do {
      status = lemonReaderNextRecord(r);
      if (status == LEMON_EOF)
          die("unexpected end-of-file");
  } while (strncmp(lemonReaderType(r), record, (int) strlen(record)) != 0);
}

/*----------------------------------------------------------------------------*/
void ildg_read_string_lemon(char *record, char *s, int len)
{
  /*  record         (input)
   *  s              string read (output)
   *  len            max. length of "s"
   */

  nbytes = lemonReaderBytes(r);
  if (nbytes > len)
    die("string (length=%d) too small for reading record %s", len, record);

  status = lemonReaderReadData(s, &nbytes, r);
  if (status != LEMON_SUCCESS)
      die("failed to read record %s (status=%d)", record, status);
  s[nbytes] = '\0';
}

/*============================================================================*/
