/*
 * COPYRIGHT:   LGPL, See LGPL.txt in the top level directory
 * PROJECT:     ReactOS CRT library
 * FILE:        lib/sdk/crt/time/mktime.c
 * PURPOSE:     Implementation of mktime, _mkgmtime
 * PROGRAMERS:  Timo Kreuzer
 */
#include <precomp.h>
#include "bitsfixup.h"

#define MAX_32BIT_TIME 0xFFFFFFFFULL

static int g_monthdays[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};

__time64_t
mktime_worker(struct tm * ptm, int utc)
{
    struct tm *ptm2;
    __time64_t time;
    int mons, years, leapyears;
    TIME_ZONE_INFORMATION tzi;
    DWORD ret;

    /* Normalize year and month */
    if (ptm->tm_mon < 0)
    {
        mons = -ptm->tm_mon - 1;
        ptm->tm_year -= 1 + mons / 12;
        ptm->tm_mon = 11 - (mons % 12);
    }
    else if (ptm->tm_mon > 11)
    {
        mons = ptm->tm_mon;
        ptm->tm_year += (mons / 12);
        ptm->tm_mon = mons % 12;
    }

    /* Is it inside margins */
    if (ptm->tm_year < 70 || ptm->tm_year > 139) // FIXME: max year for 64 bits
    {
        return -1;
    }

    years = ptm->tm_year - 70;

    /* Number of leapyears passed since 1970 */
    leapyears = (years + 1) / 4;

    /* Calculate days up to 1st of Jan */
    time = years * 365 + leapyears;

    /* Calculate days up to 1st of month */
    time += g_monthdays[ptm->tm_mon];

    /* Check if we need to add a leap day */
    if (((years + 2) % 4) == 0)
    {
        if (ptm->tm_mon > 2)
        {
            time++;
        }
    }

    time += ptm->tm_mday - 1;

    time *= 24;
    time += ptm->tm_hour;

    time *= 60;
    time += ptm->tm_min;

    time *= 60;
    time += ptm->tm_sec;

    if (time < 0)
    {
        return -1;
    }

    /* Finally get normalized tm struct */
    ptm2 = _gmtime64(&time);
    if (!ptm2)
    {
        return -1;
    }
    *ptm = *ptm2;

    /* Finally adjust by the difference to GMT in seconds */
    ret = GetTimeZoneInformation(&tzi);
    if (ret != TIME_ZONE_ID_INVALID)
    {
        time += tzi.Bias * 60;
    }

    return time;
}

/*    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
    int tm_wday;
    int tm_yday;
    int tm_isdst;
*/

/**
 * \name _mkgmtime
 *
 */
time_t
_mkgmtime(struct tm *ptm)
{
    __time64_t time = mktime_worker(ptm, 1);
    return (time_t)((time > MAX_32BIT_TIME) ? -1 : time);
}

time_t
mktime(struct tm *ptm)
{
    __time64_t time = mktime_worker(ptm, 0);
    return (time_t)((time > MAX_32BIT_TIME) ? -1 : time);
}

__time32_t
_mkgmtime32(struct tm *ptm)
{
    __time64_t time = mktime_worker(ptm, 1);
    return (__time32_t)((time > MAX_32BIT_TIME) ? -1 : time);
}

__time32_t
_mktime32(struct tm *ptm)
{
    __time64_t time = mktime_worker(ptm, 0);
    return (__time32_t)((time > MAX_32BIT_TIME) ? -1 : time);
}

__time64_t
_mkgmtime64(struct tm *ptm)
{
    return mktime_worker(ptm, 1);
}

__time64_t
_mktime64(struct tm *ptm)
{
    return mktime_worker(ptm, 0);
}