/* -*- 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) 2007-2014 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 <muse.h>
#include <string.h>

#include <muse_instrument.h>
#include "muse_data_format_z.h"

/*----------------------------------------------------------------------------*/
/**
 * @defgroup tools_musebpfromascii          Tool muse_badpix_from_ascii
 *
 * <b>muse_badpix_from_ascii</b>: Convert an ASCII list to a BADPIX_TABLE.
 *
 * Create a PADPIX_TABLE file from the pixels listed in the ASCII file.
 * The ASCII file has to take the form
 *    xpos ypos dq
 * where the coordinates have to be within a MUSE raw image (or the trimmed
 * image, if <tt>-t</tt> is given), and the data quality value has to conform to
 * the Euro3D specification, i.e. it has to be an integer flag between 1 and
 * 16384 (see Sect. 2.4 of the DRL Design document).
 * Optionally, it merges the DQ information into an existing BADPIX_TABLE.
 *
 * <b>Command line arguments:</b>
 *   - <tt>-i INTABLE</tt> (optional)\n
 *     do not create a new BADPIX_TABLE but start from this one, i.e. either
 *     merge the table of the relevant IFU, or append a new one, if this IFU
 *     is not yet stored in the input file
 *   - <tt>-r REFIMAGE</tt> (optional)\n
 *     the filename of an optional referenc FITS image file; it only uses
 *     detector-specific header information from this file to set the respective
 *     detector parameters in the output BADPIX_TABLE extension
 *   - <tt>-n extname</tt> (optional)\n
 *     extension name to look for when reading REFIMAGE, to load additional
 *     FITS headers containing detector properties
 *   - <tt>-t</tt> (optional)
 *     coordinates in the ASCIIFILE contents refer to trimmed data, i.e. this
 *     program has to add pre- and overscans before writing the values to the
 *     table
 *   - <tt><b>ASCIIFILE</b></tt>\n
 *     input ASCII file to load
 *   - <tt><b>IFUNUM</b></tt>\n
 *     IFU number, has to be between 1 and 24
 *   - <tt><b>OUTTABLE</b></tt>\n
 *     the filename of the output BADPIX_TABLE file to use
 *
 * <b>Return values:</b>
 *   - <tt> 0</tt>\n   Success
 *   - <tt> 1</tt>\n   less than 3 arguments given
 *   - <tt> 2</tt>\n   argument <tt>-i</tt> without filename
 *   - <tt> 3</tt>\n   argument <tt>-r</tt> without filename
 *   - <tt> 4</tt>\n   argument <tt>IFUNUM</tt> is not an integer in the valid
 *                     range
 *   - <tt> 5</tt>\n   no extension name given after <tt>-n</tt>
 *   - <tt> 9</tt>\n   unknown option given
 *   - <tt>10</tt>\n   input ASCII file could not be read
 */
/*----------------------------------------------------------------------------*/

/**@{*/

#define PRINT_USAGE(rc)                                                        \
  fprintf(stderr, "Usage: %s [ -i INTABLE ] [ -r REFIMAGE [ -n extname ] ] "   \
          "[ -t ] ASCIIFILE IFUNUM OUTTABLE\n", argv[0]);                      \
  cpl_end(); return (rc);

