/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set sw=2 sts=2 et cin: */
/*
 * This file is part of the MUSE Instrument Pipeline
 * Copyright (C) 2015 European Southern Observatory
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <string.h>
#include <muse.h>

/*----------------------------------------------------------------------------*/
/**
 * @defgroup tools_museproductsplit         Tool muse_product_split
 *
 * <b>muse_product_split</b>: Split a merged product, i.e. separate all IFUs.
 *
 * The output filename is constructed by adding the IFU numbers just in front
 * of the last dot of the input filename, i.e. MASTER_BIAS_bla.fits becomes
 * MASTER_BIAS_bla-nn.fits where nn is replaced by 01 to 24.
 *
 * Since pixel tables cannot be merged, and their layout is not recognized (when
 * saved as image) or they are not accepted at all by this program (if saved as
 * table, due to missing extensions), they are automatically ignored.
 *
 * <b>Command line arguments:</b>
 *   - <tt>-s</tt> (optional)\n
 *     sign the split output products
 *   - <tt>-d</tt> (optional)\n
 *     delete the input file after splitting
 *   - <tt><b>FILENAME</b></tt>\n
 *     the filename of the (merged) MUSE pipeline product
 *
 * <b>Return values:</b>
 *   - <tt> 0</tt>\n   Success
 *   - <tt> 1</tt>\n   no input filename given
 *   - <tt> 9</tt>\n   unknown option given
 *   - <tt>10</tt>\n   input file has less than two extensions (cannot be split)
 *   - <tt>50</tt>\n   unknown error
 */
/*----------------------------------------------------------------------------*/

/**@{*/

#define PRINT_USAGE(rc)                                                        \
  fprintf(stderr, "Usage: %s [ -s ] [ -d ] FILENAME\n", argv[0]);              \
  cpl_end(); return (rc);

