#include "crs_internal.h"

/******************************************************************************
 *  Functions for building up a CRS object. These allocate, so they must be 
 *  eventually freed by the caller.
 *****************************************************************************/

char *crs_string_make(char *str)
{
    CRSDEBUG(5,"Entered function.");
    return strdup(str);
}

PARTLIST *crs_partlist_make(void)
{
    PARTLIST *partlist = malloc(sizeof(PARTLIST));
    CRSDEBUG(5,"Entered function.");
    partlist->type = CRS_PARTLIST;
}

void crs_partlist_add_part(PARTLIST *partlist, PART *part)
{
    PARTLIST *pl = NULL;
    CRSDEBUG(5,"Entered function.");
    if( partlist->type != CRS_PARTLIST ) 
        crserror("Argument one not a partlist.");
    pl = (PARTLIST*)partlist;
    pl->parts[pl->num_parts++] = part;
    return;
}

AUTHORITY *crs_authority_make(char *auth_name, char *auth_id, char *auth_description)
{
    AUTHORITY *auth = malloc(sizeof(AUTHORITY));
    CRSDEBUG(5,"Entered function.");
    auth->type = CRS_AUTHORITY;
    auth->name = auth_name;
    auth->id = auth_id;
    auth->description = auth_description;
    return auth;
}

UNIT *crs_unit_make(char *unit_name, double unit_conversion, AUTHORITY *unit_auth)
{
    UNIT *unit = malloc(sizeof(UNIT));
    CRSDEBUG(5,"Entered function.");
    unit->type = CRS_UNIT;
    unit->name = unit_name;
    unit->conversion = unit_conversion;
    unit->auth = unit_auth;
    return unit;
}

PRIMEM *crs_primem_make(char *primem_name, double primem_origin, AUTHORITY *primem_auth)
{
    PRIMEM *primem = malloc(sizeof(PRIMEM));
    CRSDEBUG(5,"Entered function.");
    primem->type = CRS_PRIMEM;
    primem->name = primem_name;
    primem->origin = primem_origin;
    primem->auth = primem_auth;
    return primem;
}

SPHEROID *crs_spheroid_make(char *spheroid_name, double spheroid_semimajor, double spheroid_invflattening, AUTHORITY *spheroid_auth)
{
    SPHEROID *spheroid = malloc(sizeof(SPHEROID));
    CRSDEBUG(5,"Entered function.");
    spheroid->type = CRS_SPHEROID;
    spheroid->name = spheroid_name;
    spheroid->semimajor = spheroid_semimajor;
    spheroid->invflattening = spheroid_invflattening;
    spheroid->auth = spheroid_auth;
    return spheroid;
}

TOWGS84 *crs_towgs84_make(int wgs84_num_params, double *wgs84_params)
{
    TOWGS84 *towgs84 = malloc(sizeof(TOWGS84));
    CRSDEBUG(5,"Entered function.");
    towgs84->type = CRS_TOWGS84;
    towgs84->num_params = wgs84_num_params;
    memcpy(towgs84->params, wgs84_params, sizeof(double) * wgs84_num_params);
    return towgs84;
}

DATUM *crs_datum_make(char *datum_name, SPHEROID *datum_spheroid, TOWGS84 *datum_towgs84, AUTHORITY *datum_auth)
{
    DATUM *datum = malloc(sizeof(DATUM));
    CRSDEBUG(5,"Entered function.");
    datum->type = CRS_DATUM;
    datum->name = datum_name;
    datum->spheroid = datum_spheroid;
    datum->towgs84 = datum_towgs84;
    datum->auth = datum_auth;
    return datum;
}

DATUM *crs_datum_make_from_partlist(char *datum_name, PARTLIST *datum_partlist)
{
    DATUM *datum = malloc(sizeof(DATUM));
    int i;
    CRSDEBUG(5,"Entered function.");
    datum->type = CRS_DATUM;
    datum->name = datum_name;
    datum->spheroid = NULL;
    datum->towgs84 = NULL;
    datum->auth = NULL;
    for( i = 0; i < datum_partlist->num_parts; i++ )
    {
        switch ( datum_partlist->parts[i]->type )
        {
            case CRS_SPHEROID:
                datum->spheroid = (SPHEROID*)datum_partlist->parts[i];
                break;
            case CRS_TOWGS84:
                datum->towgs84 = (TOWGS84*)datum_partlist->parts[i];
                break;
            case CRS_AUTHORITY:
                datum->auth = (AUTHORITY*)datum_partlist->parts[i];
                break;
        }
    }
    return datum;
}

AXIS *crs_axis_make(char *axis_name, char *axis_orientation)
{
    AXIS *axis = malloc(sizeof(AXIS));
    CRSDEBUG(5,"Entered function.");
    axis->type = CRS_AXIS;
    axis->name = axis_name;
    axis->orientation = axis_orientation;
    return axis;
}

