/* $Id: visir_util_spc_std_cat.c,v 1.56 2013-02-25 16:54:32 jtaylor Exp $
 *
 * This file is part of the VISIR Pipeline
 * Copyright (C) 2002,2003 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
 */

/*
 * $Author: jtaylor $
 * $Date: 2013-02-25 16:54:32 $
 * $Revision: 1.56 $
 * $Name: not supported by cvs2svn $
 */

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

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

#include "visir_recipe.h"
#include "visir_spectro.h"
#include "visir_spc_distortion.h"
#include "visir_spc_photom.h"
#include "visir_cpl_compat.h"
#include <stdio.h>
#include <string.h>




/*-----------------------------------------------------------------------------
                            Recipe defines
 -----------------------------------------------------------------------------*/

#define RECIPE_STRING   "visir_util_spc_std_cat"

/*-----------------------------------------------------------------------------
                            Private Functions prototypes
 -----------------------------------------------------------------------------*/

static cpl_error_code visir_util_spc_std_cat_save(cpl_frameset *, 
                                                  const cpl_parameterlist *,
                                                  const cpl_table *);

VISIR_RECIPE_DEFINE
(visir_util_spc_std_cat, 0,
 "Generate a FITS catalog of spectroscopic standard stars",
 "This recipe shall be used to generate a FITS catalog of spectroscopic "
 "standard stars for the VISIR pipeline.\n"
 "The sof file shall consist of lines with the name of an ASCII-file named "
 " <Star_Name>.txt, e.g. HD133165.txt and the tag " VISIR_SPC_CAT_ASCII ".\n"
 "All input ASCII-files must comprise the same number of lines.\nThe first "
 "line of the ASCII-file must have 6 fields separated by white-space.\nThe "
 "first three fields are the RA (hh mm ss) which will be stored in degrees "
 "in a table column labeled 'RA' - all three are non-negative and hh and mm "
 "are integer.\n"
 "The 3 last fields are the DEC (dd mm ss) which will be stored in degrees in "
 "a table column labeled 'DEC' - all three are non-negative, dd and mm are "
 "integer, and dd has either a '+' or a '-' prepended (including -00).\n"
 "The remaining lines must all consist of two fields separated by white-"
 "space.\n"
 "The first field is the wavelength (in microns) and the second the (positive) "
 "model flux in W/m2/m. The wavelengths must be identical in all the input "
 "files.\n");

/*----------------------------------------------------------------------------*/
/**
 * @defgroup visir_util_spc_std_cat   Convert ASCII star catalog(s) to FITS
 */
/*----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
                                Functions code
 -----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/**
  @brief    The catalog creation occurs here 
  @param    parlist     the parameters list
  @param    framelist   the frames list
  @return   0 iff everything is ok

  The model flux is expressed in W/m2/m. 
  To compute the sensitivity, we want to convert these to mJy.
  To do that, they have to be multiplied by 3.33.10^8*lambda^2
 */
