/*                                                                            *
 *   This file is part of the ESO UVES Pipeline                               *
 *   Copyright (C) 2004,2005 European Southern Observatory                    *
 *                                                                            *
 *   This library 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA     *
 *                                                                           */

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup flames_ordpos   FLAMES order definition
 */
/*----------------------------------------------------------------------------*/

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

#include <flames_preordpos.h>

#include <flames_ordpos.h>
#include <flames_multimatch.h>
#include <flames_matchorders.h>
#include <flames_create_ordertable.h>
#include <flames_tracing.h>
#include <flames_fitting.h>
#include <flames_dfs.h>
#include <flames_def_drs_par.h>
#include <flames_utils.h>

#include <uves_dfs.h>
#include <uves_orderpos_hough.h>
#include <uves_utils_cpl.h>
#include <uves_utils_wrappers.h>
#include <uves_dump.h>
#include <uves_error.h>
#include <uves_msg.h>
#include <string.h>
#include <cpl.h>
#include <stdbool.h>
/*-----------------------------------------------------------------------------
                            Functions prototypes
 -----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/**
  @brief    computes the relative shift along the y-axis of the
            format check frame against the FF fibre frames.  
  @param    ord_gue_tab    guess
  @param    out_filename   out table
  @param    b_parOdd       fibre odd
  @param    b_parEven      fibre even
  @param    b_parOrdTra
  @param    NBTRACES       number of traces to find
  @param    DRS_P8_OFPOS   
  @param    chipchoice     CCD chip
  @param    DEFPOL          Degree of bivariate polynomial


  Input: odd/even fibres FF frames: we use it/them in order to compute the mutual
  between FrmtChk and FF (and science too) exposures 
 **/
