/******************************************************************************
 * $Id$
 *
 * Project:  LibCRS
 * Purpose:  Primary LibCRS include file.
 * Author:   Paul Ramsey
 *
 ******************************************************************************
 * Copyright (c) 2009 Paul Ramsey
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *****************************************************************************/

/******************************************************************************
 *  Theory of Operation:
 * 
 *  Every CRS shall be represented by a CRS*
 *  If the CRS is a shell, behind which is either a PROJCS or GEOGCS,
 *  just test CRS->type to see which one you have.
 *  The interpretation of parameters is governed by the VENDOR flag
 *  The default interpretation of parameters shall be VENDOR_STANDARD
 *
 *****************************************************************************/

/*
** We define a number of lists as arrays rather than trying to 
** dynamically size them. Fortunately, these are pretty small structures
** with not many elements. We can affort to waste a few bytes here and there.
*/
#define CRS_MAX_PARAMS 16

/*
** Debug level. 0 = none. 5 = max.
*/
#define CRS_DEBUG_LEVEL 5

/*
** Rather than trusting FALSE and TRUE or over-riding them.
*/
enum { CRS_FALSE = 0, CRS_TRUE = 1 };

/*
** Return values for public API functions.
*/
enum { CRS_SUCCESS = 0, CRS_FAILURE };

/*
** Component types, so we can use an anonymous pointer type,
** PART, but still cast back into particular types
** when necessary.
*/
enum { 
    CRS_AUTHORITY, 
    CRS_UNIT, 
    CRS_PRIMEM, 
    CRS_SPHEROID, 
    CRS_TOWGS84, 
    CRS_DATUM, 
    CRS_AXIS, 
    CRS_GEOGCS, 
    CRS_PROJECTION, 
    CRS_PARAMETER,
    CRS_PROJCS,
    CRS_CRS, 
	CRS_PARTLIST
};

/*
** We may need more of these. VENDOR_STANDARD is considered
** the "internal standard" to which we transform all others.
*/
typedef enum
{
	VENDOR_STANDARD = 0,
	VENDOR_ESRI,
	VENDOR_CADCORP,
	VENDOR_CSMAP,
	VENDOR_ORACLE
} VENDOR;



/******************************************************************************
 *  Structures of the CRS object.
 *  The CRS object is (overly) complex and modelled on a slightly wide 
 *  interpretation of WKT.
 *****************************************************************************/

/*
** Anonymous component structure used in the WKT parser.
*/
typedef struct
{
	int type;
} PART;

typedef struct 
{
    int    type;
    char  *name;
	char  *id;
	char  *description;
} AUTHORITY;

typedef struct
{
    int         type;
	char       *name;
	double      conversion;
	AUTHORITY  *auth;
} UNIT;

typedef struct 
{
    int         type;
	char       *name;
	double      origin;
	AUTHORITY  *auth;
} PRIMEM;

typedef struct 
{
    int         type;
	char       *name;
	double      semimajor;
	double      invflattening;
	AUTHORITY  *auth;
} SPHEROID;

typedef struct
{
    int         type;
	int         num_params;
	double      params[CRS_MAX_PARAMS];
} TOWGS84;
	
typedef struct
{
    int         type;
	char	   *name;
	SPHEROID   *spheroid;
	TOWGS84    *towgs84;
	AUTHORITY  *auth;
} DATUM;

typedef struct
{
    int         type;
	char       *name;
	char       *orientation;
} AXIS;

typedef struct
{
    int          type;
	char        *name;
	AUTHORITY   *auth;
} PROJECTION;

typedef struct
{
    int          type;
	char        *name;
	double       value;
} PARAMETER;

