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

comfun.c (C-Munipack project)
Common functions, macros, etc.
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 <stdint.h>
#include <ctype.h>
#include <limits.h>
#include <float.h>

#include "config.h"

#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/stat.h>
#include <sys/types.h>
#endif

#include "comfun.h"

int pixrange(int bitpix, double *minvalue, double *maxvalue)
{
	switch (bitpix) 
	{
	case CMPACK_BITPIX_SSHORT:
		if (minvalue) *minvalue = INT16_MIN;
		if (maxvalue) *maxvalue = INT16_MAX;
		return 1;
	case CMPACK_BITPIX_USHORT:
		if (minvalue) *minvalue = 0.0;
		if (maxvalue) *maxvalue = UINT16_MAX;
		return 1;
	case CMPACK_BITPIX_SLONG:
		if (minvalue) *minvalue = INT32_MIN;
		if (maxvalue) *maxvalue = INT32_MAX;
		return 1;
	case CMPACK_BITPIX_ULONG:
		if (minvalue) *minvalue = 0.0;
		if (maxvalue) *maxvalue = UINT32_MAX;
		return 1;
	case CMPACK_BITPIX_FLOAT:
		if (minvalue) *minvalue = -FLT_MAX;
		if (maxvalue) *maxvalue = FLT_MAX;
		return 1;
	case CMPACK_BITPIX_DOUBLE:
		if (minvalue) *minvalue = -DBL_MAX;
		if (maxvalue) *maxvalue = DBL_MAX;
		return 1;
	default:
		if (minvalue) *minvalue = 0.0;
		if (maxvalue) *maxvalue = 0.0;
		return 0;
	}
}

const char *pixformat(int bitpix)
{
	switch (bitpix) 
	{
	case CMPACK_BITPIX_SSHORT:
		return "Signed short int (2 bytes)";
	case CMPACK_BITPIX_USHORT:
		return "Unsigned short int (2 bytes)";
	case CMPACK_BITPIX_SLONG:
		return "Signed int (4 bytes)";
	case CMPACK_BITPIX_ULONG:
		return "Unsigned int (4 bytes)";
	case CMPACK_BITPIX_FLOAT:
		return "Single precision FP (4 bytes)";
	case CMPACK_BITPIX_DOUBLE:
		return "Double precision FP (8 bytes)";
	default:
		return "Unknown data format";
	}
}

int cmpack_get_param_int(CmpackParam param)
{
	switch (param)
	{
	case CMPACK_PARAM_JD_PRECISION:
		return JD_PRECISION;
	case CMPACK_PARAM_POS_PRECISION:
		return POS_PRECISION;
	case CMPACK_PARAM_EXP_PRECISION:
		return EXP_PRECISION;
	case CMPACK_PARAM_TEMP_PRECISION:
		return TEMP_PRECISION;
	case CMPACK_PARAM_AMASS_PRECISION:
		return AMASS_PRECISION;
	case CMPACK_PARAM_ALT_PRECISION:
		return ALT_PRECISION;
	case CMPACK_PARAM_MAG_PRECISION:
		return MAG_PRECISION;
	case CMPACK_PARAM_WCS_SUPPORTED:
#ifdef CMUNIPACK_HAVE_WCSLIB
		return 1;
#else
		return 0;
#endif
	default:
		return 0;
	}
}

long cmpack_get_param_long(CmpackParam param)
{
	return cmpack_get_param_int(param);
}

static int iswhitespace(char ch)
{
	return (ch > 0 && ch <= ' ');
}

int needs_trim(const char *str)
{
	if (str && *str!='\0') {
		if (iswhitespace(*str) || iswhitespace(str[strlen(str)-1]))
			return 1;
	}
	return 0;
}

/* Trim leading and trailing white spaces */
char *trim(const char *str)
{
	return rtrim(ltrim(str));
}

/* Trim trailing white spaces */
char *rtrim(char *str)
{
	if (str) {
		char *ptr = str + strlen(str);
		while (ptr>str && iswhitespace(*(ptr-1)))
			ptr--;
		*ptr = '\0';
	}
	return str;
}

