/*!
   \file lib/gis/get_projinfo.c

   \brief GIS Library - Get projection info

   (C) 1999-2014 by the GRASS Development Team

   This program is free software under the GNU General Public License
   (>=v2). Read the file COPYING that comes with GRASS for details.
 */

#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <grass/gis.h>
#include <grass/glocale.h>

#define PERMANENT "PERMANENT"

/*!
   \brief Gets units information for location

   Note: Allocated Key_Value structure should be freed by
   G_free_key_value().

   Prints a warning if no units information available.

   \return pointer to Key_Value structure with key/value pairs
   \return NULL on failure
 */
struct Key_Value *G_get_projunits(void)
{
    struct Key_Value *in_units_keys;
    char path[GPATH_MAX];

    G_file_name(path, "", UNIT_FILE, PERMANENT);
    if (access(path, 0) != 0) {
        if (G_projection() != PROJECTION_XY) {
            G_warning(_("<%s> file not found for location <%s>"), UNIT_FILE,
                      G_location());
        }
        return NULL;
    }
    in_units_keys = G_read_key_value_file(path);

    return in_units_keys;
}

/*!
   \brief Gets projection information for location

   Note: Allocated Key_Value structure should be freed by
   G_free_key_value().

   Prints a warning if no projection information available.

   \return pointer to Key_Value structure with key/value pairs
   \return NULL on failure
 */
struct Key_Value *G_get_projinfo(void)
{
    struct Key_Value *in_proj_keys, *in_epsg_keys;
    char path[GPATH_MAX];

    G_file_name(path, "", PROJECTION_FILE, PERMANENT);
    if (access(path, 0) != 0) {
        if (G_projection() != PROJECTION_XY) {
            G_warning(_("<%s> file not found for location <%s>"),
                      PROJECTION_FILE, G_location());
        }
        return NULL;
    }
    in_proj_keys = G_read_key_value_file(path);

    /* TODO: do not restrict to EPSG as the only authority */
    if ((in_epsg_keys = G_get_projepsg()) != NULL) {
        const char *epsgstr = G_find_key_value("epsg", in_epsg_keys);
        char buf[4096];

        sprintf(buf, "EPSG:%s", epsgstr);
        G_set_key_value("init", buf, in_proj_keys);
        G_free_key_value(in_epsg_keys);
    }

    return in_proj_keys;
}

/*!
   \brief Gets EPSG information for the current location

   DEPRECATED: Use G_get_projsrid() instead.

   Note: Allocated Key_Value structure should be freed by
   G_free_key_value().

   \return pointer to Key_Value structure with key/value pairs
   \return NULL when EPSG code is not defined for location
 */

/* superseded by G_get_projsrid(), keep for backwards compatibility */
struct Key_Value *G_get_projepsg(void)
{
    struct Key_Value *in_epsg_keys;
    char path[GPATH_MAX];

    G_file_name(path, "", EPSG_FILE, PERMANENT);
    if (access(path, 0) != 0) {
        if (G_projection() != PROJECTION_XY) {
            G_debug(1, "<%s> file not found for location <%s>", EPSG_FILE,
                    G_location());
        }
        return NULL;
    }
    in_epsg_keys = G_read_key_value_file(path);

    return in_epsg_keys;
}

/*!
   \brief Get WKT information for the current location

   \return pointer to WKT string
   \return NULL when WKT is not available for the current location
 */

char *G_get_projwkt(void)
{
    char *wktstring = NULL;
    char path[GPATH_MAX];
    FILE *fp;
    int n, nalloc;
    int c;

    G_file_name(path, "", WKT_FILE, "PERMANENT");
    if (access(path, 0) != 0) {
        if (G_projection() != PROJECTION_XY) {
            G_debug(1, "<%s> file not found for location <%s>", WKT_FILE,
                    G_location());
        }
        return NULL;
    }

    fp = fopen(path, "r");
    if (!fp)
        G_fatal_error(_("Unable to open input file <%s>: %s"), path,
                      strerror(errno));

    wktstring = G_malloc(1024 * sizeof(char));
    nalloc = 1024;

    n = 0;
    while (1) {
        c = fgetc(fp);

        if (c == EOF) {
            break;
        }

        if (c == '\r') { /* DOS or MacOS9 */
            c = fgetc(fp);
            if (c != EOF) {
                if (c !=
                    '\n') { /* MacOS9 - we have to return the char to stream */
                    ungetc(c, fp);
                    c = '\n';
                }
            }
            else { /* MacOS9 - we have to return the char to stream */
                ungetc(c, fp);
                c = '\n';
            }
        }

        if (n == nalloc) {
            wktstring = G_realloc(wktstring, nalloc + 1024);
            nalloc += 1024;
        }

        wktstring[n] = c;

        n++;
    }

    if (n > 0) {
        if (n == nalloc) {
            wktstring = G_realloc(wktstring, nalloc + 1);
            nalloc += 1;
        }
        wktstring[n] = '\0';
    }
    else {
        G_free(wktstring);
        wktstring = NULL;
    }

    if (fclose(fp) != 0)
        G_fatal_error(_("Error closing output file <%s>: %s"), path,
                      strerror(errno));

    if (wktstring && *wktstring)
        G_chop(wktstring);
    if (wktstring && *wktstring == '\0') {
        G_free(wktstring);
        wktstring = NULL;
    }

    return wktstring;
}

