/* $Id: sinfo_star_index.c,v 1.9 2012-03-03 10:18:26 amodigli Exp $
 *
 * This file is part of the X-Shooter 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
/*
 * $Author: amodigli $
 * $Date: 2012-03-03 10:18:26 $
 * $Revision: 1.9 $
 * $Name: not supported by cvs2svn $
 */



#ifdef HAVE_CONFIG_H
#include <config.h>          /* allows the program compilation */
#endif

#include <cpl.h>
#include <string.h>
#include <math.h>


//#include "sinfo_pro_save.h"
//#include "sinfo_pfits.h"
//#include "sinfo_utilities_scired.h"
//#include "sinfo_hidden.h"
//#include "sinfo_functions.h"
#include "uves_error.h"
#include "uves_msg.h"
#include "uves_utils_wrappers.h"
//#include "sinfo_globals.h"
#include "uves_star_index.h"

struct _star_index_
{
    cpl_table* index_table;
    char* fits_file_name;
    int index_size;
    cpl_table** cache;
    int cache_size;
    int* cache_index;
};

//typedef struct _star_index_ star_index;
static const char* COL_NAME_EXTID 	= "ext_id";
static const char* COL_NAME_NAME 	= "name";
static const char* COL_NAME_OBJECT 	= "OBJECT";
static const char* COL_NAME_RA 	= "ra";
static const char* COL_NAME_DEC 	= "dec";

static const char* COL_NAME_RA_DEG 	= "RA_DEG";
static const char* COL_NAME_DEC_DEG 	= "DEC_DEG";

static star_index* star_index_construct(const char* fits_file);
static void star_index_destruct(star_index* pindex);
// private functions

static star_index* star_index_construct(const char* fits_file)
{
    star_index* pret = cpl_malloc(sizeof(star_index));
    pret->index_size = 0;
    pret->index_table = 0;
    pret->cache_size = 0;
    pret->cache = 0;
    pret->cache_index = 0;
    if (fits_file)
    {
        size_t bt = strlen(fits_file) * sizeof(*fits_file)+1;
        pret->fits_file_name = cpl_malloc(bt);
        strcpy(pret->fits_file_name, fits_file);
    }
    else
    {
        pret->fits_file_name = 0;
    }
    return pret;
}

static void star_index_destruct(star_index* pindex)
{
    if(pindex)
    {
        if (pindex->cache)
        {
            int i = 0;
            for ( i = 0; i < pindex->cache_size; i++)
            {
                cpl_table_delete(pindex->cache[i]);
            }
            cpl_free(pindex->cache);
            pindex->cache = 0;
            pindex->cache_size = 0;
        }
        cpl_table_delete(pindex->index_table);
        if(pindex->fits_file_name)
        {
            cpl_free(pindex->fits_file_name);
        }
        cpl_free(pindex->cache_index);
        cpl_free(pindex);
    }

}

/**@{*/

star_index* star_index_create(void)
{
    star_index* pret = star_index_construct(0);
    // initialize table
    check_nomsg(pret->index_table = cpl_table_new(pret->index_size));
    // create columns ext_id, name, ra, dec
    cpl_table_new_column(pret->index_table, COL_NAME_EXTID, CPL_TYPE_INT);
    cpl_table_new_column(pret->index_table, COL_NAME_NAME, CPL_TYPE_STRING);
    cpl_table_new_column(pret->index_table, COL_NAME_RA, CPL_TYPE_DOUBLE);
    cpl_table_new_column(pret->index_table, COL_NAME_DEC, CPL_TYPE_DOUBLE);

    return pret;
    cleanup:
    star_index_destruct(pret);
    return 0;
}
star_index* star_index_load(const char* fits_file)
{
    star_index* pret = star_index_construct(fits_file);
    // load index table from the file
    cpl_table* pindex = 0;
    check_nomsg(pindex = cpl_table_load(fits_file,1,0));
    // TODO check_nomsg the structure of the table
    pret->index_table = pindex;
    check_nomsg(pret->index_size = cpl_table_get_nrow(pindex));
    return pret;
    cleanup:
	uves_print_rec_status(0);
    star_index_destruct(pret);
    cpl_error_reset();
    return 0;
}
void star_index_delete(star_index* pindex)
{
    star_index_destruct(pindex);
}
int star_index_add(star_index* pindex, double RA, double DEC, const char* star_name, cpl_table* ptable)
{
    int retval = 0;
    if (pindex)
    {
        // expand the index table
        check_nomsg(cpl_table_insert_window(pindex->index_table, pindex->index_size++, 1));
        if (!pindex->cache)
        {
            pindex->cache_size = 1;
            pindex->cache = cpl_malloc(sizeof(cpl_table*) * pindex->cache_size);
            pindex->cache_index = cpl_malloc(sizeof(pindex->cache_index[0]) * pindex->cache_size);
        }
        else
        {
            // add new entry
            pindex->cache_size++;
            pindex->cache = cpl_realloc(pindex->cache, sizeof(cpl_table*) * pindex->cache_size);
        }
        check_nomsg(pindex->cache[pindex->cache_size - 1] = cpl_table_duplicate(ptable));
        // fill the index table with values
        check_nomsg(cpl_table_set_string(pindex->index_table, COL_NAME_NAME, pindex->index_size - 1 ,star_name));
        check_nomsg(cpl_table_set(pindex->index_table, COL_NAME_RA, pindex->index_size - 1 ,RA));
        check_nomsg(cpl_table_set(pindex->index_table, COL_NAME_DEC, pindex->index_size - 1,DEC));
        check_nomsg(cpl_table_set_int(pindex->index_table, COL_NAME_EXTID, pindex->index_size - 1 ,pindex->index_size + 1));
        retval = pindex->index_size;
    }
    return retval;

    cleanup:
    //printf ("error: %s\n", cpl_error_get_message());
    return 0;
}

