/* $Id: visir_utils.h,v 1.109 2013-05-13 16:07:06 jtaylor Exp $
 *
 * This file is part of the VISIR Pipeline
 * Copyright (C) 2002,2003,2013,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 St, Fifth Floor, Boston, MA  02111-1307  USA
 */

#ifndef VISIR_UTILS_H
#define VISIR_UTILS_H

/*-----------------------------------------------------------------------------
                                   Includes
 -----------------------------------------------------------------------------*/


#include "irplib_utils.h"

#include "irplib_plugin.h"

#include "visir_spectro.h"
#include "visir_cpl_compat.h"
#include <cpl.h>
#include <string.h> /* for strlen */
#include <sys/types.h> /* for off_t */

/*-----------------------------------------------------------------------------
                                   Defines
 -----------------------------------------------------------------------------*/

#ifndef VISIR_STAR_MAX_RADIUS
   /* 2 arcminutes in degrees */
#  define VISIR_STAR_MAX_RADIUS (2.0/60.0)
#endif

#define VISIR_STR_PAR_EMPTY "NONE"

/* Plotting errors will be ignored */
#define visir_vector_plot(ARG1, ARG2, ARG3, ARG4)                       \
    do {                                                                \
        cpl_errorstate prestate = cpl_errorstate_get();                 \
        if (cpl_plot_vector(ARG1, ARG2, ARG3, ARG4))                    \
            irplib_error_recover(prestate, "Could not plot vector");    \
    } while (0)

#define visir_bivector_plot(ARG1, ARG2, ARG3, ARG4)                     \
    do {                                                                \
        cpl_errorstate prestate = cpl_errorstate_get();                 \
        if (cpl_plot_bivector(ARG1, ARG2, ARG3, ARG4))                  \
            irplib_error_recover(prestate, "Could not plot bivector");  \
    } while (0)

#define visir_table_plot(ARG1, ARG2, ARG3, ARG4, ARG5, ARG6)            \
    do {                                                                \
        cpl_errorstate prestate = cpl_errorstate_get();                 \
        if (cpl_plot_column(ARG1, ARG2, ARG3, ARG4, ARG5, ARG6))        \
            irplib_error_recover(prestate, "Could not plot table");     \
    } while (0)

#define visir_image_plot(ARG1, ARG2, ARG3, ARG4)                        \
    do {                                                                \
        cpl_errorstate prestate = cpl_errorstate_get();                 \
        if (cpl_plot_image(ARG1, ARG2, ARG3, ARG4))                     \
            irplib_error_recover(prestate, "Could not plot image");     \
    } while (0)

#define visir_image_col_plot(ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7)  \
    do {                                                                \
        cpl_errorstate prestate = cpl_errorstate_get();                 \
        if (cpl_plot_image_col(ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7)) \
            irplib_error_recover(prestate, "Could not plot image columns"); \
    } while (0)

#define visir_image_row_plot(ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7)  \
    do {                                                                \
        cpl_errorstate prestate = cpl_errorstate_get();                 \
        if (cpl_plot_image_row(ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7)) \
            irplib_error_recover(prestate, "Could not plot image raws"); \
    } while (0)


#define visir_error_set(ERRCODE) cpl_error_set(cpl_func, ERRCODE)

/* FIXME: Copy to visir_error_reset() */
#define visir_error_reset(...)                                                \
 irplib_error_recover(cleanstate, __VA_ARGS__)

#define FOR_EACH(name, l) \
    for(cx_list_iterator name = cx_list_begin(l); \
        name != cx_list_end(l); name = cx_list_next(l, name))

/* provides typed variable at expense of some namespace clobber,
 * must not be nested due to the shadowing of __end
 * usage FOR_EACH_T(type varname, list) */
#define FOR_EACH_T(var, list) \
    for (cx_list_iterator __it = cx_list_begin(list), __cond = NULL, \
         __end = cx_list_end(list); \
         __it != __end; __it = cx_list_next(list, __it)) \
        if ((__cond = NULL) != NULL) {} else \
        for (var = cx_list_get(list, __it); __cond == NULL; \
             __cond = (cx_list_iterator)1)

/* cpl 6 iterators, deprecated
#define FOR_EACH_FRAMESET(name, fs) \
    for (cpl_frame * name = cpl_frameset_get_first(fs); \
         name != NULL; \
         name = cpl_frameset_get_next(fs))

#define FOR_EACH_FRAMESET_C(name, fs) \
    for (const cpl_frame * name = cpl_frameset_get_first_const(fs); \
         name != NULL; \
         name = cpl_frameset_get_next_const(fs))
*/

