/* -*- 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) 2016 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.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/*----------------------------------------------------------------------------*
 *                             Includes                                       *
 *----------------------------------------------------------------------------*/
#include <cpl.h>

#include "muse_table.h"

#include "muse_pfits.h"
#include "muse_utils.h"

/*----------------------------------------------------------------------------*/
/**
 * @defgroup muse_table        Table handling
 *
 * This module contains a simple wrapper around the cpl_table object, so that we
 * can easily pass a table together with its properties (i.e. its primary FITS
 * header) around to between functions.
 */
/*----------------------------------------------------------------------------*/
/**@{*/

/*----------------------------------------------------------------------------*/
/**
  @brief    Allocate memory for a new <tt><b>muse_table</b></tt> object.
  @return   a new <tt><b>muse_table *</b></tt> or @c NULL on error
  @remark   The returned object has to be deallocated using
            <tt><b>muse_table_delete()</b></tt>.
  @remark   This function does not allocate the contents of the elements,
            these have to be allocated with @c cpl_table_new() or
            @c cpl_propertylist_new(), respectively, or equivalent functions.

  Simply allocate memory to store the pointers of the <tt><b>muse_table</b></tt>
  structure.
 */
/*----------------------------------------------------------------------------*/
muse_table *
muse_table_new(void)
{
  return (muse_table *)cpl_calloc(1, sizeof(muse_table));
} /* muse_table_new() */

/*----------------------------------------------------------------------------*/
/**
  @brief    Deallocate memory associated to a muse_table object.
  @param    aTable   input MUSE table

  Just calls @c cpl_table_delete() and @c cpl_propertylist_delete() for the
  four components of a <tt><b>muse_table</b></tt>, and frees memory for the
  aTable pointer. As a safeguard, it checks if a valid pointer was passed,
  so that crashes cannot occur.
 */
/*----------------------------------------------------------------------------*/
void
muse_table_delete(muse_table *aTable)
{
  if (!aTable) {
    return;
  }
  cpl_table_delete(aTable->table);
  cpl_propertylist_delete(aTable->header);
  cpl_free(aTable);
} /* muse_table_delete() */

/*----------------------------------------------------------------------------*/
/**
  @brief    Load a table file and its primary FITS header.
  @param    aFilename   name of the file to load
  @param    aIFU        MUSE IFU to load the data for (0 for generic tables).
  @return   a new <tt><b>muse_table *</b></tt> or NULL on error
  @remark   The new table has to be deallocated using
            <tt><b>muse_table_delete()</b></tt>.
  @remark   Only FITS keywords from the primary FITS header will be loaded into
            the <tt>header</tt> component.

  @error{set CPL_ERROR_NULL_INPUT\, return NULL, aFilename is NULL}
  @error{return NULL\, propagate CPL error code, cpl_table_load() fails}
 */
/*----------------------------------------------------------------------------*/
muse_table *
muse_table_load(const char *aFilename, unsigned char aIFU)
{
  cpl_ensure(aFilename, CPL_ERROR_NULL_INPUT, NULL);

  /* try to load the extension table for the given IFU */
  int extension = muse_utils_get_extension_for_ifu(aFilename, aIFU);
  /* other tables hopefully always have the data in the first extension */
  if (extension <= 0) {
    if (aIFU > 0) {
      cpl_msg_debug(__func__, "Didn't find a specific extension for IFU %hhu, "
                    "will just use the first one.", aIFU);
    }
    extension = 1;
  }
  /* load the table, checking for invalid entries */
  cpl_errorstate state = cpl_errorstate_get();
  muse_table *table = muse_table_new();
  table->table = cpl_table_load(aFilename, extension, 1);
  if (!cpl_errorstate_is_equal(state) || !table->table ||
      !cpl_table_get_nrow(table->table)) {
    cpl_msg_info(__func__, "Loading table from file \"%s\" (ext %d) failed: %s",
                 aFilename, extension, cpl_error_get_message());
    muse_table_delete(table);
    return NULL;
  }
  /* load the primary FITS header; *
   * If loading of the table above works, this is extremely unlikely to fail, so do ) */
  table->header = cpl_propertylist_load(aFilename, 0);

  /* now output a message, use the relevant EXTNAME, if it exists */
  cpl_propertylist *hext = cpl_propertylist_load(aFilename, extension);
  state = cpl_errorstate_get();
  const char *en = muse_pfits_get_extname(hext);
  char *extname = NULL;
  if (en && cpl_errorstate_is_equal(state)) {
    extname = cpl_sprintf("[%s]", en);
    /* add the EXTNAME in the table header component, if not existing yet */
    if (!cpl_propertylist_has(table->header, "EXTNAME")) {
      cpl_propertylist_append_string(table->header, "EXTNAME", en);
      cpl_propertylist_set_comment(table->header, "EXTNAME",
                                   cpl_propertylist_get_comment(hext, "EXTNAME"));
    } /* if no extname yet */
  } else {
    cpl_errorstate_set(state);
    extname = cpl_sprintf("%c", '\0');
  }
  cpl_msg_info(__func__, "Loaded table from file \"%s%s\" (ext %d).", aFilename,
               extname, extension);
  cpl_free(extname);
  cpl_propertylist_delete(hext);

  return table;
} /* muse_table_load() */

/*----------------------------------------------------------------------------*/
/**
  @brief    Save the data and the FITS headers of a MUSE table to a file.
  @param    aTable      input MUSE table
  @param    aFilename   name of the output file
  @return   CPL_ERROR_NONE or the relevant cpl_error_code on error
  @remark   The primary headers of the output file will be constructed from
            the header component of the <tt><b>muse_table</b></tt> structure.

  @error{return CPL_ERROR_NULL_INPUT, aImage or aFilename are NULL}
  @error{return CPL error code, failure to save any of the components}
 */
/*----------------------------------------------------------------------------*/
cpl_error_code muse_table_save(muse_table *aTable, const char *aFilename)
{
  cpl_ensure_code(aTable && aFilename, CPL_ERROR_NULL_INPUT);

  cpl_error_code rc = cpl_table_save(aTable->table, aTable->header,
                                     NULL, aFilename, CPL_IO_CREATE);
  return rc;
} /* muse_table_save */

/**@}*/