/* not used */
int star_index_remove_by_name(star_index* pindex, const char* starname)
{
    int i = 0;
    int index_pos = -1;
    for (i = 0; i < pindex->index_size; i++)
    {
        const char* curr_star_name = 0;
        check_nomsg(curr_star_name = cpl_table_get_string(pindex->index_table, COL_NAME_NAME, i));
        if (strcmp(curr_star_name, starname) == 0)
        {
            index_pos = i;
            break;
        }
    }
    if (index_pos >= 0)
    {
        // star is found
        // clear only the index table, real data would be cleaned during save operation
        cpl_table_set_int(pindex->index_table, COL_NAME_EXTID, index_pos, -1);
        if (index_pos - pindex->index_size + pindex->cache_size  >= 0)
        {
            // clear cache
            int cache_index = index_pos - pindex->index_size + pindex->cache_size;
            cpl_table_delete(pindex->cache[cache_index]);
            pindex->cache[cache_index] = 0;
        }
    }
    cleanup:
    return index_pos;
}

int star_index_save(star_index* pindex, const char* fits_file)
{
    int i  = 0;
    int inull = 0;
    cpl_table* pnew_index = 0;
    int nrows = 0;
    // firstly save the index table - deleted entries should be removed firstly
    check_nomsg(cpl_table_unselect_all(pindex->index_table));
    check_nomsg(cpl_table_or_selected_int(pindex->index_table, COL_NAME_EXTID, CPL_EQUAL_TO, -1));
    // inverse selection
    check_nomsg(cpl_table_not_selected(pindex->index_table));
    check_nomsg(pnew_index = cpl_table_extract_selected(pindex->index_table));

    nrows = cpl_table_get_nrow(pnew_index);
    //	printf("rows to save[%d]\n", nrows);
    for (i = 0; i < nrows; i++)
    {
        cpl_table_set_int(pnew_index, COL_NAME_EXTID, i, i+2); // ext in fits starts from 1, and another 1 is used by index_table
    }
    //	printf("writing index [%s]\n", fits_file);
    check_nomsg(cpl_table_save(pnew_index, NULL, NULL, fits_file, CPL_IO_CREATE));
    cpl_table_delete(pnew_index);
    pnew_index = 0;
    // save the data
    for (i = 0;i < pindex->index_size; i++)
    {
        //		printf("saving ext [%d]\n", i);
        // 2. save cache
        int saved_ext = cpl_table_get_int(pindex->index_table, COL_NAME_EXTID, i, &inull);
        //		printf("saving 1\n");
        if (saved_ext > 0) // check_nomsg that was not removed
        {
            cpl_table* ptable = 0;
            //			printf("saving 2\n");
            if (i < pindex->index_size - pindex->cache_size)
            {
                //				printf("saving 3\n");
                check_nomsg(ptable = cpl_table_load(pindex->fits_file_name, saved_ext, 0));
            }
            else
            {
                //				printf("saving 4\n");
                ptable = cpl_table_duplicate(pindex->cache[i - pindex->index_size + pindex->cache_size ]);
            }
            //			printf("saving 5\n");
            check_nomsg(cpl_table_save(ptable, NULL, NULL, fits_file, CPL_IO_EXTEND));
            //			printf("saving 6\n");
            cpl_table_delete(ptable);
            //			printf("saving 7\n");
        }
        //		printf("saving 8\n");
    }
    //	printf("saving exit\n");
    return nrows;
    cleanup:
    //	printf("error during save\n");
    return 0;
}


