# include <dvo.h>

/* The Ftable-TYPE conversion functions determine the format of table based on EXTNAME in header.
   they convert the table to the internal format, and set 'format'.  

   The TYPE-Ftable conversions functions create output tables in the format requested
   by the 'format' function parameter.
*/

/** this file might be more readable if I use macros for the repetative
    constructions below **/

DVOTableFormat FtableGetFormat (FTable *ftable) {

  DVOTableFormat format;
  char extname[80];

  /* read the format of this table */
  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
    fprintf (stderr, "EXTNAME missing for average table\n");
    return (DVO_FORMAT_UNDEF);
  }

  if (!strcmp (extname, "DVO_AVERAGE")) {
    format = DVO_FORMAT_INTERNAL;
    return (format);
  }
# define CONVERT_FORMAT(NAME, FORMAT)		\
  if (!strcmp (extname, NAME)) {		\
    format = DVO_FORMAT_##FORMAT;		\
    return (format); }

  CONVERT_FORMAT ("DVO_AVERAGE_ELIXIR", 	 ELIXIR);
  CONVERT_FORMAT ("DVO_AVERAGE_LONEOS", 	 LONEOS);
  CONVERT_FORMAT ("DVO_AVERAGE_PANSTARRS_DEV_0", PANSTARRS_DEV_0);
  CONVERT_FORMAT ("DVO_AVERAGE_PANSTARRS_DEV_1", PANSTARRS_DEV_1);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_DEV_1",       PS1_DEV_1);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_DEV_2",       PS1_DEV_2);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_V1",          PS1_V1);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_V2",          PS1_V2);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_V3",          PS1_V3);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_REF",         PS1_REF);
# undef CONVERT_FORMAT

  fprintf (stderr, "table format unknown: %s\n", extname);
  return (DVO_FORMAT_UNDEF);
}

/*** Average / FTable conversion functions ***/

Average *FtableToAverage (FTable *ftable, off_t *Naverage, char *format, SecFilt **primary) {

  Average *average;
  char extname[80];

  /* in the Elixir and Loneos cases, we are supplied with an average magnitude in Average
     in these cases, save these values in primary; otherwise set primary to NULL */
  *primary = NULL;

  /* convert to the internal format */
  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
    fprintf (stderr, "EXTNAME missing for average table\n");
    return (FALSE);
  }

# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
  if (!strcmp (extname, NAME)) { \
    Average_##TYPE *tmpAverage; \
    tmpAverage = gfits_table_get_Average_##TYPE (ftable, Naverage, NULL); \
    if (!tmpAverage) { \
      fprintf (stderr, "ERROR: failed to read averages\n"); \
      exit (2); \
    } \
    average = Average_##TYPE##_ToInternal (tmpAverage, *Naverage, primary); \
    free (tmpAverage); \
    *format = DVO_FORMAT_##FORMAT; \
    return (average); }

  if (!strcmp (extname, "DVO_AVERAGE")) {
    average = gfits_table_get_Average (ftable, Naverage, NULL);
    if (!average) {
      fprintf (stderr, "ERROR: failed to read averages\n");
      exit (2);
    }
    *format = DVO_FORMAT_INTERNAL;
    return (average);
  }

  CONVERT_FORMAT ("DVO_AVERAGE_ELIXIR", 	 ELIXIR, 	  Elixir);
  CONVERT_FORMAT ("DVO_AVERAGE_LONEOS", 	 LONEOS, 	  Loneos);
  CONVERT_FORMAT ("DVO_AVERAGE_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
  CONVERT_FORMAT ("DVO_AVERAGE_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_V1",          PS1_V1,          PS1_V1);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_V2",          PS1_V2,          PS1_V2);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_V3",          PS1_V3,          PS1_V3);
  CONVERT_FORMAT ("DVO_AVERAGE_PS1_REF",         PS1_REF,         PS1_REF);
# undef CONVERT_FORMAT

  fprintf (stderr, "table format unknown: %s\n", extname);

  *Naverage = 0;
  return (NULL);
}