/* Trim leading white spaces */
char *ltrim(const char *str)
{
	size_t len;
	char *buf;

	if (str) {
		const char *ptr = str;
		while (*ptr!='\0' && iswhitespace(*ptr))
			ptr++;
		len = strlen(ptr);
		buf = (char*)cmpack_malloc((len+1)*sizeof(char));
		strcpy(buf, ptr);
		return buf;
	}
	return NULL;
}

/* Find string in a memory buffer */
const char *memstr(const char *c, char *s, size_t n)
{
	const char *p;
	size_t needlesize = strlen(s);
	for (p = c; p <= (c-needlesize+n); p++) {
		if (memcmp(p, s, needlesize) == 0)
			return p; /* found */
	}
	return NULL;
}

/* Encode string that it can be safely written to XML file */
char *xml_encode_string(const char *str)
{
	int len;
	const char *sptr;
	char *buf = NULL, *dptr;

	if (str) {
		/* Compute number of bytes needed to store the data */
		len = 0;
		for (sptr=str; *sptr!='\0'; sptr++) {
			char ch = *sptr;
			if (ch == '<' || ch == '>')
				len += 3;
			else if (ch=='&')
				len += 4;
			else if (ch<0 || ch>=' ')
				len += 1;
		}
		buf = (char*)cmpack_malloc((len+1)*sizeof(char));
		for (sptr=str, dptr=buf; *sptr!='\0'; sptr++) {
			char ch = *sptr;
			if (ch == '<') {
				memcpy(dptr, "&lt;", 3);
				dptr += 3;
			} else if (ch == '>') {
				memcpy(dptr, "&gt;", 3);
				dptr += 3;
			} else if (ch=='&') {
				memcpy(dptr, "&amp;", 4);
				dptr += 4;
			} else if (ch<0 || ch>=' ') 
				*dptr++ = ch;
		}
		*dptr = '\0';
	}
	return buf;
}

int strcpy_truncate(char *buf, int bufsize, const char *str)
{
	int len;

	if (bufsize<=0 || !buf)
		return 0;

	if (!str) {
		buf[0] = '\0';
		return 0;
	}
	
	len = (int)strlen(str);
	if (len>=bufsize)
		len = bufsize-1;
	memcpy(buf, str, len);
	buf[len] = '\0';
	return len;
}

char *fits_unquote(char *str)
{
	if (str) {
		const char *sptr = str;
		char *dptr = str;
		while (*sptr!='\0' && *sptr==' ')
			sptr++;
		if (*sptr=='\'') {
			/* Quoted string */
			int state = 0;
			while (*(++sptr)!='\0') {
				char ch = *sptr;
				if (state==0) {
					if (ch=='\'')
						state = 1;
					else
						*dptr++ = ch;
				} else {
					if (ch=='\'') {
						*dptr++ = '\'';
						state = 0;
					} else
						break;
				}
			}
			*dptr = '\0';
			if (dptr>str)
				rtrim(str+1);
		}
	}
	return str;
}

/*-----------------------   MUTEX   ---------------------------*/

#ifdef _WIN32

#define MUTEX_T HANDLE

void cmpack_mutex_init(CmpackMutex *mutex)
{
	*(MUTEX_T*)mutex = CreateMutex(0, 0, 0);
}

void cmpack_mutex_lock(CmpackMutex *mutex)
{
	WaitForSingleObject(*(MUTEX_T*)mutex, INFINITE);
}

void cmpack_mutex_unlock(CmpackMutex *mutex)
{
    ReleaseMutex(*(MUTEX_T*)mutex);
}

void cmpack_mutex_destroy(CmpackMutex *mutex)
{
	if (*(MUTEX_T*)mutex != INVALID_HANDLE_VALUE)
		CloseHandle(*(MUTEX_T*)mutex);
}

#else

#include <pthread.h>

#define MUTEX_T pthread_mutex_t

void cmpack_mutex_init(CmpackMutex *mutex)
{
    pthread_mutex_init((MUTEX_T*)mutex, NULL);
}

void cmpack_mutex_lock(CmpackMutex *mutex)
{
    pthread_mutex_lock((MUTEX_T*)mutex);
}

void cmpack_mutex_unlock(CmpackMutex *mutex)
{
    pthread_mutex_unlock((MUTEX_T*)mutex);
}

void cmpack_mutex_destroy(CmpackMutex *mutex)
{
	pthread_mutex_destroy((MUTEX_T*)mutex);
}

#endif