/*----------------------------------------------------------------------------*/
void
flames_ordpos(const cpl_frame *ord_gue_tab, 
              const char *out_filename,
              const cpl_frameset *FFCAT,
              int NBTRACES,
              double DRS_P8_OFPOS[5],
              char chipchoice,
              int wlen,
              int DEFPOL[2])
{
    uves_propertylist *ltab_header = NULL;
    cpl_image *ORDREF_image = NULL;
    cpl_table *ordertable = NULL;
    cpl_table *ltab = NULL;
    cpl_table *middummr = NULL;
    uves_propertylist *middummr_header = NULL;
    cpl_image *htrans = NULL;
    cpl_image *htrans_orig = NULL;
    double *refstart = NULL;
    double *refstep = NULL;
    int *refnpix = NULL;

    /* Per frame fibre mask */
    uves_propertylist *ORDREF_header = NULL;
    int fib_msk_length;
    cpl_type fib_msk_type;
    const int *fib_msk = NULL;

    const char *fibremask_string = NULL;
    int hot_thres = 100000;
    int step = 10;
    int WIDTHI = 0;     /* Half-width, automatic if 0 */
    const char *P4 = "DENSE"; /* Step, nb of traces or ALL,CENTER,DENSE,NO,FOLLOW */

    const char *GTAB;
    const char *OTAB;
    cpl_frameset *CATNAME = NULL;

    int MAXEXTEND = 3;
    const char *in_a = "middummi.fits";
    const char *in_b = "middummr.fits";
    const char *LTAB = "middummd.fits";

    int ngueord = 0;
    const char *ORDTAB = "ordtab.fits";
    const char *BAKTAB = "baktab.fits";

    GTAB = cpl_frame_get_filename(ord_gue_tab);
    OTAB = out_filename;
    CATNAME = cpl_frameset_duplicate(FFCAT);

    {
        int NBHW[2];
        int VALI[2];
        const char *P4INT = P4;
        int STNB[] = {0, 50};
        double TAB_IO_YSHIFT = 0;
        NBHW[0] = NBTRACES;
        NBHW[1] = WIDTHI;
        VALI[0] = hot_thres;
        VALI[1] = step;

        uves_msg("Entering preordpos module: exact value for y coordinate is computed");

        check( flames_preordpos(GTAB,
                        &MAXFIBRES,
                        CATNAME,
                        &MAXEXTEND,
                        LTAB,
                        &TAB_IO_YSHIFT),
               "preordpos failed");

        uves_msg("Leaving preordpos module:");
        uves_msg("Check %s table for computed y values", LTAB);
        uves_msg("Table %s contains the first guess", GTAB);
        uves_msg(" for orders/x/y values");

        {
            double REFSTART[2] = {0, 0};
            double REFSTEP[2] = {0, 0};
            int REFNPIX[2] = {0, 0};
            char CHIPCHOICE = chipchoice;
            double XCENTER[3];
            int length;
            cpl_type refstart_type;
            cpl_type refstep_type;
            cpl_type refnpix_type;

            check( ltab_header = uves_propertylist_load(LTAB, 0),
                   "Failed to load %s FITS header", LTAB);

            /* REFSTART */
            check( refstart = uves_read_midas_array(
                            ltab_header, "REFSTART",
                            &length,
                            &refstart_type, NULL),
                   "Error reading REFSTART from %s", LTAB);

            assure( refstart_type == CPL_TYPE_DOUBLE,
                    CPL_ERROR_TYPE_MISMATCH,
                    "Type of REFSTART is %s, double expected",
                    uves_tostring_cpl_type(refstart_type));

            REFSTART[0] = refstart[0];
            REFSTART[1] = refstart[1];

            /* REFSTEP */
            check( refstep = uves_read_midas_array(
                            ltab_header, "REFSTEP",
                            &length,
                            &refstep_type, NULL),
                   "Error reading REFSTEP from %s", LTAB);

            assure( refstep_type == CPL_TYPE_DOUBLE,
                    CPL_ERROR_TYPE_MISMATCH,
                    "Type of REFSTEP is %s, double expected",
                    uves_tostring_cpl_type(refstep_type));

            REFSTEP[0] = refstep[0];
            REFSTEP[1] = refstep[1];

            /* REFNPIX */
            check( refnpix = uves_read_midas_array(
                            ltab_header, "REFNPIX",
                            &length,
                            &refnpix_type, NULL),
                   "Error reading REFNPIX from %s", LTAB);

            assure( refnpix_type == CPL_TYPE_INT,
                    CPL_ERROR_TYPE_MISMATCH,
                    "Type of REFNPIX is %s, double expected",
                    uves_tostring_cpl_type(refnpix_type));

            REFNPIX[0] = refnpix[0];
            REFNPIX[1] = refnpix[1];

            /* Save the new header */
            XCENTER[0] = REFSTART[0] + ((REFNPIX[0]-1)*(REFSTEP[0]*.4));
            XCENTER[1] = REFSTART[0] + ((REFNPIX[0]-1)*(REFSTEP[0]*.5));
            XCENTER[2] = REFSTART[0] + ((REFNPIX[0]-1)*(REFSTEP[0]*.6));

            uves_msg("Guess table %s will be used", GTAB);

            if (NBTRACES < 0) NBHW[0] = fabs(NBTRACES);

            char ALLFRAME[320];    // initial value is not used

            int LENGTH[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};

            check( flames_multimatch(CATNAME,
                            ALLFRAME,
                            LENGTH),
                   "multimatch failed");

            int NUMBER = 0;
            int limit = LENGTH[0];
            int seq = 0;
            int UPPER = LENGTH[1];
            int lower = 1;
            int fibreson = 0;
            char frame[60];
            int maxtraces = 0;

            int index = 0;
            maxtraces = limit * MAXFIBRES;

            int *FIBRENUMBERS = cpl_calloc(sizeof(int), maxtraces);
            double *FIBRESHIFTS = cpl_calloc(sizeof(double), maxtraces);

            limit = limit + 2;
            uves_msg("Looping over catalogue FF frames...");

            for (seq = 3; seq <= limit; seq++) {

                int j = 0;
                const char *ORDREF;
                int SCAN[2];
                for (j = lower; j <= UPPER; j++)
                {
                    frame[j-lower] = ALLFRAME[j-1];
                }
                frame[j-lower] = '\0';
                uves_msg("%s", frame);

                lower = UPPER + 1;
                UPPER = UPPER + LENGTH[seq-1];
                ORDREF = frame;

                passure( strcmp(P4, "DENSE") == 0, "%s", P4);

                uves_msg("****    Order Definition    ****");

                uves_msg("Order reference fibre FF frame: ORDREF=%s", ORDREF);
                uves_msg("Preprocessed frame:    middummi.fits");
                uves_msg("Hough transform:       middummh.fits");
                uves_msg("Orders detection:      middummr.fits");
                uves_msg("Output tables:         ORDTAB=%s and BAKTAB=%s", ORDTAB, BAKTAB);

                uves_msg("Preprocessing %s: median filter started...", ORDREF);

                {
                    bool extrapolate_border = true;

                    check( ORDREF_image = cpl_image_load(ORDREF, CPL_TYPE_DOUBLE, 0, 0),
                           "Error loading %s", ORDREF);

                    check( uves_filter_image_median(&ORDREF_image, 2, 1, extrapolate_border),
                           "Median filtering failed");
                }

                uves_msg("Preprocessing %s: median filter applied...", ORDREF);
                uves_msg("Output is frame middummi.fits");

                check( uves_save_image(ORDREF_image, "middummi.fits", NULL, true, true),
                       "Error saving image to middummi.fits");

                STNB[0] = cpl_image_get_size_x(ORDREF_image)/STNB[1];
                P4INT = uves_sprintf("%d, %d", STNB[0], STNB[1]);

                uves_msg("Hough transform running...");

                check( ORDREF_header = uves_propertylist_load(ORDREF, 0),
                       "Error loading %s header", ORDREF);

                check( fib_msk = uves_read_midas_array(
                                ORDREF_header, "FIBREMASK",
                                &fib_msk_length,
                                &fib_msk_type, NULL),
                       "Error reading FIBREMASK");

                assure( fib_msk_type == CPL_TYPE_INT, CPL_ERROR_TYPE_MISMATCH,
                        "Type of FIBREMASK is %s, int expected",
                        uves_tostring_cpl_type(fib_msk_type));

                DRS_NLIT_FIBRES = 0;
                uves_free_propertylist(&ORDREF_header);
                for (index = 1; index <= fib_msk_length; index++) 
                {
                    DRS_NLIT_FIBRES += fib_msk[index-1];
                }

                check( ordertable = cpl_table_load(GTAB, 1, 1),
                       "Error loading table %s", GTAB);

                ngueord = 
                                cpl_table_get_column_max(ordertable, "ORDER") -
                                cpl_table_get_column_min(ordertable, "ORDER") + 1;
                uves_free_table(&ordertable);

                if (NBTRACES > 0)
                    NBHW[0] = ngueord*DRS_NLIT_FIBRES + DRS_NLIT_FIBRES/2;

                uves_msg("ntraces=%d, nord=%d, echord=%d nfib=%d",
                         NBHW[0], ngueord, ngueord, DRS_NLIT_FIBRES);

                switch(wlen) {
                case 520: 
                    SCAN[0] = DRS_SCAN_MIN_1;
                    SCAN[1] = DRS_SCAN_MAX_1;
                    break;
                case 580: 
                    SCAN[0] = DRS_SCAN_MIN_2;
                    SCAN[1] = DRS_SCAN_MAX_2;
                    break;
                case 860: 
                    SCAN[0] = DRS_SCAN_MIN_3;
                    SCAN[1] = DRS_SCAN_MAX_3;
                    break;
                default:
                    SCAN[0] = 0;
                    SCAN[1] = 2048;
                    break;
                }

                uves_msg("CCDSCAN1 = %d, CCDSCAN2 = %d", SCAN[0], SCAN[1]);
                bool norders_is_guess = true;
                double PTHRES = 0.2;
                double minslope = 0;
                double maxslope = DRS_P8_OFPOS[2];
                double slopestep = DRS_P8_OFPOS[3];
                slopestep = 0.0005;
                int sloperes = (maxslope-minslope)/slopestep + 1;

                check( middummr = uves_hough(ORDREF_image, SCAN[0], SCAN[1],
                                //NBHW[0],
                                DRS_NLIT_FIBRES * ngueord,
                                norders_is_guess,
                                STNB[0], PTHRES,
                                minslope, maxslope, sloperes,
                                false,  /* Consecutive order lines? */
                                &htrans, &htrans_orig),
                       "Hough transform failed");

                cpl_table_cast_column (middummr, "Slope", "SLOPE", CPL_TYPE_FLOAT);
                cpl_table_erase_column(middummr, "Slope");
                cpl_table_cast_column (middummr, "Intersept", "ORIG", CPL_TYPE_FLOAT);
                cpl_table_erase_column(middummr, "Intersept");

                uves_free_image(&ORDREF_image);
                check( uves_save_image(htrans_orig, "middummh.fits", NULL, true, true),
                       "Error saving middummh.fits");
                uves_free_image(&htrans);
                uves_free_image(&htrans_orig);

                uves_msg("Hough transform completed");
                uves_msg("Output from Hough transform is middummr table");
                middummr_header = uves_propertylist_new();

                {
                    fibremask_string = uves_sprintf("%d %d %d %d %d %d %d %d %d",
                                    fib_msk[0],
                                    fib_msk[1],
                                    fib_msk[2],
                                    fib_msk[3],
                                    fib_msk[4],
                                    fib_msk[5],
                                    fib_msk[6],
                                    fib_msk[7],
                                    fib_msk[8]);

                    check( flames_dfs_set_history_val(middummr_header, 'I',
                                    "FIBREMASK", fibremask_string),
                           "Could not write FIBREMASK to middummr table");
                }

                uves_msg("Creating columns YCENTER, NEWORD and FIBRE ...");


                cpl_table_new_column(middummr, "NEWORD", CPL_TYPE_INT);
                cpl_table_new_column(middummr, "FIBRE", CPL_TYPE_INT);
                cpl_table_fill_invalid_int(middummr, "NEWORD", -1);
                cpl_table_fill_invalid_int(middummr, "FIBRE", -1);

                cpl_table_cast_column(middummr, "SLOPE", "YQUART1", CPL_TYPE_DOUBLE);
                cpl_table_cast_column(middummr, "SLOPE", "YCENTER", CPL_TYPE_DOUBLE);
                cpl_table_cast_column(middummr, "SLOPE", "YQUART3", CPL_TYPE_DOUBLE);
                cpl_table_multiply_scalar(middummr, "YQUART1", XCENTER[0]);
                cpl_table_multiply_scalar(middummr, "YCENTER", XCENTER[1]);
                cpl_table_multiply_scalar(middummr, "YQUART3", XCENTER[2]);
                cpl_table_add_columns(middummr, "YQUART1", "ORIG");
                cpl_table_add_columns(middummr, "YCENTER", "ORIG");
                cpl_table_add_columns(middummr, "YQUART3", "ORIG");

                check( uves_table_save(middummr, middummr_header, NULL, "middummr.fits", CPL_IO_DEFAULT),
                       "Error saving middummr table");

                uves_msg("matchorders module is now running:");

                check( flames_matchorders(&MATCHTHRES,
                                &limit,
                                &TAB_IO_YSHIFT,
                                FIBRENUMBERS,
                                FIBRESHIFTS,
                                LTAB,
                                &NUMBER,
                                &DYRANGE,
                                &DYSTEP,
                                &fibreson),
                       "matchorders failed");

                NUMBER = NUMBER + fibreson;

                uves_msg("Number value is %d", NUMBER);
                uves_msg("We left matchorders module");
                uves_msg("middummr NEWORD and FIBRE columns filled");


                uves_free_table(&middummr);
                uves_free_propertylist(&middummr_header);
                check( middummr = cpl_table_load("middummr.fits", 1, 1),
                       "Could not load table middummr.fits");

                check( middummr_header = uves_propertylist_load("middummr.fits", 0),
                       "Could not load table middummr.fits header");
                cpl_table_new_column(middummr, "THRES", CPL_TYPE_FLOAT);
                cpl_table_fill_column_window_float(middummr, "THRES", 
                                                   0, cpl_table_get_nrow(middummr), 
                                                   0);

                uves_table_save(middummr, middummr_header, NULL, "middummr.fits", CPL_IO_DEFAULT);
                uves_free_table(&middummr);
                uves_free_propertylist(&middummr_header);

                int INPUTI[3];
                INPUTI[0] = VALI[1];
                INPUTI[1] = SCAN[0];
                INPUTI[2] = SCAN[1];

                uves_msg("CCDSCAN1 = %d, CCDSCAN2 = %d", SCAN[0], SCAN[1]);

                float INPUTR[1];
                INPUTR[0] = VALI[0];

                check_nomsg( flames_select_non_null("middummr.fits", "NEWORD"));
                uves_msg("Creating table %s...", OTAB);
                uves_msg("Entering flames_tracing...");

                check( flames_tracing(in_a,
                                in_b,
                                OTAB,
                                INPUTI,
                                INPUTR,
                                &MAXORDER),
                       "Order tracing failed");

                /* skip plotting */
                uves_msg("Leaving flames_tracing...");

                if (seq == 3)
                {
                    check_nomsg( flames_rename_table("middummr.fits", "third.fits"));
                    check_nomsg( flames_rename_table(OTAB, "first.fits"));
                }
                else
                {
                    check_nomsg( flames_merge_table("third.fits", "middummr.fits"));
                    /* TO BE FIXED:
                         OTAB get empty string units from seq=4 on. 
                         As cpl_table_insert (called by flames_merge_table) 
                         makes a check we need to remve those units 
                     */
                    check_nomsg(uves_tablename_remove_units(OTAB));
                    check_nomsg( flames_merge_table("first.fits", OTAB));
                }
                uves_free_string_const(&P4INT);

            } /* for seq */


            check_nomsg( flames_rename_table("first.fits", OTAB));
            check_nomsg( flames_rename_table("third.fits", "middummr.fits"));

            check_nomsg( flames_sort_table(OTAB, "ORDER", "FIBRE", "X"));
            check_nomsg( flames_select_non_null(OTAB, "Y"));

            uves_msg("Entering flames_fitting...");

            check( flames_fitting(&HALFIBREWIDTH,
                            &MAXFIBRES,
                            &limit,
                            &NUMBER,
                            FIBRENUMBERS,
                            FIBRESHIFTS,
                            OTAB,
                            DEFPOL,
                            in_b,
                            REFSTART,
                            REFSTEP,
                            REFNPIX,
                            &CHIPCHOICE),
                   "flames_fitting failed");

            uves_msg("Leaving flames_fitting");

            cpl_free(FIBRENUMBERS);
            cpl_free(FIBRESHIFTS);

        } /* end scope */
    } /* end scope */

    cleanup:

    uves_free_frameset(&CATNAME);
    uves_free_propertylist(&ltab_header);
    uves_free_image(&ORDREF_image);
    uves_free_image(&htrans);
    uves_free_image(&htrans_orig);
    uves_free_propertylist(&ORDREF_header);
    uves_free_int_const(&fib_msk);
    uves_free_table(&ordertable);
    uves_free_table(&ltab);
    uves_free_table(&middummr);
    uves_free_double(&refstart);
    uves_free_double(&refstep);
    uves_free_int(&refnpix);

    uves_free_propertylist(&middummr_header);
    uves_free_string_const(&fibremask_string);
    return;
}

/**@}*/