int AverageToFtable (FTable *ftable, Average *average, off_t Naverage, char format, SecFilt *primary) {

# define FORMAT_CASE(FORMAT, TYPE) \
    case DVO_FORMAT_##FORMAT: { \
      Average_##TYPE *tmpAverage; \
      tmpAverage = AverageInternalTo_##TYPE (average, Naverage, primary); \
      gfits_table_set_Average_##TYPE (ftable, tmpAverage, Naverage); \
      free (tmpAverage); \
      break; }

  /* convert from the internal format */
  switch (format) {
    case DVO_FORMAT_INTERNAL: {
      gfits_table_set_Average (ftable, average, Naverage);
      break; }

      FORMAT_CASE (ELIXIR, 	    Elixir);
      FORMAT_CASE (LONEOS, 	    Loneos);
      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
      FORMAT_CASE (PS1_V1,          PS1_V1);
      FORMAT_CASE (PS1_V2,          PS1_V2);
      FORMAT_CASE (PS1_V3,          PS1_V3);
      FORMAT_CASE (PS1_REF,         PS1_REF);
# undef FORMAT_CASE

    default:
      fprintf (stderr, "table format unknown (average)\n");
      return (FALSE);
  }
  return (TRUE);
}

/*** Measure / FTable conversion functions ***/

Measure *FtableToMeasure (FTable *ftable, off_t *Nmeasure, char *format) {

  Measure *measure;
  char extname[80];

  /* convert to the internal format */
  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
    fprintf (stderr, "EXTNAME missing for measure table\n");
    return (FALSE);
  }

# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
  if (!strcmp (extname, NAME)) { \
    Measure_##TYPE *tmpMeasure; \
    tmpMeasure = gfits_table_get_Measure_##TYPE (ftable, Nmeasure, NULL); \
    if (!tmpMeasure) { \
      fprintf (stderr, "ERROR: failed to read measures\n"); \
      exit (2); \
    } \
    measure = Measure_##TYPE##_ToInternal (tmpMeasure, *Nmeasure); \
    free (tmpMeasure); \
    *format = DVO_FORMAT_##FORMAT; \
    return (measure); }

  if (!strcmp (extname, "DVO_MEASURE")) {
    measure = gfits_table_get_Measure (ftable, Nmeasure, NULL);
    if (!measure) {
      fprintf (stderr, "ERROR: failed to read measures\n");
      exit (2);
    }
    *format = DVO_FORMAT_INTERNAL;
    return (measure);
  }

  CONVERT_FORMAT ("DVO_MEASURE_ELIXIR", 	 ELIXIR, 	  Elixir);
  CONVERT_FORMAT ("DVO_MEASURE_LONEOS", 	 LONEOS,          Loneos);
  CONVERT_FORMAT ("DVO_MEASURE_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
  CONVERT_FORMAT ("DVO_MEASURE_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
  CONVERT_FORMAT ("DVO_MEASURE_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
  CONVERT_FORMAT ("DVO_MEASURE_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
  CONVERT_FORMAT ("DVO_MEASURE_PS1_V1",          PS1_V1,          PS1_V1);
  CONVERT_FORMAT ("DVO_MEASURE_PS1_V2",          PS1_V2,          PS1_V2);
  CONVERT_FORMAT ("DVO_MEASURE_PS1_V3",          PS1_V3,          PS1_V3);
  CONVERT_FORMAT ("DVO_MEASURE_PS1_REF",         PS1_REF,         PS1_REF);
# undef CONVERT_FORMAT

  fprintf (stderr, "table format unknown: %s\n", extname);

  *Nmeasure = 0;
  return (NULL);
}

int MeasureToFtable (FTable *ftable, Measure *measure, off_t Nmeasure, char format) {

# define FORMAT_CASE(FORMAT, TYPE) \
    case DVO_FORMAT_##FORMAT: { \
      Measure_##TYPE *tmpMeasure; \
      tmpMeasure = MeasureInternalTo_##TYPE (measure, Nmeasure); \
      gfits_table_set_Measure_##TYPE (ftable, tmpMeasure, Nmeasure); \
      free (tmpMeasure); \
      break; }

  /* convert from the internal format */
  switch (format) {
    case DVO_FORMAT_INTERNAL: {
      gfits_table_set_Measure (ftable, measure, Nmeasure);
      break; }

      FORMAT_CASE (ELIXIR, 	    Elixir);
      FORMAT_CASE (LONEOS, 	    Loneos);
      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
      FORMAT_CASE (PS1_V1,          PS1_V1);
      FORMAT_CASE (PS1_V2,          PS1_V2);
      FORMAT_CASE (PS1_V3,          PS1_V3);
      FORMAT_CASE (PS1_REF,         PS1_REF);
# undef FORMAT_CASE

    default:
      fprintf (stderr, "table format unknown (measure)\n");
      return (FALSE);
  }
  return (TRUE);
}

/*** SecFilt / FTable conversion functions ***/

SecFilt *FtableToSecFilt (FTable *ftable, off_t *Nsecfilt, char *format) {

  SecFilt *secfilt;
  char extname[80];

  /* convert to the internal format */
  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
    fprintf (stderr, "EXTNAME missing for secfilt table\n");
    return (FALSE);
  }

# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
  if (!strcmp (extname, NAME)) { \
    SecFilt_##TYPE *tmpSecFilt; \
    tmpSecFilt = gfits_table_get_SecFilt_##TYPE (ftable, Nsecfilt, NULL); \
    if (!tmpSecFilt) { \
      fprintf (stderr, "ERROR: failed to read secfilts\n"); \
      exit (2); \
    } \
    secfilt = SecFilt_##TYPE##_ToInternal (tmpSecFilt, *Nsecfilt); \
    free (tmpSecFilt); \
    *format = DVO_FORMAT_##FORMAT; \
    return (secfilt); }

  if (!strcmp (extname, "DVO_SECFILT")) {
    secfilt = gfits_table_get_SecFilt (ftable, Nsecfilt, NULL);
    if (!secfilt) {
      fprintf (stderr, "ERROR: failed to read secfilts\n");
      exit (2);
    }
    *format = DVO_FORMAT_INTERNAL;
    return (secfilt);
  }

  CONVERT_FORMAT ("DVO_SECFILT_ELIXIR", 	 ELIXIR, 	  Elixir);
  CONVERT_FORMAT ("DVO_SECFILT_LONEOS", 	 LONEOS, 	  Loneos);
  CONVERT_FORMAT ("DVO_SECFILT_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
  CONVERT_FORMAT ("DVO_SECFILT_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
  CONVERT_FORMAT ("DVO_SECFILT_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
  CONVERT_FORMAT ("DVO_SECFILT_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
  CONVERT_FORMAT ("DVO_SECFILT_PS1_V1",          PS1_V1,          PS1_V1);
  CONVERT_FORMAT ("DVO_SECFILT_PS1_V2",          PS1_V2,          PS1_V2);
  CONVERT_FORMAT ("DVO_SECFILT_PS1_V3",          PS1_V3,          PS1_V3);
  CONVERT_FORMAT ("DVO_SECFILT_PS1_REF",         PS1_REF,         PS1_REF);
# undef CONVERT_FORMAT

  fprintf (stderr, "table format unknown: %s\n", extname);

  *Nsecfilt = 0;
  return (NULL);
}

int SecFiltToFtable (FTable *ftable, SecFilt *secfilt, off_t Nsecfilt, char format) {

# define FORMAT_CASE(FORMAT, TYPE) \
    case DVO_FORMAT_##FORMAT: { \
      SecFilt_##TYPE *tmpSecFilt; \
      tmpSecFilt = SecFiltInternalTo_##TYPE (secfilt, Nsecfilt); \
      gfits_table_set_SecFilt_##TYPE (ftable, tmpSecFilt, Nsecfilt); \
      free (tmpSecFilt); \
      break; }

  /* convert from the internal format */
  switch (format) {
    case DVO_FORMAT_INTERNAL: {
      gfits_table_set_SecFilt (ftable, secfilt, Nsecfilt);
      break; }

      FORMAT_CASE (ELIXIR, 	    Elixir);
      FORMAT_CASE (LONEOS, 	    Loneos);
      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
      FORMAT_CASE (PS1_V1,          PS1_V1);
      FORMAT_CASE (PS1_V2,          PS1_V2);
      FORMAT_CASE (PS1_V3,          PS1_V3);
      FORMAT_CASE (PS1_REF,         PS1_REF);
# undef FORMAT_CASE

    default:
      fprintf (stderr, "table format unknown (secfilt)\n");
      return (FALSE);
  }
  return (TRUE);
}

/*** Image Conversions ***/

int FtableToImage (FTable *ftable, Header *theader, char *format) {

  off_t Nimage;
  char extname[80];

  /* extname may be set from outside if the source is RAW not MEF */
  if (*format == DVO_FORMAT_ELIXIR) {		  // special case for ELIXIR
    Image_Elixir *tmpimage;
    tmpimage = gfits_table_get_Image_Elixir (ftable, &Nimage, NULL);
    if (!tmpimage) {
      fprintf (stderr, "ERROR: failed to read images\n");
      exit (2);
    }
    ftable[0].buffer = (char *) Image_Elixir_ToInternal (tmpimage, Nimage);
    free (tmpimage);
    gfits_free_header (theader);
    gfits_table_mkheader_Image (theader);
    gfits_modify (theader, "NAXIS2", OFF_T_FMT, 1,  Nimage);
    theader[0].Naxis[1] = Nimage;
    ftable[0].datasize = gfits_data_size (theader);
    return (TRUE);
  }

  /* convert to the internal format */
  if (!gfits_scan (ftable[0].header, "EXTNAME", "%s", 1, extname)) {
    fprintf (stderr, "EXTNAME missing for image table\n");
    return (FALSE);
  }

# define CONVERT_FORMAT(NAME, FORMAT, TYPE) \
  if (!strcmp (extname, NAME)) { \
    Image_##TYPE *tmpimage; \
    *format = DVO_FORMAT_##FORMAT; \
    tmpimage = gfits_table_get_Image_##TYPE (ftable, &Nimage, NULL); \
    if (!tmpimage) { \
      fprintf (stderr, "ERROR: failed to read images\n"); \
      exit (2); \
    } \
    ftable[0].buffer = (char *) Image_##TYPE##_ToInternal (tmpimage, Nimage); \
    free (tmpimage); \
    gfits_free_header (theader); \
    gfits_table_mkheader_Image (theader); \
    gfits_modify (theader, "NAXIS2", OFF_T_FMT, 1,  Nimage); \
    theader[0].Naxis[1] = Nimage; \
    ftable[0].datasize = gfits_data_size (theader); \
    return (TRUE); }

  if (!strcmp (extname, "DVO_IMAGE")) {
    Image *image;
    image = gfits_table_get_Image (ftable, &Nimage, NULL);
    if (!image) {
      fprintf (stderr, "ERROR: failed to read images\n");
      exit (2);
    }
    *format = DVO_FORMAT_INTERNAL;
    return (TRUE);
  }

  CONVERT_FORMAT ("DVO_IMAGE_ELIXIR", 	       ELIXIR,          Elixir);
  CONVERT_FORMAT ("DVO_IMAGE_LONEOS", 	       LONEOS,          Loneos);
  CONVERT_FORMAT ("DVO_IMAGE_PANSTARRS_DEV_0", PANSTARRS_DEV_0, Panstarrs_DEV_0);
  CONVERT_FORMAT ("DVO_IMAGE_PANSTARRS_DEV_1", PANSTARRS_DEV_1, Panstarrs_DEV_1);
  CONVERT_FORMAT ("DVO_IMAGE_PS1_DEV_1",       PS1_DEV_1,       PS1_DEV_1);
  CONVERT_FORMAT ("DVO_IMAGE_PS1_DEV_2",       PS1_DEV_2,       PS1_DEV_2);
  CONVERT_FORMAT ("DVO_IMAGE_PS1_V1",          PS1_V1,          PS1_V1);
  CONVERT_FORMAT ("DVO_IMAGE_PS1_V2",          PS1_V2,          PS1_V2);
  CONVERT_FORMAT ("DVO_IMAGE_PS1_V3",          PS1_V3,          PS1_V3);
  CONVERT_FORMAT ("DVO_IMAGE_PS1_REF",         PS1_REF,         PS1_REF);

# undef CONVERT_FORMAT

  fprintf (stderr, "table format unknown: %s\n", extname);
  return (FALSE);
}

int ImageToFtable (FTable *ftable, Header *theader, char format) {

  off_t Nimage;

  Nimage = theader[0].Naxis[1];

# define FORMAT_CASE(FORMAT, TYPE) \
    case DVO_FORMAT_##FORMAT: { \
      Image_##TYPE *tmpImage; \
      tmpImage = ImageInternalTo_##TYPE ((Image *) ftable[0].buffer, Nimage); \
      free (ftable[0].buffer); \
      gfits_table_set_Image_##TYPE (ftable, tmpImage, Nimage); \
      free (tmpImage); \
      break; }

  /* convert from the internal format */
  switch (format) {
    case DVO_FORMAT_INTERNAL: {
      gfits_convert_Image ((Image *) ftable[0].buffer, sizeof(Image), Nimage);
      break; }

      FORMAT_CASE (ELIXIR, 	    Elixir);
      FORMAT_CASE (LONEOS, 	    Loneos);
      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
      FORMAT_CASE (PS1_V1,          PS1_V1);
      FORMAT_CASE (PS1_V2,          PS1_V2);
      FORMAT_CASE (PS1_V3,          PS1_V3);
      FORMAT_CASE (PS1_REF,         PS1_REF);

# undef FORMAT_CASE

    default:
      fprintf (stderr, "table format unknown (image ftable)\n");
      return (FALSE);
  }
  return (TRUE);
}

int ImageToVtable (VTable *vtable, Header *theader, char format) {

  off_t i, Nrow, Nimage;

  Nrow = vtable[0].Nrow;

# define FORMAT_CASE(FORMAT, TYPE) \
  case DVO_FORMAT_##FORMAT: { \
      Image_##TYPE *tmpImage; \
      /* convert table rows from internal to external format */ \
      for (i = 0; i < Nrow; i++) { \
	  tmpImage = ImageInternalTo_##TYPE ((Image *) vtable[0].buffer[i], 1); \
	  gfits_convert_Image_##TYPE (tmpImage, sizeof(Image_##TYPE), 1); \
	  free (vtable[0].buffer[i]); \
	  vtable[0].buffer[i] = (char *) tmpImage; \
      } \
      /* convert header from old format to new format */ \
      gfits_scan (theader, "NAXIS2", OFF_T_FMT, 1,  &Nimage); \
      gfits_free_header (theader); \
      gfits_table_mkheader_Image_##TYPE (theader); \
      gfits_modify (theader, "NAXIS2", OFF_T_FMT, 1,  Nimage); \
      theader[0].Naxis[1] = Nimage; \
      vtable[0].datasize = gfits_data_size (theader); \
      return (TRUE); }


  /* convert from the internal format */
  switch (format) {
    case DVO_FORMAT_INTERNAL: {
      for (i = 0; i < Nrow; i++) {
	gfits_convert_Image ((Image *) vtable[0].buffer[i], sizeof(Image), 1);
      }
      return (TRUE); }

      FORMAT_CASE (ELIXIR, 	    Elixir);
      FORMAT_CASE (LONEOS, 	    Loneos);
      FORMAT_CASE (PANSTARRS_DEV_0, Panstarrs_DEV_0);
      FORMAT_CASE (PANSTARRS_DEV_1, Panstarrs_DEV_1);
      FORMAT_CASE (PS1_DEV_1,       PS1_DEV_1);
      FORMAT_CASE (PS1_DEV_2,       PS1_DEV_2);
      FORMAT_CASE (PS1_V1,          PS1_V1);
      FORMAT_CASE (PS1_V2,          PS1_V2);
      FORMAT_CASE (PS1_V3,          PS1_V3);
      FORMAT_CASE (PS1_REF,         PS1_REF);

# undef FORMAT_CASE

    default:
      break;
  }
  fprintf (stderr, "table format unknown (image vtable)\n");
  return (FALSE);
}