static cpl_table*
uves_table_change_cell_format(const char* fname, const int row)
{
	cpl_table* table = cpl_table_load(fname, 1, 0);
    int status=0;
    int ndata = cpl_table_get_int(table,"NDATA",row,&status);
    const cpl_array* a_lambda;
    const cpl_array* a_bin_width;
    const cpl_array* a_f_lambda;

    a_lambda    = cpl_table_get_array(table,"LAMBDA",row);
    a_bin_width = cpl_table_get_array(table,"BIN_WIDTH",row);
    a_f_lambda  = cpl_table_get_array(table,"F_LAMBDA",row);

    cpl_table* tab=cpl_table_new(ndata);
    cpl_table_new_column(tab,"LAMBDA",CPL_TYPE_DOUBLE);
    cpl_table_new_column(tab,"BIN_WIDTH",CPL_TYPE_DOUBLE);
    cpl_table_new_column(tab,"FLUX",CPL_TYPE_DOUBLE);
    cpl_table_fill_column_window_double(tab,"LAMBDA",0,ndata,0);
    cpl_table_fill_column_window_double(tab,"BIN_WIDTH",0,ndata,0);
    cpl_table_fill_column_window_double(tab,"FLUX",0,ndata,0);
    double* plambda=cpl_table_get_data_double(tab,"LAMBDA");
    double* pbin_width=cpl_table_get_data_double(tab,"BIN_WIDTH");
    double* pflambda=cpl_table_get_data_double(tab,"FLUX");

    for (int i = 0; i < ndata; i++) {

    	plambda[i]=cpl_array_get(a_lambda,i,&status);
    	pbin_width[i]=cpl_array_get(a_bin_width,i,&status);
    	pflambda[i]=cpl_array_get(a_f_lambda,i,&status);

    }

    //cpl_table_save(tab,NULL,NULL,"pippo.fits",CPL_IO_DEFAULT);
    cpl_table_delete(table);
    return tab;
}

cpl_table* star_index_get(star_index* pindex, double RA, double DEC, double RA_EPS, double DEC_EPS, const char** pstar_name)
{
    int i = 0;
    cpl_table* pret = 0;
    int inull = 0;
    int star_index=0;
    //uves_msg_warning("index filename %s, size %d",pindex->fits_file_name,pindex->index_size);
    //cpl_table_dump(pindex->index_table,0,2,stdout);
    for (i = 0; i < pindex->index_size; i++)
    {
        double curr_ra = 0;
        double curr_dec = 0;
        int ext_id = 0;
        if(cpl_table_has_column(pindex->index_table,COL_NAME_RA)) {
        	ext_id = cpl_table_get_int(pindex->index_table, COL_NAME_EXTID, i ,&inull);
        } else if(cpl_table_has_column(pindex->index_table,COL_NAME_RA_DEG)) {
        	ext_id=1;
        }

        if(cpl_table_has_column(pindex->index_table,COL_NAME_RA)) {
            curr_ra = cpl_table_get(pindex->index_table, COL_NAME_RA, i,&inull);
        } else if(cpl_table_has_column(pindex->index_table,COL_NAME_RA_DEG)) {
        	curr_ra = cpl_table_get(pindex->index_table, COL_NAME_RA_DEG, i,&inull);
        }
        if(cpl_table_has_column(pindex->index_table,COL_NAME_DEC)) {
            curr_dec = cpl_table_get(pindex->index_table, COL_NAME_DEC, i,&inull);
        } else if(cpl_table_has_column(pindex->index_table,COL_NAME_DEC_DEG)) {
        	curr_dec = cpl_table_get(pindex->index_table, COL_NAME_DEC_DEG, i,&inull);
        }
        if ( (fabs(curr_ra - RA) < RA_EPS) && (fabs(curr_dec - DEC) < DEC_EPS) )
        {
            // found
            // retrieve the data
        	star_index = i;
            if (i - pindex->index_size + pindex->cache_size  >= 0)
            {
                // data is in cache
                pret = cpl_table_duplicate(pindex->cache[i - pindex->index_size + pindex->cache_size ]);
            }
            else
            {
                // data is on disk

                if(cpl_table_has_column(pindex->index_table,"NDATA")) {
                	uves_msg("convert std-star table cell format");
                    pret = uves_table_change_cell_format(pindex->fits_file_name,
                		star_index);
                } else {
                	uves_msg("X-shoot like std-star table format");
                	pret = cpl_table_load(pindex->fits_file_name, ext_id, 0);
                }

            }
            if (pret && pstar_name)
            {
            	if(cpl_table_has_column(pindex->index_table,COL_NAME_NAME)) {
                   *pstar_name = cpl_table_get_string(pindex->index_table, COL_NAME_NAME, i);
            	} else if(cpl_table_has_column(pindex->index_table,COL_NAME_OBJECT)) {
            	   *pstar_name = cpl_table_get_string(pindex->index_table, COL_NAME_OBJECT, i);
            	}
            }
            break;
        }
    }
    cleanup:
	uves_print_rec_status(0);
    return pret;
}

void star_index_dump(star_index* pindex, FILE* pfile)
{
    cpl_table_dump(pindex->index_table, 0, 	cpl_table_get_nrow(pindex->index_table), pfile);
}
/**@}*/