#define FOR_EACH_FRAMESET(name, fs) \
    for (int __ind = 0, __cond = 0; __ind  < cpl_frameset_get_size(fs); \
         __ind++) \
        if ((__cond = 0) != 0) {} else \
        for (cpl_frame * name = cpl_frameset_get_position(fs, __ind); \
             __cond == 0; __cond = 1)

#define FOR_EACH_FRAMESET_C(name, fs) \
    for (int __ind = 0, __cond = 0; __ind  < cpl_frameset_get_size(fs); \
         __ind++) \
        if ((__cond = 0) != 0) {} else \
        for (const cpl_frame * name = cpl_frameset_get_position_const(fs, __ind); \
             __cond == 0; __cond = 1)

#define ARRAY_LEN(x) (sizeof(x)/sizeof(x[0]))

/* must not be followed by a semicolon! */
#ifdef _OPENMP
    #if _OPENMP >= 200805
        #define OMP_PRAGMA(x) _Pragma (#x)
        #define OMP3_PRAGMA(x) _Pragma (#x)
        #define VISIR_OMP3
        #define VISIR_OMP
    #else
        #define OMP_PRAGMA(x) _Pragma (#x)
        #define OMP3_PRAGMA(x)
        #define VISIR_OMP
    #endif
#else
    #define OMP_PRAGMA(x)
    #define OMP3_PRAGMA(x)
#endif

#ifndef VISIR_ATTR_UNUSED
    #if defined(__GNUC__) || defined(__ICC) || defined(__clang__)
        #define VISIR_ATTR_UNUSED __attribute__ ((__unused__))
    #else
        #define VISIR_ATTR_UNUSED
    #endif
#endif


static inline cpl_boolean visir_str_par_is_empty(const char * str)
{
    return (str == NULL || strlen(str) == 0 ||
            strcmp(str, VISIR_STR_PAR_EMPTY) == 0);
}

static inline int visir_round_to_int(const double x)
{
    return x >= 0 ? (int)(x + 0.5) : (int)(x - 0.5);
}

/*-----------------------------------------------------------------------------
                                       Prototypes
 -----------------------------------------------------------------------------*/

size_t visir_get_num_threads(cpl_boolean force);
int visir_cmp_frm_fn(const cpl_frame * a, const cpl_frame * b);
cpl_error_code visir_image_multiply_fast(cpl_image *, const cpl_image *);
double visir_image_get_mean_fast(const cpl_image * im);
cpl_error_code visir_move_products(cpl_frameset *frames,
                                   const char * dest_, const char * src);
double visir_utils_get_exptime(const int, const cpl_propertylist *);
double * visir_utils_get_wls(const irplib_framelist *);
cpl_image * visir_create_disk_intimage(int, int, int, int, int);
cpl_image * visir_create_ring_intimage(int, int, int, int, int, int);
double visir_image_sigma_clip(const cpl_image *, double *);
double visir_img_phot_sigma_clip(const cpl_image *);
cpl_error_code visir_get_subpixel_maxpos(const cpl_image *,
                                         cpl_size, cpl_size,
                                         double *, double *);

typedef struct _visir_fftx_cache visir_fftx_cache;
visir_fftx_cache * visir_new_fftx_cache(void);
void visir_delete_fftx_cache(visir_fftx_cache *);
cpl_error_code visir_fftxcorrelate(const cpl_image *, const cpl_image *,
                                   cpl_boolean, double *, double *,
                                   double *, visir_fftx_cache *);
cpl_error_code
visir_interpolate_rejected(cpl_image * img, size_t ** ppoints, size_t * n);

cpl_image *
visir_parallel_median_collapse(const cpl_imagelist * l);

typedef struct {
    /* main propertylist (e.g. from extension 0), must be deleted explicitly*/
    cpl_propertylist * mplist;
    /* arbitrary data associated with the list, must be deleted explicitly */
    void ** auxdata;

    /* images */
    cpl_imagelist * imgs;
    /* per image property lists */
    cpl_propertylist ** plists;

    cpl_size _size;
    cpl_size _capacity;
} visir_imglist;

typedef void(*visir_free)(void *);

visir_imglist *
visir_imglist_new(const cpl_size capacity, cpl_propertylist * mplist);

void
visir_imglist_delete(visir_imglist * l, visir_free aux_destructor);

void
visir_imglist_unwrap(visir_imglist * l, visir_free aux_destructor);

void
visir_imglist_append(visir_imglist * l, cpl_image * img,
                     void * auxdata);

void
visir_imglist_append_imglist(visir_imglist * l, cpl_imagelist * imgs,
                             void ** auxdata);

cpl_size
visir_imglist_get_size(const visir_imglist * imgs);

cpl_image *
visir_imglist_get_img(const visir_imglist * imgs, const cpl_size index);

void *
visir_imglist_get_data(const visir_imglist * imgs, const cpl_size index);