/*!
   \brief Get srid (spatial reference id) for the current location

   Typically an srid will be of the form authority NAME:CODE,
   e.g. EPSG:4326

   This srid is passed to proj_create() using PROJ or
   OSRSetFromUserInput() using GDAL. Therefore various other forms of
   srid are possible, e.g. in OSRSetFromUserInput():

   1. Well Known Text definition - passed on to importFromWkt().
   2. "EPSG:n" - number passed on to importFromEPSG().
   3. "EPSGA:n" - number passed on to importFromEPSGA().
   4. "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
   5. "urn:ogc:def:crs:EPSG::n" - ogc urns
   6. PROJ.4 definitions - passed on to importFromProj4().
   7. filename - file read for WKT, XML or PROJ.4 definition.
   8. well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
   WGS84 or WGS72.
   9. "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
   10. PROJJSON (PROJ >= 6.2)

   \return pointer to srid string
   \return NULL when srid is not available for the current location
 */

char *G_get_projsrid(void)
{
    char *sridstring = NULL;
    char path[GPATH_MAX];
    FILE *fp;
    int n, nalloc;
    int c;

    G_file_name(path, "", SRID_FILE, "PERMANENT");
    if (access(path, 0) != 0) {
        if (G_projection() != PROJECTION_XY) {
            struct Key_Value *projepsg;
            const char *epsg_num;

            G_debug(1, "<%s> file not found for location <%s>", SRID_FILE,
                    G_location());

            /* for backwards compatibility, check if PROJ_EPSG exists */
            if ((projepsg = G_get_projepsg()) != NULL) {
                epsg_num = G_find_key_value("epsg", projepsg);
                if (*epsg_num) {
                    G_debug(1, "Using <%s> file instead for location <%s>",
                            EPSG_FILE, G_location());
                    G_asprintf(&sridstring, "EPSG:%s", epsg_num);
                    G_free_key_value(projepsg);

                    return sridstring;
                }
            }
        }
        return NULL;
    }

    fp = fopen(path, "r");
    if (!fp)
        G_fatal_error(_("Unable to open input file <%s>: %s"), path,
                      strerror(errno));

    sridstring = G_malloc(1024 * sizeof(char));
    nalloc = 1024;

    n = 0;
    while (1) {
        c = fgetc(fp);

        if (c == EOF) {
            break;
        }

        if (c == '\r') { /* DOS or MacOS9 */
            c = fgetc(fp);
            if (c != EOF) {
                if (c !=
                    '\n') { /* MacOS9 - we have to return the char to stream */
                    ungetc(c, fp);
                    c = '\n';
                }
            }
            else { /* MacOS9 - we have to return the char to stream */
                ungetc(c, fp);
                c = '\n';
            }
        }

        if (n == nalloc) {
            sridstring = G_realloc(sridstring, nalloc + 1024);
            nalloc += 1024;
        }

        sridstring[n] = c;

        n++;
    }

    if (n > 0) {
        if (n == nalloc) {
            sridstring = G_realloc(sridstring, nalloc + 1);
            nalloc += 1;
        }
        sridstring[n] = '\0';
    }
    else {
        G_free(sridstring);
        sridstring = NULL;
    }

    if (fclose(fp) != 0)
        G_fatal_error(_("Error closing output file <%s>: %s"), path,
                      strerror(errno));

    if (sridstring && *sridstring)
        G_chop(sridstring);
    if (sridstring && *sridstring == '\0') {
        G_free(sridstring);
        sridstring = NULL;
    }

    return sridstring;
}
