/* $Id: xsh_star_index.c,v 1.3 2011-12-02 14:15:28 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: 2011-12-02 14:15:28 $
 * $Revision: 1.3 $
 * $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 "xsh_pro_save.h"
//#include "xsh_pfits.h"
//#include "xsh_utilities_scired.h"
//#include "xsh_hidden.h"
//#include "xsh_functions.h"
#include "xsh_error.h"
#include "xsh_utils_wrappers.h"
//#include "xsh_globals.h"
#include "xsh_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_RA 	= "ra";
static const char* COL_NAME_DEC 	= "dec";

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
	pret->index_table = cpl_table_new(pret->index_size);
	// create columns ext_id, name, ra, dec
	check(cpl_table_new_column(pret->index_table, COL_NAME_EXTID, CPL_TYPE_INT));
	check(cpl_table_new_column(pret->index_table, COL_NAME_NAME, CPL_TYPE_STRING));
	check(cpl_table_new_column(pret->index_table, COL_NAME_RA, CPL_TYPE_DOUBLE));
	check(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(pindex = cpl_table_load(fits_file,1,0));
	// TODO check the structure of the table
	pret->index_table = pindex;
	check(pret->index_size = cpl_table_get_nrow(pindex));
	return pret;
	cleanup:
	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(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(pindex->cache[pindex->cache_size - 1] = cpl_table_duplicate(ptable));
		// fill the index table with values
		check(cpl_table_set_string(pindex->index_table, COL_NAME_NAME, pindex->index_size - 1 ,star_name));
		check(cpl_table_set(pindex->index_table, COL_NAME_RA, pindex->index_size - 1 ,RA));
		check(cpl_table_set(pindex->index_table, COL_NAME_DEC, pindex->index_size - 1,DEC));
		check(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:
	return 0;
}

int start_index_get_size(star_index* pindex)
{
	return pindex ? pindex->index_size : 0;
}

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(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(cpl_table_unselect_all(pindex->index_table));
	check(cpl_table_or_selected_int(pindex->index_table, COL_NAME_EXTID, CPL_EQUAL_TO, -1));
	// inverse selection
	check(cpl_table_not_selected(pindex->index_table));
	check(pnew_index = cpl_table_extract_selected(pindex->index_table));

	nrows = cpl_table_get_nrow(pnew_index);
	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
	}
	check(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++)
	{
		// 2. save cache
		int saved_ext = cpl_table_get_int(pindex->index_table, COL_NAME_EXTID, i, &inull);
		if (saved_ext > 0) // check that was not removed
		{
			cpl_table* ptable = 0;
			if (i < pindex->index_size - pindex->cache_size)
			{
				check(ptable = cpl_table_load(pindex->fits_file_name, saved_ext, 0));
			}
			else
			{
				ptable = cpl_table_duplicate(pindex->cache[i - pindex->index_size + pindex->cache_size ]);
			}
			check(cpl_table_save(ptable, NULL, NULL, fits_file, CPL_IO_EXTEND));
			cpl_table_delete(ptable);
		}
	}
	cleanup:
	return nrows;
}
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;

	for (i = 0; i < pindex->index_size; i++)
	{
		double curr_ra = 0;
		double curr_dec = 0;
		int ext_id = 0;

		check(ext_id = cpl_table_get_int(pindex->index_table, COL_NAME_EXTID, i ,&inull));
		check(curr_ra = cpl_table_get(pindex->index_table, COL_NAME_RA, i,&inull));
		check(curr_dec = cpl_table_get(pindex->index_table, COL_NAME_DEC, i,&inull));
		if ((ext_id > 0) && (fabs(curr_ra - RA) < RA_EPS) && (fabs(curr_dec - DEC) < DEC_EPS))
		{
			// found
			// retrieve the data
			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
				pret = cpl_table_load(pindex->fits_file_name, ext_id, 0);
			}
			if (pret && pstar_name)
			{
				check(*pstar_name = cpl_table_get_string(pindex->index_table, COL_NAME_NAME, i));
			}
			break;
		}
	}
	cleanup:
	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);
}
