/*
 * This file is part of the GIRAFFE Pipeline
 * Copyright (C) 2002-2019 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 St, Fifth Floor, Boston, MA  02110-1301  USA
 */

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

#include <cxmemory.h>
#include <cxmessages.h>
#include <cxstrutils.h>

#include <cpl_msg.h>
#include <cpl_parameterlist.h>

#include "giframe.h"
#include "gifiberutils.h"
#include "gifibers.h"


/**
 * @defgroup gifibers Fiber Selection
 *
 * TBD
 */

/**@{*/

/**
 * @brief
 *   Selects the spectra to process.
 *
 * @param frame      A frame containing an Ozpoz and a FLAMES fibers table.
 * @param reference  Slit geometry for the current setup defining active
 *                   fibers.
 * @param config     Setup structure containing the selection of fibers.
 *
 * @return The function returns the table containing the spectra/fibers to
 *   process on success, or @c NULL otherwise.
 *
 * The function creates a table of the fibers to be processed in subsequent
 * reduction steps from the Ozpoz and the FLAMES Fibers table in @em frame
 * and a selection of spectra given by @em config. The selection may be given
 * as the number of spectra (treated as a continuous range from 1 to the
 * given number), as a list of spectra, or from the column "FPS" of the
 * table @em reference. The final table will contains all the fibers that
 * satisfy all selection criteria.
 *
 * If @c NULL is passed as @em fiber_setup, or @em config the respective
 * parameter has no effect on the creation of the result table.
 *
 * The function expects that @em frame contains an OzPoz and a fiber table.
 * If this is not the case the function returns an error.
 */

GiTable *
giraffe_fibers_select(const cpl_frame *frame, const GiTable *reference,
                      GiFibersConfig *config)
{

    const cxchar *fctid = "giraffe_fibers_select";

    const cxchar *filename;

    cxint nspec = 0;
    cxint *spectra = NULL;

    cpl_table *_fibers;

    GiTable *fibers;


    if (!frame || !config) {
        return NULL;
    }

    filename = cpl_frame_get_filename(frame);
    cx_assert(filename != NULL);


    if (config->spectra && *config->spectra != '\0') {

        if (strcmp(config->spectra, "setup") == 0) {

            if (reference != NULL) {
                spectra = giraffe_create_spectrum_selection(filename, reference,
                                                            &nspec);
            }

            if (!spectra) {
                cpl_msg_error(fctid, "Invalid fiber setup!");
                return NULL;
            }

        }
        else {

            spectra = giraffe_parse_spectrum_selection(config->spectra,
                                                       &nspec);
            if (!spectra) {
                cpl_msg_error(fctid, "Invalid selection string `%s'!",
                              config->spectra);
                return NULL;
            }

        }

        if (config->nspec > 0) {

            /*
             * Both, the number of spectra and a selection list were
             * given
             */

            if (config->nspec < nspec) {

                spectra = cx_realloc(spectra,
                                     config->nspec * sizeof(cxint));
                nspec = config->nspec;

                cpl_msg_warning(fctid, "Requested number of spectra (%d) "
                                "is less than number of listed spectra "
                                "(%d). Using %d spectra.", config->nspec,
                                nspec, config->nspec);

            }
            else {
                if (config->nspec > nspec) {

                    cpl_msg_warning(fctid, "Number of requested spectra "
                                    "(%d) exceeds the number of listed "
                                    "spectra (%d). Using all spectra in "
                                    "the list!", config->nspec, nspec);

                }
            }
        }
    }
    else {

        if (config->nspec > 0) {

            /*
             * No selection list, but the number of spectra to process
             * was given.
             */

            register cxint i;

            nspec = config->nspec;
            spectra = cx_malloc(nspec * sizeof(cxint));

            /*
             * Fiber positions in the image are counted starting from 1!
             */

            for (i = 0; i < nspec; i++) {
                spectra[i] = i + 1;
            }
        }
    }

    _fibers = giraffe_fiberlist_create(filename, nspec, spectra);

    fibers = giraffe_table_new();
    giraffe_table_set(fibers, _fibers);

    cpl_table_delete(_fibers);


    /*
     * Cleanup
     */

    if (spectra) {
        cx_free(spectra);
    }

    return fibers;

}


