#include <stdlib.h>
#include <stdio.h>
#include <grass/G3d.h>
#include "G3d_intern.h"

/*---------------------------------------------------------------------------*/

#define G3D_NO_DEFAULT -10

#define G3D_COMPRESSION_DEFAULT G3D_COMPRESSION
#define G3D_USE_LZW_DEFAULT G3D_NO_LZW
#define G3D_USE_RLE_DEFAULT G3D_USE_RLE
#define G3D_PRECISION_DEFAULT G3D_MAX_PRECISION
#define G3D_CACHE_SIZE_DEFAULT 1000
#define G3D_CACHE_SIZE_MAX_DEFAULT 2000000
#define G3D_FILE_TYPE_DEFAULT DCELL_TYPE
#define G3D_TILE_X_DEFAULT 8
#define G3D_TILE_Y_DEFAULT 8
#define G3D_TILE_Z_DEFAULT 8
#define G3D_ERROR_FUN_DEFAULT G3d_skipError
#define G3D_UNIT_DEFAULT "none"

/*---------------------------------------------------------------------------*/

#define G3D_COMPRESSION_ENV_VAR_YES "G3D_USE_COMPRESSION"
#define G3D_COMPRESSION_ENV_VAR_NO "G3D_NO_COMPRESSION"

#define G3D_LZW_ENV_VAR_YES "G3D_USE_LZW"
#define G3D_LZW_ENV_VAR_NO "G3D_NO_LZW"

#define G3D_RLE_ENV_VAR_YES "G3D_USE_RLE"
#define G3D_RLE_ENV_VAR_NO "G3D_NO_RLE"

#define G3D_PRECISION_ENV_VAR "G3D_PRECISION"
#define G3D_PRECISION_ENV_VAR_MAX "G3D_MAX_PRECISION"

#define G3D_CACHE_SIZE_ENV_VAR "G3D_DEFAULT_CACHE_SIZE"
#define G3D_CACHE_SIZE_MAX_ENV_VAR "G3D_MAX_CACHE_SIZE"

#define G3D_FILE_FLOAT_ENV_VAR "G3D_WRITE_FLOAT"
#define G3D_FILE_DOUBLE_ENV_VAR "G3D_WRITE_DOUBLE"

#define G3D_TILE_DIM_X_ENV_VAR "G3D_TILE_DIMENSION_X"
#define G3D_TILE_DIM_Y_ENV_VAR "G3D_TILE_DIMENSION_Y"
#define G3D_TILE_DIM_Z_ENV_VAR "G3D_TILE_DIMENSION_Z"

#define G3D_FATAL_ERROR_ENV_VAR "G3D_USE_FATAL_ERROR"
#define G3D_PRINT_ERROR_ENV_VAR "G3D_USE_PRINT_ERROR"

#define G3D_DEFAULT_WINDOW3D "G3D_DEFAULT_WINDOW3D"

/*---------------------------------------------------------------------------*/

int g3d_do_compression = G3D_NO_DEFAULT;
int g3d_do_lzw_compression = G3D_NO_DEFAULT;
int g3d_do_rle_compression = G3D_NO_DEFAULT;
int g3d_precision = G3D_NO_DEFAULT;
int g3d_cache_default = G3D_NO_DEFAULT;
int g3d_cache_max = G3D_NO_DEFAULT;
int g3d_file_type = G3D_NO_DEFAULT;
int g3d_tile_dimension[3] =
    { G3D_NO_DEFAULT, G3D_NO_DEFAULT, G3D_NO_DEFAULT };
void (*g3d_error_fun) (const char *) = NULL;
char *g3d_unit_default = NULL;

/*---------------------------------------------------------------------------*/


/*!
 * \brief 
 *
 * <em>doCompress</em> should be one of G3D_NO_COMPRESSION and
 * G3D_COMPRESSION, <em>doRle</em> should be either G3D_NO_RLE or
 * G3D_USE_RLE, and <em>precision</em> should be either G3D_MAX_PRECISION or
 * a positive integer.
 *
 *  \param doCompress
 *  \param doLzw
 *  \param doRle
 *  \param precision
 *  \return void
 */

