/*
 * Copyright (C) 1995.  Bill Brown <brown@gis.uiuc.edu> & Michael Shapiro
 *
 * This program is free software under the GPL (>=v2)
 * Read the file GPL.TXT coming with GRASS for details.
 */
#include <grass/datetime.h>

static int have(int x, const DateTime *dt)
{
    return datetime_is_between(x, dt->from, dt->to);
}

/*!
 * \brief
 *
 * Returns:
 * 0 is legal year for dt
 * -1 illegal year for this dt
 * -2 dt has no year component
 *
 *  \param dt
 *  \param year
 *  \return int
 */

int datetime_check_year(const DateTime *dt, int year)
{
    if (!have(DATETIME_YEAR, dt))
        return datetime_error(-2, "datetime has no year");
    if (year < 0)
        return datetime_error(-1, "invalid datetime year");
    if (datetime_is_absolute(dt) && year <= 0)
        return datetime_error(-1, "invalid datetime year");

    return 0;
}

/*!
 * \brief
 *
 * Returns:
 * 0 is legal month for dt
 * -1 illegal month for this dt
 * -2 dt has no month component
 *
 *  \param dt
 *  \param month
 *  \return int
 */

int datetime_check_month(const DateTime *dt, int month)
{
    if (!have(DATETIME_MONTH, dt))
        return datetime_error(-2, "datetime has no month");
    if (month < 0)
        return datetime_error(-1, "invalid datetime month");
    if (datetime_is_absolute(dt) && (month < 1 || month > 12))
        return datetime_error(-1, "invalid datetime month");
    /*
       if (dt->from != DATETIME_MONTH && month > 11)
       return datetime_error(-1,"invalid datetime month");
       BILL CHANGED TO: */

    if (datetime_is_relative(dt) && dt->from != DATETIME_MONTH && month > 11)
        return datetime_error(-1, "invalid datetime month");

    return 0;
}

/*!
 * \brief
 *
 * Returns:
 * 0 is legal day for dt
 * -1 illegal day for this dt
 * -2 dt has no day component<br>
 * Note: if dt.mode is ABSOLUTE, then dt.year and
 * dt.month must also be legal, since the 'day' must be a legal value for the
 * dt.year/dt.month
 *
 *  \param dt
 *  \param day
 *  \return int
 */

int datetime_check_day(const DateTime *dt, int day)
{
    int month, year, ad;
    int stat;

    if (!have(DATETIME_DAY, dt))
        return datetime_error(-2, "datetime has no day");
    if (day < 0)
        return datetime_error(-1, "invalid datetime day");
    if (datetime_is_absolute(dt)) {
        stat = datetime_get_month(dt, &month);
        if (stat != 0)
            return stat;
        stat = datetime_get_year(dt, &year);
        if (stat != 0)
            return stat;
        ad = datetime_is_positive(dt);
        if (day < 1 || day > datetime_days_in_month(year, month, ad))
            return datetime_error(-1, "invalid datetime day");
    }

    return 0;
}

/*!
 * \brief
 *
 * returns:
 * 0 on success
 * -1 if 'dt' has an invalid hour
 * -2 if 'dt' has no hour
 *
 *  \param dt
 *  \param hour
 *  \return int
 */

int datetime_check_hour(const DateTime *dt, int hour)
{
    if (!have(DATETIME_HOUR, dt))
        return datetime_error(-2, "datetime has no hour");
    if (hour < 0)
        return datetime_error(-1, "invalid datetime hour");
    if (dt->from != DATETIME_HOUR && hour > 23)
        return datetime_error(-1, "invalid datetime hour");

    return 0;
}

/*!
 * \brief
 *
 * returns:
 * 0 on success
 * -1 if 'dt' has an invalid minute
 * -2 if 'dt' has no minute
 *
 *  \param dt
 *  \param minute
 *  \return int
 */

int datetime_check_minute(const DateTime *dt, int minute)
{
    if (!have(DATETIME_MINUTE, dt))
        return datetime_error(-2, "datetime has no minute");
    if (minute < 0)
        return datetime_error(-1, "invalid datetime minute");
    if (dt->from != DATETIME_MINUTE && minute > 59)
        return datetime_error(-1, "invalid datetime minute");

    return 0;
}

/*!
 * \brief
 *
 * returns:
 * 0 on success
 * -1 if 'dt' has an invalid second
 * -2 if 'dt' has no second
 *
 *  \param dt
 *  \param second
 *  \return int
 */

int datetime_check_second(const DateTime *dt, double second)
{
    if (!have(DATETIME_SECOND, dt))
        return datetime_error(-2, "datetime has no second");
    if (second < 0)
        return datetime_error(-1, "invalid datetime second");
    if (dt->from != DATETIME_SECOND && second >= 60.0)
        return datetime_error(-1, "invalid datetime second");

    return 0;
}

