/******************************************************************************
 G_lat_scan (buf, lat)
     char *buf;
     double *lat;

 G_lon_scan (buf, lon)
     char *buf;
     double *lon;

 G_llres_scan (buf, res)
     char *buf;
     double *res;

 Convert ascii string representations of latitude/longitude to a double.
 The string format is:

       dd:mm:ss.ffh

 where:
       dd is degrees, 0-90 for latitude, 0-180 for longitude
       mm is minutes, 0-59
       ss is seconds, 0-59
       ff is fractions of a second, >= 0
       h  is 'n' or 's' for latitude,
             'e' or 'w' for longitude.
             missing for resolution

 lat (or lon) is set to the double value for the lat/lon represented in buf.

 lat is always in the range  -90 thru  90,
 lon is always in the range -180 thru 180.

 note: southern latitude and western longitude are returned as negative values.

 returns 1 if input format is ok, 0 otherwise.
******************************************************************************/
#include <grass/gis.h>

#define LL_TOLERANCE 10

static int scan_ll(const char *, const char *, double *, int);
static int check_minutes(const char *);
static int check_seconds(const char *);

int G_lat_scan(const char *buf, double *lat)
{
    return scan_ll(buf, "sn", lat, 90 + LL_TOLERANCE);
}

int G_lon_scan(const char *buf, double *lon)
{
    return scan_ll(buf, "we", lon, 360 + LL_TOLERANCE);
}

int G_llres_scan(const char *buf, double *res)
{
    char tbuf[100];

    sprintf(tbuf, "%se", buf);
    return scan_ll(tbuf, "we", res, 0);
}

#define MARKER 1
static int scan_ll(const char *buf, const char *dir, double *result, int max)
{
    char h[100];
    int d, m, s;
    char ps[20], *pps;
    double p, f;
    double pm = 0.0;
    char tbuf[100];

    sprintf(tbuf, "%s%c", buf, MARKER); /* add a marker at end of string */
    buf = tbuf;

    if (sscanf(buf, "%d:%d:%d.%[0123456789]%[^\n]", &d, &m, &s, ps, h) == 5) {
        p = 0.0;
        f = .1;
        for (pps = ps; *pps; pps++) {
            p += (*pps - '0') * f;
            f /= 10.0;
        }
    }
    else if (sscanf(buf, "%d:%d:%d%[^\n]", &d, &m, &s, h) == 4) {
        p = 0.0;
    }
    else if (sscanf(buf, "%d:%d.%[0123456789]%[^\n]", &d, &m, ps, h) == 4) {
        s = 0;
        p = 0.0;
        f = .1;
        for (pps = ps; *pps; pps++) {
            pm += (*pps - '0') * f;
            f /= 10.0;
        }
    }
    else if (sscanf(buf, "%d:%d%[^\n]", &d, &m, h) == 3) {
        p = 0.0;
        s = 0;
    }
    else if (sscanf(buf, "%d%[^\n]", &d, h) == 2) {
        p = 0.0;
        s = m = 0;
    }
    else
        return 0;

    if (d < 0)
        return 0;
    if (m < 0 || m >= 60)
        return 0;
    if (s < 0 || s >= 60)
        return 0;

    if (max) {
        if (d > max)
            return 0;
        if (d == max && (m > 0 || s > 0 || p > 0.0))
            return 0;
    }

    if (m && !check_minutes(buf))
        return 0;
    if (s && !check_seconds(buf))
        return 0;

    *result = d + (m + pm) / 60.0 + (s + p) / 3600.0;

    G_strip(h);

    if (*result == 0.0 && *h == MARKER)
        return (1);

    if (*h >= 'A' && *h <= 'Z')
        *h += 'a' - 'A';

    if (*h != dir[0] && *h != dir[1])
        return 0;

    if (h[1] != MARKER)
        return 0;

    if (*h == dir[0] && *result != 0.0)
        *result = -(*result);

    return 1;
}

static int check_minutes(const char *buf)
{
    /* skip over degrees */
    while (*buf != ':')
        if (*buf++ == 0)
            return 1;
    buf++;

    /* must have 2 digits for minutes */
    if (*buf < '0' || *buf > '9')
        return 0;
    buf++;
    if (*buf < '0' || *buf > '9')
        return 0;
    buf++;
    return (*buf < '0' || *buf > '9');
}

static int check_seconds(const char *buf)
{
    /* skip over degrees */
    while (*buf != ':')
        if (*buf++ == 0)
            return 1;
    buf++;
    /* skip over minutes */
    while (*buf != ':')
        if (*buf++ == 0)
            return 1;
    buf++;

    /* must have 2 digits for seconds */
    if (*buf < '0' || *buf > '9')
        return 0;
    buf++;
    if (*buf < '0' || *buf > '9')
        return 0;
    buf++;
    return (*buf < '0' || *buf > '9');
}