int main(int argc, char **argv)
{
  cpl_init(CPL_INIT_DEFAULT);
  muse_processing_recipeinfo(NULL);
  if (argc <= 3) {
    /* two filenames plus IFU number are needed at least */
    PRINT_USAGE(1);
  }

  if (getenv("ESOREX_MSG_LEVEL") && !strncmp(getenv("ESOREX_MSG_LEVEL"),
                                             "debug", 6)) {
    cpl_msg_set_level(CPL_MSG_DEBUG);
  }

  char *fname = NULL, /* input ASCII file */
       *toname = NULL, /* output table */
       *riname = NULL, /* optional reference image */
       *tiname = NULL, /* optional input table */
       *extname = NULL; /* optional extension name */
  unsigned char nifu = 0; /* IFU number */
  cpl_boolean trimmedcoords = CPL_FALSE;

  /* argument processing */
  int i;
  for (i = 1; i < argc; i++) {
    if (strncmp(argv[i], "-i", 3) == 0) {
      /* skip to next arg to input table filename */
      i++;
      if (i < argc) {
        tiname = argv[i];
      } else {
        PRINT_USAGE(2);
      }
    } else if (strncmp(argv[i], "-r", 3) == 0) {
      /* skip to next arg to input image filename */
      i++;
      if (i < argc) {
        riname = argv[i];
      } else {
        PRINT_USAGE(3);
      }
    } else if (strncmp(argv[i], "-n", 3) == 0) {
      /* skip to next arg to get sigma value */
      i++;
      if (i < argc) {
        extname = argv[i];
      } else {
        PRINT_USAGE(5);
      }
    } else if (strncmp(argv[i], "-t", 3) == 0) {
      /* skip to next arg to get sigma value */
      trimmedcoords = CPL_TRUE;
    } else if (strncmp(argv[i], "-", 1) == 0) { /* unallowed options */
      PRINT_USAGE(9);
    } else {
      if (fname && toname) {
        break; /* we have the possible names, skip the rest */
      }
      if (!fname) {
        fname = argv[i]; /* set the name for the input ASCII file */
      } else if (nifu == 0) {
        int ifunum = atoi(argv[i]);
        if (ifunum < 1 || ifunum > kMuseNumIFUs) {
          PRINT_USAGE(9);
        }
        nifu = ifunum;
      } else {
        toname = argv[i]; /* set the name for the output table */
      }
    }
  } /* for i (all arguments) */

  /* open the ASCII input file to verify its existence */
  FILE *fp = fopen(fname, "r");
  if (!fp) {
    PRINT_USAGE(10);
  }

  /* part1: ======================================= *
   * convert the ASCII file into a new table        */
  if (trimmedcoords) {
    printf("Converting coordinates from trimmed to raw data dimensions\n");
  }
#define START_SIZE 10000
  cpl_table *table = muse_cpltable_new(muse_badpix_table_def, START_SIZE);
  cpl_size irow = 0, nrow = cpl_table_get_nrow(table);
  int ix, iy, idq;
  while (fscanf(fp, "%d %d %d", &ix, &iy, &idq) == 3) {
    if (irow >= nrow) {
      cpl_table_set_size(table, nrow + START_SIZE);
      nrow = cpl_table_get_nrow(table);
    } /* if index too large */
    int iorig = ix, /* keep values before converting to un-trimmed */
        jorig = iy;
    if (trimmedcoords) {
      muse_quadrants_coords_to_raw(NULL, &ix, &iy);
    }
    if (ix < 1 || ix > (kMuseOutputXRight + 4*kMusePreOverscanSize) ||
        iy < 1 || iy > (kMuseOutputYTop + 4*kMusePreOverscanSize)) {
      fprintf(stderr, "Excluding bad pixel at given %d,%d %s coordinates, as "
              "it would be outside the MUSE CCD!\n", iorig, jorig,
              trimmedcoords ? "trimmed" : "raw");
        continue;
      } /* if outside */
    cpl_table_set_int(table, MUSE_BADPIX_X, irow, ix);
    cpl_table_set_int(table, MUSE_BADPIX_Y, irow, iy);
    cpl_table_set_int(table, MUSE_BADPIX_DQ, irow++, idq);
  } /* while lines to scan */
  cpl_table_set_size(table, irow);
  printf("%"CPL_SIZE_FORMAT" bad pixel%s read from \"%s\"\n", irow,
         irow != 1 ? "s" : "", fname);
  fclose(fp);
  if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
    printf("first 10 entries of the new table, as converted from ASCII:\n");
    cpl_table_dump(table, 0, 10, stdout);
    fflush(stdout);
  }

  /* part2: ============================================ *
   * load and handle the input badpix table if it exists */
  if (tiname) { /* try to open the input table, verify that it really exists */
    cpl_propertylist *htest = cpl_propertylist_load(tiname, 0);
    if (htest) {
      cpl_propertylist_delete(htest);
    } else {
      printf("WARNING: could not open input table \"%s\"!\n", tiname);
      tiname = NULL; /* it doesn't exist, don't keep the name */
    } /* else */
  } /* if tiname */

  /* merge created table with existing one, if there is one */
  char *chan = cpl_sprintf("CHAN%02hhu", nifu);
  cpl_table *intable = NULL;
  int inext = -1;
  if (tiname) {
    cpl_errorstate state = cpl_errorstate_get();
    intable = muse_quality_merge_badpix_from_file(table, tiname, chan, &inext);
    if (!intable) {
      cpl_errorstate_set(state);
      intable = table;
    } /* if !intable */
  } /* if tiname */
  /* copy the input BADPIX_TABLE, maybe replacing the one extension we modified */
  cpl_error_code rc = muse_quality_copy_badpix_table(tiname, toname, inext,
                                                     intable);
  cpl_boolean savedsomething = rc == CPL_ERROR_NONE;

  /* save or append the extension if not already done above */
  inext = cpl_fits_find_extension(toname, chan);
  if (inext <= 0) {
    cpl_propertylist *pheader = NULL,
                     *header = NULL;
    if (!riname) {
      printf("WARNING: no pre-existing data, creating minimal header from "
             "scratch!\n");
      pheader = cpl_propertylist_new();
      cpl_propertylist_append_string(pheader, "TELESCOP", "ESO-VLT-U4");
      cpl_propertylist_append_string(pheader, "INSTRUME", "MUSE");
      cpl_propertylist_append_string(pheader, "OBJECT",
                                     "Bad pixel table for MUSE (BADPIX_TABLE)");
      header = cpl_propertylist_new();
      cpl_propertylist_append_string(header, "EXTNAME", chan);
      cpl_propertylist_append_string(header, "ESO DET CHIP NAME", chan);
    } else {
      printf("Using primary header from \"%s\" as starting point\n", riname);
      pheader = cpl_propertylist_load_regexp(riname, 0, "TELESCOP|INSTRUME|"
                                             "ESO DET ", 0);
      /* remove exposure-specifc info and the stuff about     *
       * CHIP and OUTi again, that belongs into the extension */
      cpl_propertylist_erase_regexp(pheader, "ESO DET DEV[0-9] (SHUT |EXP )|"
                                    "ESO DET (EXP |[DU]IT|NDIT|DKTM)", 0);
      cpl_propertylist_erase_regexp(pheader, "ESO DET (CHIP |OUT[1-4])", 0);
      cpl_propertylist_update_string(pheader, "OBJECT",
                                     "Bad pixel table for MUSE (BADPIX_TABLE)");
      int refext = cpl_fits_find_extension(riname, extname);
      if (refext >= 0) {
        printf("Using extension header from \"%s[%s]\" (%d)\n", riname, extname,
               refext);
        header = cpl_propertylist_load_regexp(riname, refext,
                                              "^EXT|ESO DET (CHIP |OUT[1-4])", 0);
      } else {
        printf("WARNING: no pre-existing extension found, creating minimal "
               "extension header from scratch!\n");
        header = cpl_propertylist_new();
        cpl_propertylist_append_string(header, "EXTNAME", chan);
        cpl_propertylist_append_string(header, "ESO DET CHIP NAME", chan);
      }
    }
    if (savedsomething) {
      /* just extend the already saved data with the new extension */
      rc = cpl_table_save(table, NULL, header, toname, CPL_IO_EXTEND);
    } else {
      /* save the whole file, overwriting what may be there under that name */
      cpl_propertylist_update_string(pheader, "PIPEFILE", toname);
      cpl_propertylist_set_comment(pheader, "PIPEFILE",
                                   "pretend to be a pipeline output file");
      cpl_propertylist_update_string(pheader, "ESO PRO CATG", MUSE_TAG_BADPIX_TABLE);
      cpl_propertylist_set_comment(pheader, "ESO PRO CATG",
                                   "MUSE bad pixel table");
      rc = cpl_table_save(table, pheader, header, toname, CPL_IO_CREATE);
    }
    if (rc != CPL_ERROR_NONE) {
      fprintf(stderr, "Saving to \"%s\" failed (rc=%d): %s\n", toname, rc,
              cpl_error_get_message());
    } else {
      printf("Saved to \"%s\" (%s)\n", toname,
             savedsomething ? "new extension" : "new file");
    }
    cpl_propertylist_delete(pheader);
    cpl_propertylist_delete(header);
  } /* if new table now saved yet */
  cpl_free(chan);
  cpl_table_delete(table);

  if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
    printf("Output file \"%s\" has primary header and %"CPL_SIZE_FORMAT
           " extensions\n", toname, cpl_fits_count_extensions(toname));
    cpl_errorstate_dump(0, CPL_FALSE, NULL);
    cpl_memory_dump();
    fflush(NULL);
  }
  cpl_end();
  return 0;
}

/**@}*/