GEOGCS *crs_geogcs_make(char *geogcs_name, DATUM *geogcs_datum, PRIMEM *geogcs_primem, AXIS *geogcs_axis0, AXIS *geogcs_axis1, UNIT *geogcs_unit, AUTHORITY *geogcs_auth)
{
    GEOGCS *geogcs = malloc(sizeof(GEOGCS));
    CRSDEBUG(5,"Entered function.");
    geogcs->type = CRS_GEOGCS;
    geogcs->name = geogcs_name;
    geogcs->datum = geogcs_datum;
    geogcs->primem = geogcs_primem;
    geogcs->unit = geogcs_unit;
    geogcs->auth = geogcs_auth;
    geogcs->axis[0] = geogcs_axis0;
    geogcs->axis[1] = geogcs_axis1;
    return geogcs;
}

GEOGCS *crs_geogcs_make_from_partlist(char *geogcs_name, PARTLIST *geogcs_partlist)
{
    GEOGCS *geogcs = malloc(sizeof(GEOGCS));
    int i;
    int axisnum = 0;
    CRSDEBUG(5,"Entered function.");
    geogcs->type = CRS_GEOGCS;
    geogcs->name = geogcs_name;
    geogcs->primem = NULL;
    geogcs->datum = NULL;
    geogcs->unit = NULL;
    geogcs->axis[0]  = NULL;
    geogcs->axis[1]  = NULL;
    geogcs->auth = NULL;
    for( i = 0; i < geogcs_partlist->num_parts; i++ )
    {
        switch ( geogcs_partlist->parts[i]->type )
        {
            case CRS_PRIMEM:
                geogcs->primem = (PRIMEM*)geogcs_partlist->parts[i];
                break;
            case CRS_DATUM:
                geogcs->datum = (DATUM*)geogcs_partlist->parts[i];
                break;
            case CRS_UNIT:
                geogcs->unit = (UNIT*)geogcs_partlist->parts[i];
                break;
            case CRS_AXIS:
                geogcs->axis[axisnum++] = (AXIS*)geogcs_partlist->parts[i];
                break;
            case CRS_AUTHORITY:
                geogcs->auth = (AUTHORITY*)geogcs_partlist->parts[i];
                break;
        }
    }
    return geogcs;
}

PROJECTION *crs_projection_make(char *projection_name, AUTHORITY *projection_auth)
{	
    PROJECTION *projection = malloc(sizeof(PROJECTION));
    CRSDEBUG(5,"Entered function.");
    projection->type = CRS_PROJECTION;
    projection->name = projection_name;
	projection->auth = projection_auth;
    return projection;
}

PARAMETER *crs_parameter_make(char *parameter_name, double parameter_value)
{
    PARAMETER *parameter = malloc(sizeof(PARAMETER));
    CRSDEBUG(5,"Entered function.");
    parameter->type = CRS_PARAMETER;
    parameter->name = parameter_name;
    parameter->value = parameter_value;
    return parameter;
}

PROJCS *crs_projcs_make(PROJECTION *projcs_projection, int projcs_num_parameters, PARAMETER *projcs_parameters, AXIS *projcs_axis0, AXIS *projcs_axis1, UNIT *projcs_unit, AUTHORITY *projcs_auth)
{
    PROJCS *projcs = malloc(sizeof(PROJCS));
    CRSDEBUG(5,"Entered function.");
    projcs->type = CRS_PROJCS;
    projcs->projection = projcs_projection;
    projcs->num_parameters = projcs_num_parameters;
    memcpy(projcs->parameters, projcs_parameters, sizeof(PARAMETER) * projcs_num_parameters);
    projcs->unit = projcs_unit;
    projcs->auth = projcs_auth;
    projcs->axis[0] = projcs_axis0;
    projcs->axis[1] = projcs_axis1;
    return projcs;
}

PROJCS *crs_projcs_make_from_partlist(char *projcs_name, PARTLIST *projcs_partlist)
{
    PROJCS *projcs = malloc(sizeof(PROJCS));
    int i;
    int axis = 0;
    CRSDEBUG(5,"Entered function.");
    projcs->type = CRS_PROJCS;
    projcs->name = projcs_name;
    projcs->num_parameters = 0;
    projcs->projection = NULL;
    projcs->geogcs = NULL;
    projcs->unit = NULL;
    projcs->auth = NULL;
    projcs->axis[0] = NULL;
    projcs->axis[1] = NULL;
    for( i = 0; i < projcs_partlist->num_parts; i++ )
    {
        switch ( projcs_partlist->parts[i]->type )
        {
            case CRS_PROJECTION:
                projcs->projection = (PROJECTION*)projcs_partlist->parts[i];
                break;
            case CRS_GEOGCS:
                projcs->geogcs = (GEOGCS*)projcs_partlist->parts[i];
                break;
            case CRS_PARAMETER:
                projcs->parameters[projcs->num_parameters++] = (PARAMETER*)projcs_partlist->parts[i];
                break;
            case CRS_AXIS:
                projcs->axis[axis++] = (AXIS*)projcs_partlist->parts[i];
                break;
            case CRS_UNIT:
                projcs->unit = (UNIT*)projcs_partlist->parts[i];
                break;
            case CRS_AUTHORITY:
                projcs->auth = (AUTHORITY*)projcs_partlist->parts[i];
                break;
        }
    }
    return projcs;
}