int main(int argc, char **argv)
{
  cpl_init(CPL_INIT_DEFAULT);
  muse_processing_recipeinfo(NULL);

  if (argc <= 1) {
    /* filename is needed */
    PRINT_USAGE(1);
  }

  /* argument processing */
  cpl_boolean dodelete = CPL_FALSE, /* do not delete input by default */
              dosign = CPL_FALSE; /* do not sign by default */
  char *iname = NULL; /* filename */
  int i;
  for (i = 1; i < argc; i++) {
    if (strncmp(argv[i], "-s", 3) == 0) {
      dosign = CPL_TRUE;
    } else if (strncmp(argv[i], "-d", 3) == 0) {
      dodelete = CPL_TRUE;
    } else if (strncmp(argv[i], "-", 1) == 0) { /* unallowed options */
      PRINT_USAGE(9);
    } else {
      if (iname) {
        break; /* we have the required name, skip the rest */
      }
      if (!iname) {
        iname = argv[i]; /* set the filename */
      }
    }
  } /* for i (all arguments) */

  cpl_errorstate state = cpl_errorstate_get();

  /* for a file to split, there need to be at least two extensions */
  int next = cpl_fits_count_extensions(iname);
  if (next < 2) {
    PRINT_USAGE(10);
  }
  cpl_propertylist *header = cpl_propertylist_load(iname, 0);
  const char *tag = muse_pfits_get_pro_catg(header);
  printf("Splitting \"%s\" (PRO.CATG %s, %d extensions).\n", iname, tag, next);

  /* loop through all extensions, and create a frameset with all split files */
  cpl_frameset *fset = cpl_frameset_new();
  /* first assume what we are not dealing with a merged     *
   * 3-extension MUSE image (that's a bit more complicated) */
  int iext;
  for (iext = 1; iext <= next; iext++) {
    /* read extension header */
    cpl_propertylist *hext = cpl_propertylist_load(iname, iext);
    /* see what type of object we have */
    unsigned char ifu = muse_utils_get_ifu(hext);
    const char *xtension = cpl_propertylist_get_string(hext, "XTENSION"),
               *extname = muse_pfits_get_extname(hext);
    if (!xtension) {
      fprintf(stderr, "Broken FITS extension (missing XTENSION, extension "
              "%d)\n", iext);
      cpl_propertylist_delete(hext);
      continue;
    }

    enum {
      IS_BINTABLE,
      IS_IMAGE,
      IS_CHANNEL_CUBE,
      IS_MUSE_IMAGE_DATA,
      IS_MUSE_IMAGE_DQ,
      IS_MUSE_IMAGE_STAT,
      IS_OTHER
    } exttype = IS_OTHER;
    if (!strncmp(xtension, "BINTABLE", 8)) {
      exttype = IS_BINTABLE;
    } else if (!strncmp(xtension, "IMAGE", 8)) {
      char *chan = cpl_sprintf("CHAN%02hhu", ifu),
           *chandata = cpl_sprintf("CHAN%02hhu.DATA", ifu),
           *chandq = cpl_sprintf("CHAN%02hhu.DQ", ifu),
           *chanstat = cpl_sprintf("CHAN%02hhu.STAT", ifu);
      if (extname && !strncmp(extname, chan, strlen(chan) + 1)) {
        exttype = IS_IMAGE;
      } else if (extname && !strncmp(extname, chandata, strlen(chandata) + 1)) {
        exttype = IS_MUSE_IMAGE_DATA;
      } else if (extname && !strncmp(extname, chandq, strlen(chandq) + 1)) {
        exttype = IS_MUSE_IMAGE_DQ;
      } else if (extname && !strncmp(extname, chanstat, strlen(chanstat) + 1)) {
        exttype = IS_MUSE_IMAGE_STAT;
      } else if (extname && /* here only compare to the number, not what's... */
                 !strncmp(extname, chan, strlen(chan)) &&        /* after it! */
                 muse_pfits_get_naxis(hext, 0) == 3) {
        exttype = IS_CHANNEL_CUBE;
      } else {
        exttype = IS_OTHER;
      }
      cpl_free(chan);
      cpl_free(chandata);
      cpl_free(chandq);
      cpl_free(chanstat);
    } /* image extension */

    /* create output filename, prefixing the extension with the IFU number */
    char *basefn = cpl_strdup(iname),
         *end = strstr(basefn, ".fits");
    if (end) {
      *end = '\0'; /* strip off ".fits" */
    }
    char *outfn = cpl_sprintf("%s-%02hhu.fits", basefn, ifu);
    cpl_free(basefn);

    /* now do the work to save the object */
    switch (exttype) {
    case IS_BINTABLE: {
      printf("\tbinary table (XTENSION=\'%s\', EXTNAME=\'%s\')\n", xtension,
             extname);
      /* for tables, just merge primary and extension header and load and save the table */
      cpl_table *table = cpl_table_load(iname, iext, 1);
      cpl_propertylist *hout = cpl_propertylist_duplicate(header);
      cpl_propertylist_append(hout, hext);
      if (strlen(extname) == 6) {
        /* the original extensions don't have any header, not even EXTNAME */
        cpl_table_save(table, hout, NULL, outfn, CPL_IO_CREATE);
      } else {
        cpl_propertylist *hextout = cpl_propertylist_new();
        /* find the part after the dot and use that for the output EXTNAME */
        char *extnew = strchr(extname, '.');
        if (extnew) {
          extnew += 1; /* go after the dot */
          cpl_propertylist_append_string(hextout, "EXTNAME", extnew);
        }
        cpl_table_save(table, hout, hextout, outfn, CPL_IO_CREATE);
        cpl_propertylist_delete(hextout);
      }
      cpl_table_delete(table);
      cpl_propertylist_delete(hout);
      break;
      }
    case IS_IMAGE: {
      printf("\tstandard image(XTENSION=\'%s\', EXTNAME=\'%s\')\n", xtension,
             extname);
      /* for normal images, just merge primary and extension header and load and save the image */
      cpl_image *image = cpl_image_load(iname, CPL_TYPE_UNSPECIFIED, 0, iext);
      cpl_propertylist *hout = cpl_propertylist_duplicate(header);
      cpl_propertylist_append(hout, hext);
      cpl_image_save(image, outfn, CPL_TYPE_UNSPECIFIED, hout, CPL_IO_CREATE);
      cpl_image_delete(image);
      cpl_propertylist_delete(hout);
      break;
      }
    case IS_CHANNEL_CUBE: { /* handle the case of a cube (LSF_PROFILE!) */
      printf("\tchannel cube (XTENSION=\'%s\', EXTNAME=\'%s\')\n", xtension,
             extname);
      /* for normal images, just merge primary and extension header and load and save the image */
      cpl_imagelist *list = cpl_imagelist_load(iname, CPL_TYPE_UNSPECIFIED, iext);
      cpl_propertylist *hout = cpl_propertylist_duplicate(header);
      /* leave part after channel number in the extension header */
      unsigned char dummy;
      char extnamerest[KEYWORD_LENGTH];
      sscanf(extname, "CHAN%02hhu.%s", &dummy, extnamerest);
#if 0
      printf("%s -> %02hhu and %s\n", extname, dummy, extnamerest);
#endif
      cpl_propertylist_update_string(hext, "EXTNAME", extnamerest);
      /* add channel number to primary header */
      char *chan = cpl_sprintf("CHAN%02hhu", ifu);
      cpl_propertylist_update_string(hout, "EXTNAME", chan);
      cpl_free(chan);
      cpl_propertylist_copy_property_regexp(hout, hext, "^ESO ", 0);
      cpl_propertylist_save(hout, outfn, CPL_IO_CREATE);
      cpl_propertylist_erase_regexp(hext, "^ESO ", 0);
      cpl_imagelist_save(list, outfn, CPL_TYPE_UNSPECIFIED, hext, CPL_IO_EXTEND);
      cpl_imagelist_delete(list);
      cpl_propertylist_delete(hout);
      break;
      }
    case IS_MUSE_IMAGE_DATA: {
      printf("\tMUSE image (XTENSION=\'%s\', EXTNAME=\'%s\')\n", xtension,
             extname);
      /* load all three extensions and them save them again */
      muse_image *image = muse_image_load_from_extensions(iname, ifu);
      muse_image_save(image, outfn);
      muse_image_delete(image);
      break;
      }
    case IS_MUSE_IMAGE_DQ:
    case IS_MUSE_IMAGE_STAT:
#if 0
      printf("MUSE OTHER (%s %s)\n", xtension, extname);
#endif
      cpl_propertylist_delete(hext);
      cpl_free(outfn);
      continue; /* silently skip this extension */
    default:
      fprintf(stderr, "\tUnsupported FITS extension (XTENSION=\'%s\', "
              "extension %d)\n", xtension, iext);
      cpl_propertylist_delete(hext);
      cpl_free(outfn);
      continue;
    }
    cpl_propertylist_delete(hext);

    /* now we can add the written file to the (output) frameset */
    cpl_frame *frame = cpl_frame_new();
    cpl_frame_set_filename(frame, outfn);
    cpl_free(outfn);
    cpl_frame_set_tag(frame, tag);
    cpl_frame_set_group(frame, CPL_FRAME_GROUP_PRODUCT);
    /* should be of no consequence to set these to "none" */
    cpl_frame_set_type(frame, CPL_FRAME_TYPE_NONE);
    cpl_frame_set_level(frame, CPL_FRAME_LEVEL_NONE);
#if 0
    printf("frame:\t");
    cpl_frame_dump(frame, stdout);
    fflush(stdout);
#endif
    cpl_frameset_insert(fset, frame);
  } /* for iext (all FITS extensions) */
  cpl_propertylist_delete(header);

  int nout = cpl_frameset_get_size(fset);
  if (dosign) {
    printf("Signing %d output product%s...\n", nout, nout == 0 ? "" : "s");
    cpl_dfs_sign_products(fset, CPL_DFS_SIGNATURE_DATAMD5 | CPL_DFS_SIGNATURE_CHECKSUM);
  }
  if (dodelete) {
    printf("Deleting input file \"%s\"...\n", iname);
    remove(iname);
  }

  printf("Split output product%s:\n", nout == 0 ? "" : "s");
  for (i = 0; i < nout; i++) {
    const cpl_frame *frame = cpl_frameset_get_position_const(fset, i);
    const char *fn = cpl_frame_get_filename(frame);
    next = cpl_fits_count_extensions(fn);
    printf("\t\"%s\" (%d FITS extensions)\n", fn, next);
  } /* for i (all output frames) */
  cpl_frameset_delete(fset);

  int rc = 0;
  if (!cpl_errorstate_is_equal(state)) {
    cpl_errorstate_dump(state, CPL_FALSE, muse_cplerrorstate_dump_some);
    rc = 50;
  }
  cpl_memory_dump(); /* only active when CPL_MEMORY_MODE != 0 */
  cpl_end();
  return rc;
}

/**@}*/