cpl_error_code
visir_imglist_get(const visir_imglist * imgs, const cpl_size index,
                  cpl_image ** img, void ** auxdata);

cpl_error_code
visir_imglist_set_data(visir_imglist * imgs,
                           const cpl_size index, void * auxdata);

void
visir_imglist_set_mplist(visir_imglist * imgs, cpl_propertylist * mplist);

cpl_propertylist *
visir_imglist_get_mplist(const visir_imglist * imgs);

cpl_imagelist *
visir_imglist_get_imglist(const visir_imglist * imgs);


int visir_star_find(const cpl_vector *, const cpl_vector *, double, double,
                    double, double *);

cpl_error_code visir_star_convert(const char *, int, int, double, char, int,
                                  int, double, const double *, int, double *,
                                  double *);

double visir_star_dist_min(const double *, const double *, int, int *, int *);

cpl_table * visir_table_new_xypos(const cpl_imagelist * images, const char *);

int visir_vector_minpos(const cpl_vector *);


char * visir_frameset_serialize(const cpl_frameset * frames, size_t * size);
cpl_frameset * visir_frameset_deserialize(char * block, size_t * size);
cpl_error_code visir_send_frameset(FILE * , const cpl_frameset * );

void visir_drop_cache(const char * filename, off_t offset, off_t length);
void visir_readahead(const char * filename, off_t offset, off_t length);

size_t visir_get_nbytes(const cpl_image * img);
size_t visir_get_nbytes_plist(const cpl_propertylist * plist);

/* FIXME: warn_unused_result */
cpl_boolean visir_get_tempdir(char * tmpdir_);
char * visir_get_cwd(void);

cpl_parameter * visir_parameter_duplicate(const cpl_parameter * p);

cpl_error_code visir_copy_parameters(cpl_parameterlist *,
                                     const cpl_parameterlist *);

cpl_recipe *
visir_init_recipe(const char * name, int (*get_info)(cpl_pluginlist *),
                  cpl_pluginlist * plugins);

cpl_error_code
visir_run_recipe(cpl_recipe * recipe,
                 cpl_frameset * framelist, const cpl_parameterlist * parlist,
                 cpl_error_code (*set_parlist)(cpl_parameterlist *,
                                               const cpl_parameterlist *));

cpl_error_code
visir_tmpdir_exec(const char * recipename, cpl_plugin * plugin,
                  int (*function)(cpl_frameset *, const cpl_parameterlist *));
cpl_error_code
visir_forking_exec(const char * recipename, cpl_plugin * plugin,
                   int (*function)(cpl_frameset *, const cpl_parameterlist *));

cpl_frameset *
visir_prepare_frameset(const cpl_frameset * frameset,
                       const char ** tagmap, size_t ntags,
                       cpl_boolean reverse);
cpl_frameset * visir_remove_modified_calib(cpl_frameset * set);

cpl_error_code visir_bivector_load(cpl_bivector *, FILE *);

const char ** visir_framelist_set_tag(irplib_framelist *,
                                      char * (*)(const cpl_frame *,
                                                 const cpl_propertylist *, int),
                                      int *);

cpl_error_code visir_qc_append_background(cpl_propertylist *,
                                          const irplib_framelist *, int, int);

cpl_error_code visir_qc_append_capa(cpl_propertylist *,
                                    const irplib_framelist *);

cpl_error_code visir_qc_append_filter(cpl_propertylist *,
                                      const irplib_framelist *);
cpl_error_code visir_qc_append_exptime(cpl_propertylist *,
                                       const irplib_framelist *);

#ifdef _OPENMP
struct visir_queue;
typedef struct visir_queue visir_queue;
visir_queue * visir_queue_init(const int max_size);
void visir_queue_set_error(visir_queue * q, cpl_error_code error);
void visir_queue_delete(visir_queue * q);
cpl_error_code visir_queue_put(visir_queue * q, void * data);
void * visir_queue_pop(visir_queue * q);
#endif

size_t visir_upper_bound(const cpl_vector * vec, double val);
size_t visir_lower_bound(const cpl_vector * vec, double val);
cpl_image * visir_linintp_values(const cpl_image * inp, const cpl_bivector * ref);
cpl_error_code
fit_2d_gauss(const cpl_image * img_, const cpl_image * weights, cpl_size x, cpl_size y,
             double est_fwhmx, double est_fwhmy,
             double * peak, double * peak_err,
             double * major, double * major_err,
             double * minor, double * minor_err,
             double * angle, double * angle_err);
cpl_error_code
fit_1d_gauss(const cpl_vector * xv, const cpl_vector * y, cpl_vector * dyv,
             double * x0, double * x0_err,
             double * peak, double * peak_err,
             double * sigma, double * sigma_err);

#endif