/**
* Geographic (lat/lon) coordinate system structure.
* Modelled on the WKT geographic coordinate structure.
* Two or zero axis definitions. Any of the sub-components may or may not be 
* present, always test for NULL before de-referencing.
*
* @see PROJCS
*/
typedef struct 
{
    int          type;
	char        *name;
    VENDOR       vendor;
	DATUM       *datum;
	PRIMEM      *primem;
	UNIT        *unit;
	AXIS        *axis[2];
	AUTHORITY   *auth;
} GEOGCS;

/**
* Projected coordinate system structure.
* Modelled on the WKT projected coordinate structure. There are a variable number of
* parameter. Two or zero axis definitions. Any of the sub-components may or may not be 
* present, always test for NULL before de-referencing.
*
* @see GEOGCS
*/
typedef struct 
{
    int          type;
	char        *name;
    VENDOR       vendor;
    GEOGCS      *geogcs;
	PROJECTION  *projection;
	int          num_parameters;
	PARAMETER   *parameters[CRS_MAX_PARAMS];
	UNIT        *unit;
	AXIS        *axis[2];
	AUTHORITY   *auth;
} PROJCS;

/**
* Coordinate reference system struct wrapper. Can be a PROJCS or GEOGCS. 
* Test on the type to determine what it is, then cast.
*
* @see PROJCS
* @see GEOGCS
*/
typedef PART CRS;

/******************************************************************************
 *  CRSDEBUG()
 * 
 *  Debugging calls as macros. When debugging is set to 0, they get optimized 
 *  away. Otherwise, do the test and call a logging method.
 *****************************************************************************/

#if CRS_DEBUG_LEVEL > 0

#define CRSDEBUG(level, msg) \
	do { \
		if (CRS_DEBUG_LEVEL >= level) \
			crsnotice("[%s:%s:%d] " msg, __FILE__, __func__, __LINE__); \
		} while (0);

#define CRSDEBUGF(level, msg, ...) \
	do { \
		if (CRS_DEBUG_LEVEL >= level) \
			crsnotice("[%s:%s:%d] " msg, __FILE__, __func__, __LINE__, __VA_ARGS__); \
	} while (0);
#else

/* Empty prototypes that can be optimised away by the compiler for non-debug builds */
#define CRSDEBUG(level, msg) ((void) 0)
#define CRSDEBUGF(level, msg, ...) ((void) 0)

#endif

/*
** Prototypes for the actual error handling functions. 
*/

/**
* Write a notice to stderr.
*
* @param fmt Input format string.
* @param ... Parameters to the format.
*/
extern void crsnotice(const char *fmt, ...);

/**
* Write a notice to stderr, and then exit.
*
* @param fmt Input format string.
* @param ... Parameters to the format.
*/
extern void crserror(const char *fmt, ...);


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

/**
* Create a CRS struct pointer from a well-known text string.
* Reads the WKT string and create a CRS without validating the 
* contents of the CRS. If you are not sure of the provenance of the WKT,
* use standardization functions before carrying out any further operations.
*
* @param str Input WKT string.
* @param crs Output CRS pointer. Will be NULL on parse error.
* @param errstr Output error string. Will be NULL on parse success.
* @see CRS
*/
extern int crs_from_wkt_string(char *str, CRS **crs, char **errstr);

/**
* Deep free a CRS struct pointer.
* Frees the CRS and all sub-components. Will automatically detect
* whether the CRS is a PROJCS or GEOGCS and carry out the appropriate
* freeing.
*
* @param crs Input CRS pointer.
* @see crs_geogcs_free()
* @see crs_projcs_free()
* @see CRS
*/
extern void crs_free(CRS *crs);

/**
* Deep free a GEOGCS struct pointer.
* Frees the GEOGCS and all sub-components.
*
* @param crs Input GEOGCS pointer.
* @see GEOGCS
*/
extern void crs_geogcs_free(GEOGCS *geogcs);

/**
* Deep free a PROJCS struct pointer.
* Frees the PROJCS and all sub-components.
*
* @param crs Input PROJCS pointer.
* @see PROJCS
*/
extern void crs_projcs_free(PROJCS *projcs);
