/******************************************************************************
 * Copyright (c) 2008, Frank Warmerdam
 *
 * 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 above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * 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.
 ******************************************************************************
 *
 *  geo_simpletags.c  TIFF Interface module that just keeps track of the
 *                    tags in memory, without depending on libtiff.
 *
 *****************************************************************************/

#include "geotiff.h"    /* public GTIFF interface */
#include "geo_simpletags.h"

#include "geo_tiffp.h"  /* Private TIFF interface */
#include "geo_keyp.h"   /* Private GTIFF interface */

static int ST_TypeSize( int st_type );

static int        _GTIFGetField (tiff_t *tif, pinfo_t tag, int *count, void *value );
static int        _GTIFSetField (tiff_t *tif, pinfo_t tag, int  count, void *value );
static tagtype_t  _GTIFTagType  (tiff_t *tif, pinfo_t tag);

/*
 * Set up default TIFF handlers.
 */
void GTIFSetSimpleTagsMethods(TIFFMethod *method)
{
	if (!method) return;

	method->get = _GTIFGetField;
	method->set = _GTIFSetField;
	method->type = _GTIFTagType;
}

/* returns the value of TIFF tag <tag>, or if
 * the value is an array, returns an allocated buffer
 * containing the values. Allocate a copy of the actual
 * buffer, sized up for updating.
 */
static int _GTIFGetField (tiff_t *tif, pinfo_t tag, int *count, void *val )
{
    int data_type;
    void *internal_value;
    if( !ST_GetKey( (ST_TIFF*) tif, (int) tag, count, &data_type,
                    &internal_value ) )
        return 0;

    if( data_type != ST_TagType( tag ) )
        return 0;

    const int item_size = ST_TypeSize( data_type );

    void *ret_value = (char *)_GTIFcalloc( *count * item_size );
    if (!ret_value) return 0;

    _TIFFmemcpy( ret_value, internal_value,  item_size * *count );

    *(void **)val = ret_value;
    return 1;
}

/*
 * Set a GeoTIFF TIFF field.
 */
static int _GTIFSetField (tiff_t *tif, pinfo_t tag, int count, void *value )
{
    const int st_type = ST_TagType( tag );

    return ST_SetKey( (ST_TIFF *) tif, (int) tag, count, st_type, value );
}

/*
 *  This routine is supposed to return the TagType of the <tag>
 *  TIFF tag. Unfortunately, "libtiff" does not provide this
 *  service by default, so we just have to "know" what type of tags
 *  we've got, and how many. We only define the ones Geotiff
 *  uses here, and others return UNKNOWN. The "tif" parameter
 *  is provided for those TIFF implementations that provide
 *  for tag-type queries.
 */
static tagtype_t  _GTIFTagType  (tiff_t *tif, pinfo_t tag)
{
	(void) tif; /* dummy reference */

	tagtype_t ttype;
	switch (tag)
	{
		case GTIFF_ASCIIPARAMS:    ttype=TYPE_ASCII; break;
		case GTIFF_PIXELSCALE:
		case GTIFF_TRANSMATRIX:
		case GTIFF_TIEPOINTS:
		case GTIFF_DOUBLEPARAMS:   ttype=TYPE_DOUBLE; break;
		case GTIFF_GEOKEYDIRECTORY: ttype=TYPE_SHORT; break;
		default: ttype = TYPE_UNKNOWN;
	}

	return ttype;
}

/************************************************************************/
/*                             ST_TagType()                             */
/************************************************************************/

int ST_TagType( int tag )
{
    switch (tag)
    {
      case GTIFF_ASCIIPARAMS:
        return STT_ASCII;

      case GTIFF_PIXELSCALE:
      case GTIFF_TRANSMATRIX:
      case GTIFF_TIEPOINTS:
      case GTIFF_DOUBLEPARAMS:
        return STT_DOUBLE;

      case GTIFF_GEOKEYDIRECTORY:
        return STT_SHORT;
    }

    return -1;
}


/************************************************************************/
/*                            ST_TypeSize()                             */
/************************************************************************/

static int ST_TypeSize( int st_type )

{
    if( st_type == STT_ASCII )
        return 1;
    else if( st_type == STT_SHORT )
        return 2;
    else /* if( st_type == STT_DOUBLE ) */
        return 8;
}

/************************************************************************/
/*                             ST_Create()                              */
/************************************************************************/

ST_TIFF *ST_Create()

{
    return (ST_TIFF *) calloc(1,sizeof(ST_TIFF));
}

/************************************************************************/
/*                             ST_Destroy()                             */
/************************************************************************/

void ST_Destroy( ST_TIFF *st )

{
    for( int i = 0; i < st->key_count; i++ )
        free( st->key_list[i].data );

    if( st->key_list )
        free( st->key_list );
    free( st );
}

/************************************************************************/
/*                             ST_SetKey()                              */
/************************************************************************/

int ST_SetKey( ST_TIFF *st, int tag, int count, int st_type, void *data )

{
/* -------------------------------------------------------------------- */
/*      We should compute the length if we were not given a count       */
/* -------------------------------------------------------------------- */
    if (count == 0 && st_type == STT_ASCII )
    {
        count = (int)strlen((char*)data)+1;
    }

/* -------------------------------------------------------------------- */
/*      If we already have a value for this tag, replace it.            */
/* -------------------------------------------------------------------- */
    const int item_size = ST_TypeSize( st_type );
    for( int i = 0; i < st->key_count; i++ )
    {
        if( st->key_list[i].tag == tag )
        {
            free( st->key_list[i].data );
            st->key_list[i].count = count;
            st->key_list[i].type = st_type;
            /* +1 to make clang static analyzer not warn about potential malloc(0) */
            st->key_list[i].data = malloc(item_size*count+1);
            memcpy( st->key_list[i].data, data, count * item_size );
            return 1;
        }
    }

/* -------------------------------------------------------------------- */
/*      Otherwise, add a new entry.                                     */
/* -------------------------------------------------------------------- */
    st->key_count++;
    st->key_list = (ST_KEY *) realloc(st->key_list,
                                      sizeof(ST_KEY) * st->key_count);
    st->key_list[st->key_count-1].tag = tag;
    st->key_list[st->key_count-1].count = count;
    st->key_list[st->key_count-1].type = st_type;
    /* +1 to make clang static analyzer not warn about potential malloc(0) */
    st->key_list[st->key_count-1].data = malloc(item_size * count+1);
    memcpy( st->key_list[st->key_count-1].data, data, item_size * count );

    return 1;
}

/************************************************************************/
/*                             ST_GetKey()                              */
/************************************************************************/

int ST_GetKey( ST_TIFF *st, int tag, int *count,
               int *st_type, void **data_ptr )

{
    for( int i = 0; i < st->key_count; i++ )
    {
        if( st->key_list[i].tag == tag )
        {
            if( count )
                *count = st->key_list[i].count;
            if( st_type )
                *st_type = st->key_list[i].type;
            if( data_ptr )
                *data_ptr = st->key_list[i].data;
            return 1;
        }
    }

    return 0;
}