/**
 * @brief
 *   Setup a fiber list.
 *
 * @param frame      Frame for which the fiber list is built.
 * @param reference  Frame containing the reference fiber setup.
 *
 * @return The function returns the created fiber list on success, or
 *   @c NULL if an error occurred. In the latter case an appropriate error
 *   code is also set.
 *
 * The function creates an initial fiber setup from the input frame
 * @em frame and associates it to the fiber setup read from the reference
 * frame @em reference.
 *
 * @see giraffe_fiberlist_create(), giraffe_fiberlist_associate(),
 */

GiTable *
giraffe_fibers_setup(const cpl_frame *frame, const cpl_frame *reference)
{

    const cxchar *fctid = "giraffe_fibers_setup";

    cxchar *filename = NULL;

    cpl_table *_fibers =NULL;

    GiTable *fibers = NULL;


    if (frame == NULL) {
        cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
        return NULL;
    }

    filename = (cxchar *)cpl_frame_get_filename(frame);

    if (filename == NULL) {
        cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
        return NULL;
    }

    _fibers = giraffe_fiberlist_create(filename, 0, NULL);

    if (_fibers == NULL) {
        return NULL;
    }

    fibers = giraffe_table_new();
    giraffe_table_set(fibers, _fibers);

    cpl_table_delete(_fibers);
    _fibers = NULL;


    /*
     * Associate the newly created fiber setup list with a reference list
     * if it was given.
     */

    if (reference != NULL) {

        cxint status;

        GiTable *rfibers = 0;


        filename = (cxchar *)cpl_frame_get_filename(reference);

        if (filename == NULL) {
            
            giraffe_table_delete(fibers);
            cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
            return NULL;
            
        }

        rfibers = giraffe_fiberlist_load(filename, 1, GIFRAME_FIBER_SETUP);

        if (rfibers == NULL) {

            giraffe_table_delete(fibers);
            return NULL;
            
        }

        status = giraffe_fiberlist_associate(fibers, rfibers);

        if (status) {
            giraffe_table_delete(fibers);
            giraffe_table_delete(rfibers);
            
            return NULL;
        }

        giraffe_table_delete(rfibers);

    }

    return fibers;

}


/**
 * @brief
 *   Creates a setup structure for the fiber selection.
 *
 * @param list  Parameter list from which the setup informations is read.
 *
 * @return A newly allocated and initialized setup structure if no errors
 *   occurred, or @c NULL otherwise.
 */

GiFibersConfig *
giraffe_fibers_config_create(cpl_parameterlist *list)
{

    cpl_parameter *p;

    GiFibersConfig *config = NULL;


    if (!list) {
        return NULL;
    }

    config = cx_calloc(1, sizeof *config);


    /*
     * Some defaults
     */

    config->nspec = 0;
    config->spectra = NULL;


    p = cpl_parameterlist_find(list, "giraffe.fibers.nspectra");
    config->nspec = cpl_parameter_get_int(p);


    p = cpl_parameterlist_find(list, "giraffe.fibers.spectra");
    config->spectra = cx_strdup(cpl_parameter_get_string(p));

    return config;

}


/**
 * @brief
 *   Destroys a fibers setup structure.
 *
 * @param config  The setup structure to destroy.
 *
 * @return Nothing.
 *
 * The function deallocates the memory used by the setup structure
 * @em config.
 */

void
giraffe_fibers_config_destroy(GiFibersConfig *config)
{

    if (config) {
        if (config->spectra) {
            cx_free(config->spectra);
            config->spectra = NULL;
        }

        cx_free(config);
    }

    return;
}


/**
 * @brief
 *   Adds parameters for the spectrum selection.
 *
 * @param list  Parameter list to which parameters are added.
 *
 * @return Nothing.
 *
 * TBD
 */

void
giraffe_fibers_config_add(cpl_parameterlist *list)
{

    cpl_parameter *p;


    if (!list) {
        return;
    }

    p = cpl_parameter_new_value("giraffe.fibers.spectra",
                                CPL_TYPE_STRING,
                                "Index list of spectra to use for "
                                "localization (e.g. 2,10,30-40,55).",
                                "giraffe.fibers",
                                "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "fiber-splist");
    cpl_parameterlist_append(list, p);

    p = cpl_parameter_new_range("giraffe.fibers.nspectra",
                                CPL_TYPE_INT,
                                "Number of spectra to localize.",
                                "giraffe.fibers",
                                0, 0, CX_MAXINT - 1);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "fiber-nspec");
    cpl_parameterlist_append(list, p);

    return;

}
/**@}*/
