/* $Id: fors_stack.c,v 1.17 2010-09-14 07:49:30 cizzo Exp $
 *
 * This file is part of the FORS Library
 * Copyright (C) 2002-2010 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
 */

/*
 * $Author: cizzo $
 * $Date: 2010-09-14 07:49:30 $
 * $Revision: 1.17 $
 * $Name: not supported by cvs2svn $
 */

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

#include <fors_stack.h>

#include <fors_dfs.h>
#include <fors_utils.h>

#include <cpl.h>

#include <string.h>
#include <stdbool.h>

/**
 * @defgroup fors_stack Image stacking
 */

/**@{*/

/**
 * @brief    Define recipe parameters
 * @param    parameters     parameter list to fill
 * @param    context        parameters context
 * @param    default_method default stack method
 */

void fors_stack_define_parameters(cpl_parameterlist *parameters, 
                                  const char *context,
                                  const char *default_method)
{
    cpl_parameter *p;
    const char *full_name = NULL;
    const char *name;

    name = "stack_method";
    full_name = cpl_sprintf("%s.%s", context, name);
    p = cpl_parameter_new_enum(full_name,
                               CPL_TYPE_STRING,
                               "Frames combination method",
                               context,
                               default_method, 4,
                               "average", "median", "minmax", "ksigma");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(parameters, p);
    cpl_free((void *)full_name);


    /* minmax */
    name = "minrejection";
    full_name = cpl_sprintf("%s.%s", context, name);
    p = cpl_parameter_new_value(full_name,
                                CPL_TYPE_INT,
                                "Number of lowest values to be rejected",
                                context,
                                1);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(parameters, p);
    cpl_free((void *)full_name);

    name = "maxrejection";
    full_name = cpl_sprintf("%s.%s", context, name);
    p = cpl_parameter_new_value(full_name,
                                CPL_TYPE_INT,
                                "Number of highest values to be rejected",
                                context,
                                1);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(parameters, p);
    cpl_free((void *)full_name);

    /* ksigma */
    name = "klow";
    full_name = cpl_sprintf("%s.%s", context, name);
    p = cpl_parameter_new_value(full_name,
                                CPL_TYPE_DOUBLE,
                                "Low threshold in ksigma method",
                                context,
                                3.0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(parameters, p);
    cpl_free((void *)full_name);

    name = "khigh";
    full_name = cpl_sprintf("%s.%s", context, name);
    p = cpl_parameter_new_value(full_name,
                                CPL_TYPE_DOUBLE,
                                "High threshold in ksigma method",
                                context,
                                3.0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(parameters, p);
    cpl_free((void *)full_name);

    name = "kiter";
    full_name = cpl_sprintf("%s.%s", context, name);
    p = cpl_parameter_new_value(full_name,
                                CPL_TYPE_INT,
                                "Max number of iterations in ksigma method",
                                context,
                                999);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(parameters, p);
    cpl_free((void *)full_name);

    return;
}

#undef cleanup
#define cleanup \
do { \
    cpl_free((void *)name); \
} while (0)
/**
 * @brief    Get stack method from parameter list
 * @param    parameters     recipe parameter list
 * @param    context        read stack method from this context
 * @return   newly allocated opaque stack method
 *
 * The parameter list should have been previously created using
 * fors_stack_define_parameters()
 */
stack_method *
fors_stack_method_new(const cpl_parameterlist *parameters, const char *context)
{
    stack_method *sm = cpl_malloc(sizeof(stack_method));
    const char *name = NULL;

    cpl_msg_info(cpl_func, "Stack method parameters:");

    cpl_msg_indent_more();
    name = cpl_sprintf("%s.%s", context, "stack_method");
    sm->method_name = dfs_get_parameter_string_const(parameters, 
                                            name);
    cpl_free((void *)name); name = NULL;
    cpl_msg_indent_less();

    assure( !cpl_error_get_code(), return NULL, NULL );
    assure( sm->method_name != NULL, return NULL, NULL );

    if (strcmp(sm->method_name, "average") == 0) {
        sm->method = AVERAGE;
    }
    else if (strcmp(sm->method_name, "mean") == 0) {
        sm->method = MEAN;
    }
    else if (strcmp(sm->method_name, "wmean") == 0) {
        sm->method = WMEAN;
    }
    else if (strcmp(sm->method_name, "median") == 0) {
        sm->method = MEDIAN;
    }
    else if (strcmp(sm->method_name, "minmax") == 0) {
/*
        assure( false, return NULL, "Unsupported stack method %s", sm->method_name);
*/
        sm->method = MINMAX;
    }
    else if (strcmp(sm->method_name, "ksigma") == 0) {
        sm->method = KSIGMA;
    }
    else {
        assure( false, return NULL, "Unknown stack method '%s'", sm->method_name);
    }

    switch (sm->method) {
    case AVERAGE: break;
    case MEAN: break;
    case WMEAN: break;
    case MEDIAN: break;
    case MINMAX:

        cpl_msg_indent_more();
        cpl_msg_indent_more();
        name = cpl_sprintf("%s.%s", context, "minrejection");
        sm->pars.minmax.min_reject = dfs_get_parameter_int_const(parameters, 
                                                                 name);
        cpl_free((void *)name); name = NULL;
        cpl_msg_indent_less();
        cpl_msg_indent_less();
        assure( !cpl_error_get_code(), return NULL, NULL );
        
        cpl_msg_indent_more();
        cpl_msg_indent_more();
        name = cpl_sprintf("%s.%s", context, "maxrejection");
        sm->pars.minmax.max_reject = dfs_get_parameter_int_const(parameters, 
                                                                 name);
        cpl_free((void *)name); name = NULL;
        cpl_msg_indent_less();
        cpl_msg_indent_less();
        assure( !cpl_error_get_code(), return NULL, NULL );

        break;
    case KSIGMA:
        cpl_msg_indent_more();
        cpl_msg_indent_more();
        name = cpl_sprintf("%s.%s", context, "klow");
        sm->pars.ksigma.klow = dfs_get_parameter_double_const(parameters, 
                                                              name);
        cpl_free((void *)name); name = NULL;
        cpl_msg_indent_less();
        cpl_msg_indent_less();
        assure( !cpl_error_get_code(), return NULL, NULL );
        
        cpl_msg_indent_more();
        cpl_msg_indent_more();
        name = cpl_sprintf("%s.%s", context, "khigh");
        sm->pars.ksigma.khigh = dfs_get_parameter_double_const(parameters, 
                                                               name);
        cpl_free((void *)name); name = NULL;
        cpl_msg_indent_less();
        cpl_msg_indent_less();
        assure( !cpl_error_get_code(), return NULL, NULL );

        cpl_msg_indent_more();
        cpl_msg_indent_more();
        name = cpl_sprintf("%s.%s", context, "kiter");
        sm->pars.ksigma.kiter = dfs_get_parameter_int_const(parameters, 
                                                            name);
        cpl_free((void *)name); name = NULL;
        cpl_msg_indent_less();
        cpl_msg_indent_less();
        assure( !cpl_error_get_code(), return NULL, NULL );
        
        break;
    default:
        passure( false, return NULL );
        break;
    } /* switch sm->method */

    cleanup;
    return sm;
}

/**
 * @brief    Destructor
 * @param    sm            object to delete
 */
void
fors_stack_method_delete(stack_method **sm)
{
    if (sm && *sm) {
        cpl_free(*sm); *sm = NULL;
    }
    return;
}

#undef cleanup
#define cleanup
/**
 * @brief   Stack method as string
 * @param   sm            stack method
 * @return  textual representation of the provided stack method
 */
const char *fors_stack_method_get_string(const stack_method *sm)
{
    assure( sm != NULL, return "Null", NULL );

    return sm->method_name;
}

#undef cleanup
#define cleanup \
do { \
} while (0)
/**
 * @brief    Stack images
 * @param    images        list of images to stack
 * @param    sm            stacking method
 * @return   master images stacked using the specified method
 */
fors_image *
fors_stack_const(const fors_image_list *images, const stack_method *sm)
{
    fors_image *master = NULL;

    assure( images != NULL, return master, NULL );
    assure( fors_image_list_size(images) > 0, return master, 
           "No images to collapse");
    
    cpl_msg_info(cpl_func, "Stacking images (method = %s)",
                 fors_stack_method_get_string(sm)); 

    switch (sm->method) {
    case AVERAGE: 
        master = fors_image_collapse_create(images);
        break;
    case MEDIAN: 
        master = fors_image_collapse_median_create(images);
        break;
    case MINMAX:
        master = fors_image_collapse_minmax_create(images, 
                                  sm->pars.minmax.min_reject,
                                  sm->pars.minmax.max_reject);
        break;
    case KSIGMA: 
/*
        assure( false, return NULL, "Unsupported stack method %s",
                fors_stack_method_get_string(sm));
*/
        master = fors_image_collapse_ksigma_create(images, 
                                  sm->pars.ksigma.klow,
                                  sm->pars.ksigma.khigh,
                                  sm->pars.ksigma.kiter);
        break;
    default:
        assure( false, return NULL, "Unknown stack method '%s' (%d)",
                fors_stack_method_get_string(sm), sm->method);
        break;
    }    

    return master;
}

/**
 * @brief    Same as fors_stack_const()
 */
fors_image *
fors_stack(fors_image_list *images, const stack_method *sm)
{
    return fors_stack_const((const fors_image_list *)images, sm);
}


/**@}*/
