/**************************************************************

phot_head.c (C-Munipack project)
Photometry file header
Copyright (C) 2003 David Motl, dmotl@volny.cz

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.

**************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "console.h"
#include "comfun.h"
#include "header.h"

/*************************   Constants  *************************/

#define ALLOC_BY 	64

/********************   Local functions    **********************/

/* Init head item */
static CmpackHeadItem *headitem_create(const char *key)
{
	CmpackHeadItem *item = cmpack_calloc(1, sizeof(CmpackHeadItem));
	item->key = trim(key);
	return item;
}

/* Change value of header item */
void headitem_setval(CmpackHeadItem *item, const char *val)
{
	cmpack_free(item->val);
	item->val = cmpack_strdup(val);
}

void headitem_setcom(CmpackHeadItem *item, const char *com)
{
	cmpack_free(item->com);
	item->com = cmpack_strdup(com);
}

/* Destroy head item */
static void headitem_free(CmpackHeadItem *item)
{
	cmpack_free(item->key);
	cmpack_free(item->val);
	cmpack_free(item->com);
	cmpack_free(item);
}

/* Clear all parameters in the section */
static void head_clear(CmpackHeader *head)
{
	int j;

	for (j=0; j<head->count; j++)
		headitem_free(head->list[j]);
	cmpack_free(head->list);
	head->list = NULL;
	head->count = head->capacity = 0;
	hash_clear(&head->hash);
}

static void headitem_write(CmpackHeadItem *item, FILE *to)
{
	if (item->key && strlen(item->key) && item->val && strlen(item->val)) {
		if (strchr(item->val, 13) || strchr(item->val, 10)) {
			if (item->com && strlen(item->com)) 
				fprintf(to, "\t<%s>\n%s\n<!-- %s -->\n</%s>\n", item->key, item->val, item->com, item->key);
			else
				fprintf(to, "\t<%s>\n%s\n</%s>\n", item->key, item->val, item->key);
		} else {
			if (item->com && strlen(item->com)) 
				fprintf(to, "\t<%s>%s <!-- %s --></%s>\n", item->key, item->val, item->com, item->key);
			else
				fprintf(to, "\t<%s>%s</%s>\n", item->key, item->val, item->key);
		}
	}
}	

/********************   Public functions    ***********************/

/* Init header */
void header_init(CmpackHeader *head)
{
	memset(head, 0, sizeof(CmpackHeader));
	hash_init(&head->hash);
}	

/* Free allocated memory */
void header_clear(CmpackHeader *head)
{
	head_clear(head);
}	

/* Make copy of header */
void header_copy(CmpackHeader *dst, const CmpackHeader *src)
{
	int j;
	
	head_clear(dst);
	if (src->count>0) {
		dst->capacity = dst->count = src->count;
		dst->list = (CmpackHeadItem**) cmpack_calloc(dst->capacity, sizeof(CmpackHeadItem*));
		for (j=0; j<src->count; j++) {
			dst->list[j] = headitem_create(src->list[j]->key);
			headitem_setval(dst->list[j], src->list[j]->val);
			headitem_setcom(dst->list[j], src->list[j]->com);
			hash_insert(&dst->hash, dst->list[j]->key, dst->list[j]);
		}
	}
}

/* Dump content of header */
void header_dump(CmpackConsole *con, CmpackHeader *head)
{
	int i;
	char aux[MAXLINE];

	printout(con, 1, "\tHeader:");
	for (i=0; i<head->count; i++) {
		if (head->list[i]->key && head->list[i]->val) {
			sprintf(aux, "\t\t%s", head->list[i]->key);
			if (head->list[i]->val) {
				strcat(aux, " = ");
				strcat(aux, head->list[i]->val);
			}
			if (head->list[i]->com) {
				strcat(aux, " ; ");
				strcat(aux, head->list[i]->com);
			}
			printout(con, 1, aux);
		}
	}
}	

/* Trim all leading and trailing whitespaces in keys, values and comments */
void header_normalize(CmpackHeader *head)
{
	int i;
	char *str;

	for (i=0; i<head->count; i++) {
		if (needs_trim(head->list[i]->key)) {
			str = trim(head->list[i]->key);
			cmpack_free(head->list[i]->key);
			head->list[i]->key = str;
		}
		if (needs_trim(head->list[i]->val)) {
			str = trim(head->list[i]->val);
			cmpack_free(head->list[i]->val);
			head->list[i]->val = str;
		}
		if (needs_trim(head->list[i]->com)) {
			str = trim(head->list[i]->com);
			cmpack_free(head->list[i]->com);
			head->list[i]->com = str;
		}
	}
}

/* Load content from a file */
void header_load_xml(CmpackHeader *head, CmpackElement *parent)
{
	CmpackNode *node = parent->node.firstChild;
	while (node) {
		if (node->nodeType == CMPACK_XML_ELEMENT_NODE) {
			int index = header_add(head, node->nodeName);
			if (index>=0) {
				CmpackHeadItem *item = head->list[index];
				headitem_setval(item, cmpack_xml_value((CmpackElement*)node, NULL));
				headitem_setcom(item, cmpack_xml_comment_text(cmpack_xml_comment(node)));
			}
		}
		node = node->nextSibling;
	}
}

/* Write content to the file */
void header_write_xml(CmpackHeader *head, FILE *to)
{
	int i;

	header_normalize(head);
	for (i=0; i<head->count; i++) 
		headitem_write(head->list[i], to);
}