/******************************************************************************
 *  Functions for deep-freeing of CRS objects. Call free at the top of the
 *  object heirarchy (PROJCS* or GEOGCS*) and everything below will be freed
 *  automatically.
 *****************************************************************************/

void crs_string_free(char *str)
{
    CRSDEBUG(5,"Entered function.");
    free(str);
}

void crs_authority_free(AUTHORITY *auth)
{
    CRSDEBUG(5,"Entered function.");
    if( auth->name ) 
        crs_string_free(auth->name);
    if( auth->id )
        crs_string_free(auth->id);
    if( auth->description )
        crs_string_free(auth->description);
    free(auth);
}

void crs_unit_free(UNIT *unit)
{
    CRSDEBUG(5,"Entered function.");
    if( unit->name )
        crs_string_free(unit->name);
    if( unit->auth )
        crs_authority_free(unit->auth);
    free(unit);
}

void crs_axis_free(AXIS *axis)
{
    CRSDEBUG(5,"Entered function.");
    if( axis->name )
        crs_string_free(axis->name);
    if( axis->orientation )
        crs_string_free(axis->orientation);
    free(axis);
}

void crs_projection_free(PROJECTION *projection)
{
    CRSDEBUG(5,"Entered function.");
    if( projection->name )
        crs_string_free(projection->name);
    if( projection->auth )
        crs_authority_free(projection->auth);
    free(projection);
}

void crs_parameter_free(PARAMETER *parameter)
{
    CRSDEBUG(5,"Entered function.");
    if( parameter->name )
        crs_string_free(parameter->name);
    free(parameter);
}

void crs_primem_free(PRIMEM *primem)
{
    CRSDEBUG(5,"Entered function.");
    if( primem->name )
        crs_string_free(primem->name);
    if( primem->auth )
        crs_authority_free(primem->auth);
    free(primem);
}

void crs_towgs84_free(TOWGS84 *towgs84)
{
    CRSDEBUG(5,"Entered function.");
    free(towgs84);
}

void crs_spheroid_free(SPHEROID *spheroid)
{
    CRSDEBUG(5,"Entered function.");
    if( spheroid->name )
        crs_string_free(spheroid->name);
    if( spheroid->auth )
        crs_authority_free(spheroid->auth);
    free(spheroid);
}

void crs_datum_free(DATUM *datum)
{
    CRSDEBUG(5,"Entered function.");
    if( datum->name )
        crs_string_free(datum->name);
    if( datum->spheroid )
        crs_spheroid_free(datum->spheroid);
    if( datum->towgs84 )
        crs_towgs84_free(datum->towgs84);
    if( datum->auth )
        crs_authority_free(datum->auth);
    free(datum);
}

void crs_geogcs_free(GEOGCS *geogcs)
{
    CRSDEBUG(5,"Entered function.");
    if( geogcs->name ) 
        crs_string_free(geogcs->name);
    if( geogcs->datum )
        crs_datum_free(geogcs->datum);
    if( geogcs->primem )
        crs_primem_free(geogcs->primem);
    if( geogcs->unit )
        crs_unit_free(geogcs->unit);
    if( geogcs->axis[0] )
        crs_axis_free(geogcs->axis[0]);
    if( geogcs->axis[1] )
        crs_axis_free(geogcs->axis[1]);
    if( geogcs->auth )
        crs_authority_free(geogcs->auth);
    free(geogcs);
}

void crs_projcs_free(PROJCS *projcs)
{
    int i = 0;
    CRSDEBUG(5,"Entered function.");
    
    if( projcs->name )
        crs_string_free(projcs->name);
    if( projcs->geogcs )
        crs_geogcs_free(projcs->geogcs);
    if( projcs->projection )
        crs_projection_free(projcs->projection);
    for( i = 0; i < projcs->num_parameters; i++ )
    {
        if( projcs->parameters[i] )
            crs_parameter_free(projcs->parameters[i]);
    }
    if( projcs->unit )
        crs_unit_free(projcs->unit);
    if( projcs->axis[0] ) 
        crs_axis_free(projcs->axis[0]);
    if( projcs->axis[1] ) 
        crs_axis_free(projcs->axis[1]);
    if( projcs->auth )
        crs_authority_free(projcs->auth);
    free(projcs);
    return;
}

void crs_free(CRS* crs)
{
    CRSDEBUG(5,"Entered function.");
    if( crs->type == CRS_GEOGCS )
        crs_geogcs_free((GEOGCS*)crs);
    if( crs->type == CRS_PROJCS )
        crs_projcs_free((PROJCS*)crs);
    return;
}
        