/*----------------------------------------------------------------------------*/
static int visir_util_spc_std_cat(cpl_frameset            * framelist,
                                  const cpl_parameterlist * parlist)
{
    int               nfiles;
    cpl_bivector    * spectrum = NULL;  /* Flux model for one star */
    cpl_table       * catalog  = NULL;  /* Flux model for all stars */ 
    cpl_array      ** wlen_arr;
    cpl_array      ** flux_arr;
    char           ** star_arr;
    FILE            * in = NULL;
    char              line[1024];
    const double    * ras;              /* RAs */
    const double    * decs;             /* DECs */
    const double      max_radius = VISIR_STAR_MAX_RADIUS;
    double            mindist;
    int               iloc1, iloc2;
    int               nvals;
    int               i;
    

    if (cpl_error_get_code()) return cpl_error_get_code();

    /* Identify the RAW frames in the input frameset */
    skip_if (visir_dfs_set_groups(framelist));

    /* Get the number of files */
    nfiles = cpl_frameset_get_size(framelist);

    skip_if (nfiles < 1);

    catalog = cpl_table_new(nfiles); /* One row per file */

    bug_if (catalog == NULL);

    nvals = 2300; /* 1st guess at length of spectra */

    bug_if (cpl_table_new_column(catalog, "STARS", CPL_TYPE_STRING));
    bug_if (cpl_table_new_column(catalog, "RA",    CPL_TYPE_DOUBLE));
    bug_if (cpl_table_new_column(catalog, "DEC",   CPL_TYPE_DOUBLE));
    bug_if (cpl_table_new_column_array(catalog, "WAVELENGTHS", CPL_TYPE_DOUBLE,
                                       nvals));
    bug_if (cpl_table_new_column_array(catalog, "MODEL_FLUX", CPL_TYPE_DOUBLE,
                                       nvals));

    star_arr = cpl_table_get_data_string(catalog, "STARS");
    wlen_arr = cpl_table_get_data_array(catalog,  "WAVELENGTHS");
    flux_arr = cpl_table_get_data_array(catalog,  "MODEL_FLUX");

    bug_if(star_arr == NULL);
    bug_if(wlen_arr == NULL);
    bug_if(flux_arr == NULL);

    /* Loop on all input frames */
    for (i=0; i < cpl_frameset_get_size(framelist); i++) {
        int ra1, ra2, dec1, dec2;
        double ra3, dec3;
        double ra, dec; /* Converted star position [degree] */
        const char * fstart;
        const char * fstop;
        size_t ilen;
        char isign;
        double  * wlen;
        double  * flux;
        const cpl_frame * frame = cpl_frameset_get_position_const(framelist, i);
        /* Get file name */
        const char * filename = cpl_frame_get_filename(frame);

        skip_if (filename == NULL);

        /* Open the file */
        if ((in = fopen(filename, "r")) == NULL) {
            cpl_msg_error(cpl_func, "Could not open file number %d (%s)", i+1,
                          filename);
            skip_if (1);
        }

        /* Read header */
        /* Get RA and DEC */
        if (fgets(line, 1024, in) == NULL) {
            cpl_msg_error(cpl_func, "fgets() returned NULL when reading the "
                          "first line in file number %d (%s)",
                          i+1, filename);
            skip_if (1);
        }

        if (sscanf(line, "%d %d %lg %c%d %d %lg ", &ra1, &ra2, &ra3, &isign,
                    &dec1,  &dec2, &dec3) != 7) {
            cpl_msg_error(cpl_func, "Invalid first line in file number %d (%s)",
                          i+1, filename);
            skip_if (1);
        }

        /* Get the flux model */
        /* On 1st iteration a resizing may be needed */
        spectrum = cpl_bivector_new(nvals);
        skip_if (visir_bivector_load(spectrum, in));

        fclose(in);
        in = NULL;
    
        if (i == 0) {
            nvals = cpl_bivector_get_size(spectrum); /* Get actual length */
            cpl_table_set_column_depth(catalog, "WAVELENGTHS", nvals);
            cpl_table_set_column_depth(catalog, "MODEL_FLUX",  nvals);
        } else if (nvals != cpl_bivector_get_size(spectrum)) {
            /* The number of vals must be the same in all files */
            cpl_msg_error(cpl_func, "Length of spectrum in file number %d (%s) "
                          "is different from the previous (with length %d)",
                          i+1, filename, nvals);
            skip_if (1);
        }

        skip_if (visir_star_convert(line, ra1, ra2, ra3, isign, dec1, dec2,
                                    dec3, cpl_bivector_get_y_data(spectrum),
                                    nvals, &ra, &dec));

        /* Get the star name, i.e. the basename of the file */
        fstart = strrchr(filename, '/');
        fstart = fstart == NULL ? filename : 1 + fstart;
        fstop  = strrchr(fstart, '.');
        /* ilen is the length of the star name without the null-terminator */
        ilen = fstop == NULL ? strlen(fstart) : (size_t) (fstop - fstart);

        if ((int)ilen <= 0) {
            cpl_msg_error(cpl_func, "File number %d (%s) has no basename for "
                          "the FITS table", i+1, filename);
            skip_if(1);
        }
        
        /* Create, copy and NULL-terminate starname */
        star_arr[i] = cpl_malloc(1+ilen);
        memcpy(star_arr[i], fstart, ilen);
        star_arr[i][ilen] = '\0';

        /* Pointers to the wavelengths and fluxes of one star */
        wlen = cpl_vector_unwrap(cpl_bivector_get_x(spectrum));
        flux = cpl_vector_unwrap(cpl_bivector_get_y(spectrum));

        cpl_bivector_unwrap_vectors(spectrum);
        spectrum = NULL;

        wlen_arr[i] = cpl_array_wrap_double(wlen, nvals);
        flux_arr[i] = cpl_array_wrap_double(flux, nvals);

        bug_if (cpl_table_set_double(catalog, "RA",  i, ra));
        bug_if (cpl_table_set_double(catalog, "DEC", i, dec));

    }

    skip_if (i != nfiles);

    ras  = cpl_table_get_data_double(catalog, "RA");
    decs = cpl_table_get_data_double(catalog, "DEC");

    mindist = visir_star_dist_min(ras, decs, nfiles, &iloc1, &iloc2);

    if (mindist < max_radius) {
        cpl_frame * floc1 = visir_frameset_get_frame(framelist, iloc1);
        cpl_frame * floc2 = visir_frameset_get_frame(framelist, iloc2);
        cpl_msg_warning(cpl_func, "The pair of closest stars is %s (%d) and %s "
                        "(%d) with the distance: %g",
                        cpl_frame_get_filename(floc1), iloc1,
                        cpl_frame_get_filename(floc2), iloc2, mindist);

    }
    else {
        cpl_frame * floc1 = visir_frameset_get_frame(framelist, iloc1);
        cpl_frame * floc2 = visir_frameset_get_frame(framelist, iloc2);
        cpl_msg_info(cpl_func, "The pair of closest stars is %s (%d) and %s "
                     "(%d) with the distance: %g",
                     cpl_frame_get_filename(floc1), iloc1,
                     cpl_frame_get_filename(floc2), iloc2, mindist);
    }
    
    /* Save the table */
    cpl_msg_info(cpl_func, "Saving the table with %d rows each with a spectrum "
                 "length of %d", nfiles, nvals);
    skip_if (visir_util_spc_std_cat_save(framelist, parlist, catalog));

    end_skip;

    cpl_bivector_delete(spectrum);
    cpl_table_delete(catalog);
    if (in) fclose(in);

    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Save the visir_util_spc_std_cat recipe products on disk
  @param    set      The input frame set
  @param    parlist  The input list of parameters
  @param    catalog  The 3D-table
  @return   0 iff everything is ok

  The table contains 5 columns:
  "STARS", "RA, "DEC", "WAVELENGTHS", "MODEL_FLUX".
 */
/*----------------------------------------------------------------------------*/
static
cpl_error_code visir_util_spc_std_cat_save(cpl_frameset            * set,
                                           const cpl_parameterlist * parlist,
                                           const cpl_table         * catalog)
{
    cpl_propertylist * applist = cpl_propertylist_new();


    bug_if (catalog == NULL);
    bug_if (parlist == NULL);
    bug_if (set     == NULL);

    bug_if(cpl_propertylist_append_string(applist, "INSTRUME", "VISIR"));

    skip_if (irplib_dfs_save_table(set, parlist, set, catalog, NULL,
                               RECIPE_STRING, VISIR_SPEC_STD_CAT_PROCATG,
                               applist, NULL, visir_pipe_id,
                               RECIPE_STRING CPL_DFS_FITS));
    
    end_skip;

    cpl_propertylist_delete(applist);

    return cpl_error_get_code();
}