int header_add(CmpackHeader *head, const char *key)
{
	int index;

	if (key && strlen(key) && !hash_search(&head->hash, key)) {
		/* Add new item */
		if (head->count>=head->capacity) {
			head->capacity += ALLOC_BY;
			head->list = (CmpackHeadItem**)cmpack_realloc(head->list, head->capacity*sizeof(CmpackHeadItem*));
		}
		index = head->count;
		head->list[index] = headitem_create(key);
		hash_insert(&head->hash, key, head->list[head->count]);
		head->count++;
		return index;
	}
	return -1;
}

CmpackHeadItem *header_finditem(CmpackHeader *head, const char *key)
{
	return hash_search(&head->hash, key);
}

int header_find(CmpackHeader *head, const char *key)
{
	int i;

	CmpackHeadItem *item = header_finditem(head, key);
	for (i=0; i<head->count; i++) {
		if (head->list[i] == item)
			return i;
	}
	return -1;
}

void header_delete(CmpackHeader *head, int index)
{
	CmpackHeadItem *item;

	if (index>=0 && index<head->count) {
		item = head->list[index];
		/* Delete item */
		hash_delete(&head->hash, item->key, item);
		headitem_free(item);
		head->count--;
		if (head->count==0) {
			/* If there is no item, clear the list */
			cmpack_free(head->list);
			head->list = NULL;
			head->count = head->capacity = 0;
		} else {
			/* Pack the list (move all following items to the left) */
			size_t len = head->count - index;
			if (len>0) {
				CmpackHeadItem **aux = (CmpackHeadItem**)cmpack_malloc(len*sizeof(CmpackHeadItem*));
				memcpy(aux, item+1, len*sizeof(CmpackHeadItem*));
				memcpy(item, aux, len*sizeof(CmpackHeadItem*));
				cmpack_free(aux);
			}
		}
	}
}

/********************   File header   *******************************/

/* Sets value and comment of specified key */
void header_pkys(CmpackHeader *head, const char *key, const char *val, const char *com)
{
	CmpackHeadItem *item = header_finditem(head, key);
	if (!item) {
		int index = header_add(head, key);
		if (index>=0)
			item = head->list[index];
	}
	if (item) {
		headitem_setval(item, val);
		headitem_setcom(item, com);
	}
}

/* Sets value and comment of specified key */
void header_pkyi(CmpackHeader *head, const char *key, int val, const char *com)
{
	char buf[16];

	sprintf(buf, "%d", val);
	header_pkys(head, key, buf, com);
}

/* Sets value and comment of specified key */
void header_pkye(CmpackHeader *head, const char *key, double val, int prec, const char *com)
{
	char buf[256];

	if (prec<0)
		prec = 0;
	else if (prec>16)
		prec = 16;
	sprintf(buf, "%.*e", prec, val);
	if (strspn(buf, "+-0.eE")==strlen(buf))
		strcpy(buf, "0.0");
	header_pkys(head, key, buf, com);
}

/* Sets value and comment of specified key */
void header_pkyf(CmpackHeader *head, const char *key, double val, int prec, const char *com)
{
	char buf[256];

	if (prec<0)
		prec = 0;
	else if (prec>16)
		prec = 16;
	sprintf(buf, "%.*f", prec, val);
	if (strspn(buf, "+-0.eE")==strlen(buf))
		strcpy(buf, "0.0");
	header_pkys(head, key, buf, com);
}

/* Sets value and comment of specified key */
void header_pkyg(CmpackHeader *head, const char *key, double val, int prec, const char *com)
{
	char buf[256];

	if (prec<0)
		prec = 0;
	else if (prec>16)
		prec = 16;
	sprintf(buf, "%.*g", prec, val);
	if (strspn(buf, "+-0.eE")==strlen(buf))
		strcpy(buf, "0.0");
	header_pkys(head, key, buf, com);
}

/* Gets value of specified key */
const char *header_gkys(CmpackHeader *head, const char *key)
{
	CmpackHeadItem *item = header_finditem(head, key);
	return (item!=NULL ? item->val : NULL);
}

/* Gets comment of specified key */
const char *header_gkyc(CmpackHeader *head, const char *key)
{
	CmpackHeadItem *item = header_finditem(head, key);
	return (item!=NULL ? item->com : NULL);
}

/* Gets value and comment of specified key */
int header_gkyi(CmpackHeader *head, const char *key, int *value)
{
	char *endptr;

	const char *str = header_gkys(head, key);
	if (str) {
		*value = strtol(str, &endptr, 10);
		if (str!=endptr)
			return CMPACK_ERR_OK;
	}
	return CMPACK_ERR_UNDEF_VALUE;
}

/* Gets value and comment of specified key */
int header_gkyd(CmpackHeader *head, const char *key, double *value)
{
	char *endptr;

	const char *str = header_gkys(head, key);
	if (str) {
		*value = strtod(str, &endptr);
		if (str!=endptr)
			return CMPACK_ERR_OK;
	}
	return CMPACK_ERR_UNDEF_VALUE;
}

/* Get key, value and comment triplet by its index. The first record has index 0.
 * Returns nonzero on success or zero on failure.
 */
int header_gkyn(CmpackHeader *head, int index, const char **key, const char **val, const char **com)
{
	if (index>=0 && index<head->count) {
		if (key) *key = head->list[index]->key;
		if (val) *val = head->list[index]->val;
		if (com) *com = head->list[index]->com;
		return 1;
	}
	if (key) *key = NULL;
	if (val) *val = NULL;
	if (com) *com = NULL;
	return 0;
}

/* Deletes specified key */
void header_dkey(CmpackHeader *head, const char *key)
{
	int index = header_find(head, key);
	if (index>=0)
		header_delete(head, index);
}