void
G3d_setCompressionMode(int doCompress, int doLzw, int doRle, int precision)
{
    if ((doCompress != G3D_NO_COMPRESSION) && (doCompress != G3D_COMPRESSION))
	G3d_fatalError("G3d_setCompressionMode: wrong value for doCompress.");

    g3d_do_compression = doCompress;

    if (doCompress == G3D_NO_COMPRESSION)
	return;

    if ((doLzw != G3D_NO_LZW) && (doLzw != G3D_USE_LZW))
	G3d_fatalError("G3d_setCompressionMode: wrong value for doLzw.");

    if ((doRle != G3D_NO_RLE) && (doRle != G3D_USE_RLE))
	G3d_fatalError("G3d_setCompressionMode: wrong value for doRle.");

    if (precision < -1)
	G3d_fatalError("G3d_setCompressionMode: wrong value for precision.");

    g3d_do_lzw_compression = doLzw;
    g3d_do_rle_compression = doRle;
    g3d_precision = precision;
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief 
 *
 * 
 *
 *  \param doCompress
 *  \param doLzw
 *  \param doRle
 *  \param precision
 *  \return void
 */

void
G3d_getCompressionMode(int *doCompress, int *doLzw, int *doRle,
		       int *precision)
{
    if (doCompress != NULL)
	*doCompress = g3d_do_compression;
    if (doLzw != NULL)
	*doLzw = g3d_do_lzw_compression;
    if (doRle != NULL)
	*doRle = g3d_do_rle_compression;
    if (precision != NULL)
	*precision = g3d_precision;
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief 
 *
 *  set cache size
 *
 *  \param nTiles
 *  \return void
 */

void G3d_setCacheSize(int nTiles)
{
    if (nTiles < 0)
	G3d_fatalError("G3d_setCacheSize: size out of range.");

    g3d_cache_default = nTiles;
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief 
 *
 *  get cache size
 *
 *  \return int
 */

int G3d_getCacheSize()
{
    return g3d_cache_default;
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief Set cache limit
 *
 *  set cache limit
 *
 *  \param nBytes
 *  \return void
 */

void G3d_setCacheLimit(int nBytes)
{
    if (nBytes <= 0)
	G3d_fatalError("G3d_setCacheLimit: size out of range.");

    g3d_cache_max = nBytes;
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief Get cache limit
 *
 *  get cache limit
 *
 *  \param nBytes
 *  \return int
 */

int G3d_getCacheLimit()
{
    return g3d_cache_max;
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief 
 *
 *  set G3d file type
 *
 *  \param type
 *  \return void
 */

void G3d_setFileType(int type)
{
    if ((type != FCELL_TYPE) && (type != DCELL_TYPE))
	G3d_fatalError("G3d_setFileTypeDefault: invalid type");

    g3d_file_type = type;
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief 
 *
 * get G3d file type
 *
 *  \param type
 *  \return int
 */

int G3d_getFileType()
{
    return g3d_file_type;
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief 
 *
 * set Tile Dimension
 *
 *  \param tileX
 *  \param tileY
 *  \param tileZ
 *  \return void
 */

void G3d_setTileDimension(int tileX, int tileY, int tileZ)
{
    if ((g3d_tile_dimension[0] = tileX) <= 0)
	G3d_fatalError
	    ("G3d_setTileDimension: value for tile x environment variable out of range");

    if ((g3d_tile_dimension[1] = tileY) <= 0)
	G3d_fatalError
	    ("G3d_setTileDimension: value for tile y environment variable out of range");

    if ((g3d_tile_dimension[2] = tileZ) <= 0)
	G3d_fatalError
	    ("G3d_setTileDimension: value for tile z environment variable out of range");
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief 
 *
 *  get Tile Dimension
 *
 *  \param tileX
 *  \param tileY
 *  \param tileZ
 *  \return void
 */

void G3d_getTileDimension(int *tileX, int *tileY, int *tileZ)
{
    *tileX = g3d_tile_dimension[0];
    *tileY = g3d_tile_dimension[1];
    *tileZ = g3d_tile_dimension[2];
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief 
 *
 *  set error function
 *
 *  \param 
 *  \return void
 */

void G3d_setErrorFun(void (*fun) (const char *))
{
    g3d_error_fun = fun;
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief 
 *
 *  set G3d unit
 *
 *  \param unit
 *  \return void
 */

void G3d_setUnit(const char *unit)
{
    G3d_free(g3d_unit_default);
    g3d_unit_default = G_store(unit);
}

/*---------------------------------------------------------------------------*/


/*!
 * \brief 
 *
 *  Initializes the default values described
 * in G3D Defaults.  Applications have to use this function only if they need to
 * query the default values before the first file (either old or new) has been
 * opened.
 *
 *  \return void
 */

void G3d_initDefaults(void)
{
    static int firstTime = 1;
    const char *value, *windowName;
    G3D_Region window;

    if (!firstTime)
	return;
    firstTime = 0;

    if (g3d_do_compression == G3D_NO_DEFAULT) {
	if (NULL != getenv(G3D_COMPRESSION_ENV_VAR_YES)) {
	    g3d_do_compression = G3D_COMPRESSION;
	}
	else {
	    if (NULL != getenv(G3D_COMPRESSION_ENV_VAR_NO)) {
		g3d_do_compression = G3D_NO_COMPRESSION;
	    }
	    else {
		g3d_do_compression = G3D_COMPRESSION_DEFAULT;
	    }
	}
    }

    if (g3d_do_lzw_compression == G3D_NO_DEFAULT) {
	if (NULL != getenv(G3D_LZW_ENV_VAR_YES)) {
	    g3d_do_lzw_compression = G3D_USE_LZW;
	}
	else {
	    if (NULL != getenv(G3D_LZW_ENV_VAR_NO)) {
		g3d_do_lzw_compression = G3D_NO_LZW;
	    }
	    else {
		g3d_do_lzw_compression = G3D_USE_LZW_DEFAULT;
	    }
	}
    }

    if (g3d_do_rle_compression == G3D_NO_DEFAULT) {
	if (NULL != getenv(G3D_RLE_ENV_VAR_YES)) {
	    g3d_do_rle_compression = G3D_USE_RLE;
	}
	else {
	    if (NULL != getenv(G3D_RLE_ENV_VAR_NO)) {
		g3d_do_rle_compression = G3D_NO_RLE;
	    }
	    else {
		g3d_do_rle_compression = G3D_USE_RLE_DEFAULT;
	    }
	}
    }

    if (g3d_precision == G3D_NO_DEFAULT) {
	if (NULL != getenv(G3D_PRECISION_ENV_VAR_MAX)) {
	    g3d_precision = G3D_MAX_PRECISION;
	}
	else {
	    value = getenv(G3D_PRECISION_ENV_VAR);
	    if (value == NULL) {
		g3d_precision = G3D_PRECISION_DEFAULT;
	    }
	    else {
		if (sscanf(value, "%d", &g3d_precision) != 1) {
		    G3d_fatalError
			("G3d_initDefaults: precision environment variable has invalid value");
		}
		else {
		    if (g3d_precision < -1) {
			G3d_fatalError
			    ("G3d_initDefaults: value for cache environment variable out of range");
		    }
		}
	    }
	}
    }

    if (g3d_file_type == G3D_NO_DEFAULT) {
	if (NULL != getenv(G3D_FILE_FLOAT_ENV_VAR)) {
	    g3d_file_type = FCELL_TYPE;
	}
	else {
	    if (NULL != getenv(G3D_FILE_DOUBLE_ENV_VAR)) {
		g3d_file_type = DCELL_TYPE;
	    }
	    else {
		g3d_file_type = G3D_FILE_TYPE_DEFAULT;
	    }
	}
    }

    if (g3d_cache_default == G3D_NO_DEFAULT) {
	value = getenv(G3D_CACHE_SIZE_ENV_VAR);

	if (value == NULL) {
	    g3d_cache_default = G3D_CACHE_SIZE_DEFAULT;
	}
	else {
	    if (sscanf(value, "%d", &g3d_cache_default) != 1) {
		G3d_fatalError
		    ("G3d_initDefaults: cache environment variable has invalid value");
	    }
	    if (g3d_cache_default < 0) {
		G3d_fatalError
		    ("G3d_initDefaults: value for cache environment variable out of range");
	    }
	}
    }

    if (g3d_cache_max == G3D_NO_DEFAULT) {
	value = getenv(G3D_CACHE_SIZE_MAX_ENV_VAR);

	if (value == NULL) {
	    g3d_cache_max = G3D_CACHE_SIZE_MAX_DEFAULT;
	}
	else {
	    if (sscanf(value, "%d", &g3d_cache_max) != 1) {
		G3d_fatalError
		    ("G3d_initDefaults: cache environment variable has invalid value");
	    }
	    if (g3d_cache_max < 0) {
		G3d_fatalError
		    ("G3d_initDefaults: value for cache environment variable out of range");
	    }
	}
    }

    if (g3d_tile_dimension[0] == G3D_NO_DEFAULT) {
	value = getenv(G3D_TILE_DIM_X_ENV_VAR);

	if (value == NULL) {
	    g3d_tile_dimension[0] = G3D_TILE_X_DEFAULT;
	}
	else {
	    if (sscanf(value, "%d", g3d_tile_dimension) != 1) {
		G3d_fatalError
		    ("G3d_initDefaults: tile dimension x environment variable has invalid value");
	    }
	    if (g3d_tile_dimension[0] <= 0) {
		G3d_fatalError
		    ("G3d_initDefaults: value for tile x environment variable out of range");
	    }
	}

	value = getenv(G3D_TILE_DIM_Y_ENV_VAR);

	if (value == NULL) {
	    g3d_tile_dimension[1] = G3D_TILE_Y_DEFAULT;
	}
	else {
	    if (sscanf(value, "%d", g3d_tile_dimension + 1) != 1) {
		G3d_fatalError
		    ("G3d_initDefaults: tile dimension y environment variable has invalid value");
	    }
	    if (g3d_tile_dimension[1] <= 0) {
		G3d_fatalError
		    ("G3d_initDefaults: value for tile y environment variable out of range");
	    }
	}

	value = getenv(G3D_TILE_DIM_Z_ENV_VAR);

	if (value == NULL) {
	    g3d_tile_dimension[2] = G3D_TILE_Z_DEFAULT;
	}
	else {
	    if (sscanf(value, "%d", g3d_tile_dimension + 2) != 1) {
		G3d_fatalError
		    ("G3d_initDefaults: tile dimension z environment variable has invalid value");
	    }
	    if (g3d_tile_dimension[2] <= 0) {
		G3d_fatalError
		    ("G3d_initDefaults: value for tile z environment variable out of range");
	    }
	}
    }

    if (g3d_error_fun == NULL) {
	value = getenv(G3D_FATAL_ERROR_ENV_VAR);

	if (value != NULL) {
	    g3d_error_fun = G3d_fatalError_noargs;
	}
	else {
	    value = getenv(G3D_PRINT_ERROR_ENV_VAR);

	    if (value != NULL) {
		g3d_error_fun = G3d_printError;
	    }
	    else {
		g3d_error_fun = G3D_ERROR_FUN_DEFAULT;
	    }
	}
    }

    if (g3d_unit_default == NULL)
	g3d_unit_default = G_store(G3D_UNIT_DEFAULT);

    windowName = G3d_getWindowParams();
    if (windowName == NULL) {
	value = getenv(G3D_DEFAULT_WINDOW3D);
	if (value != NULL)
	    if (*value != 0)
		windowName = value;
    }

    if (!G3d_readWindow(&window, windowName))
	G3d_fatalError("G3d_initDefaults: Error reading window");
    G3d_setWindow(&window);

}