/*!
 * \brief
 *
 * returns:
 * 0 on success
 * -1 if 'dt' has an invalid fracsec
 * -2 if 'dt' has no fracsec
 *
 *  \param dt
 *  \param fracsec
 *  \return int
 */

int datetime_check_fracsec(const DateTime *dt, int fracsec)
{
    if (!have(DATETIME_SECOND, dt))
        return datetime_error(-2, "datetime has no fracsec");
    if (fracsec < 0)
        return datetime_error(-1, "invalid datetime fracsec");
    return 0;
}

/*!
 * \brief
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param year
 *  \return int
 */

int datetime_get_year(const DateTime *dt, int *year)
{
    int stat;

    stat = datetime_check_year(dt, dt->year);
    if (stat == 0)
        *year = dt->year;

    return stat;
}

/*!
 * \brief
 *
 * if dt.mode = ABSOLUTE, this also sets dt.day = 0
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param year
 *  \return int
 */

int datetime_set_year(DateTime *dt, int year)
{
    int stat;

    stat = datetime_check_year(dt, year);
    if (stat == 0) {
        dt->year = year;
        if (datetime_is_absolute(dt))
            dt->day = 0;
    }

    return stat;
}

/*!
 * \brief
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param month
 *  \return int
 */

int datetime_get_month(const DateTime *dt, int *month)
{
    int stat;

    stat = datetime_check_month(dt, dt->month);
    if (stat == 0)
        *month = dt->month;

    return stat;
}

/*!
 * \brief
 *
 * if dt.mode = ABSOLUTE, this also sets dt.day = 0
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param month
 *  \return int
 */

int datetime_set_month(DateTime *dt, int month)
{
    int stat;

    stat = datetime_check_month(dt, month);
    if (stat == 0) {
        dt->month = month;
        if (datetime_is_absolute(dt))
            dt->day = 0;
    }

    return stat;
}

/*!
 * \brief
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param day
 *  \return int
 */

int datetime_get_day(const DateTime *dt, int *day)
{
    int stat;

    stat = datetime_check_day(dt, dt->day);
    if (stat == 0)
        *day = dt->day;

    return stat;
}

/*!
 * \brief
 *
 * if dt.mode = ABSOLUTE, then  the dt.year, dt.month:
 \code
 if (day >  <b>datetime_days_in_month</b> (dt.year, dt.month))
 {error}
 \endcode
 * This implies that year/month must be set  for ABSOLUTE datetimes.
 *
 * Returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param day
 *  \return int
 */

int datetime_set_day(DateTime *dt, int day)
{
    int stat;

    stat = datetime_check_day(dt, day);
    if (stat == 0)
        dt->day = day;

    return stat;
}

/*!
 * \brief
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param hour
 *  \return int
 */

int datetime_get_hour(const DateTime *dt, int *hour)
{
    int stat;

    stat = datetime_check_hour(dt, dt->hour);
    if (stat == 0)
        *hour = dt->hour;

    return stat;
}

/*!
 * \brief
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param hour
 *  \return int
 */

int datetime_set_hour(DateTime *dt, int hour)
{
    int stat;

    stat = datetime_check_hour(dt, hour);
    if (stat == 0)
        dt->hour = hour;

    return stat;
}

/*!
 * \brief
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param minute
 *  \return int
 */

int datetime_get_minute(const DateTime *dt, int *minute)
{
    int stat;

    stat = datetime_check_minute(dt, dt->minute);
    if (stat == 0)
        *minute = dt->minute;

    return stat;
}

/*!
 * \brief
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param minute
 *  \return int
 */

int datetime_set_minute(DateTime *dt, int minute)
{
    int stat;

    stat = datetime_check_minute(dt, minute);
    if (stat == 0)
        dt->minute = minute;

    return stat;
}

/*!
 * \brief
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param second
 *  \return int
 */

int datetime_get_second(const DateTime *dt, double *second)
{
    int stat;

    stat = datetime_check_second(dt, dt->second);
    if (stat == 0)
        *second = dt->second;

    return stat;
}

/*!
 * \brief
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param second
 *  \return int
 */

int datetime_set_second(DateTime *dt, double second)
{
    int stat;

    stat = datetime_check_second(dt, second);
    if (stat == 0)
        dt->second = second;

    return stat;
}

/*!
 * \brief
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param fracsec
 *  \return int
 */

int datetime_get_fracsec(const DateTime *dt, int *fracsec)
{
    int stat;

    stat = datetime_check_fracsec(dt, dt->fracsec);
    if (stat == 0)
        *fracsec = dt->fracsec;

    return stat;
}

/*!
 * \brief
 *
 * returns 0 on success or negative value on error
 *
 *  \param dt
 *  \param fracsec
 *  \return int
 */

int datetime_set_fracsec(DateTime *dt, int fracsec)
{
    int stat;

    stat = datetime_check_fracsec(dt, fracsec);
    if (stat == 0)
        dt->fracsec = fracsec;

    return stat;
}
