// 2021 JHG
// BSD 2 clause. Go nuts.

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <assert.h>

#if  defined(_WIN32) || defined(WIN32)
#include <windows.h>
#include <winsock.h>
#elif defined(__unix__)
#include <sys/time.h>
#endif

#if _WIN32
#    define timegm _mkgmtime
#endif

time_t local_utc_offset_from_seconds(time_t t) {
    if (t < 0)
        t = 0;
    struct tm* locg = localtime(&t);
    struct tm locl;
    memcpy(&locl, locg, sizeof(struct tm));
    return (timegm(locg) - mktime(&locl));
}

int local_utc_offset_seconds() {
    return (int)(local_utc_offset_from_seconds(0));
}

int local_utc_offset_minutes() {
    return (int)(local_utc_offset_seconds()) / 60;
}

time_t read_ts(char buf[]) {
    //const time_t offset = 18000;
    struct tm epoch = {};
    epoch.tm_mday = 2; // to workaround new handling in VC, add a day
    epoch.tm_year = 70;
    time_t offset = mktime(&epoch) - 60 * 60 * 24; // and subtract it again
    //printf("%10ld offset rts\n", offset);  // 18000 -> 5 hours

    struct tm tm, *ptm;
    time_t ts;
    timeval tval;

    if (sscanf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%03ld",
        &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tval.tv_usec) != EOF) {
        //tm.tm_isdst = -1;  // doesn't work
        tm.tm_isdst = 0;  // does work

        printf("%04d-%02d-%02d %02d:%02d:%02d.%03ld read back\n", tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tval.tv_usec);

        tm.tm_year -= 1900;
        tm.tm_mon -= 1;


        tval.tv_sec = mktime(&tm);  // the issues start here
        printf("%ld local_utc_offset_from_seconds(tval.tv_sec)\n", local_utc_offset_from_seconds(tval.tv_sec));

        ptm = localtime(&tval.tv_sec);
        printf("%d ptm->tm_isdst\n ", ptm->tm_isdst);

        printf("%ld tval.tv_sec = mktime(&tm) rts\n", tval.tv_sec);
        //
        ///////////////////////////// JHG TODO: here make it go
        assert(tval.tv_sec == 1630607588);  // Thu Sep 02 2021 18:33:08 GMT+0000 tm.tm_isdst = 0
        //assert(tval.tv_sec == 1630603988);  // Thu Sep 02 2021 17:33:08 GMT+0000 tm.tm_isdst = 1
        // Thu Sep 02 2021 17:33:08 GMT+0000
        // Thu Sep 02 2021 13:33:08 GMT-0400

        //printf("%ld (tval.tv_sec - offset) rts\n", (tval.tv_sec - offset));

        //ts = (tval.tv_sec) * 1000 + tval.tv_usec;  // tval.tv_usec is actually millis at the moment
        ts = (tval.tv_sec - offset) * 1000 + tval.tv_usec;
        // 1,630,585,988 * 1000
        // Thu Sep 02 2021 12:33:08 GMT+0000

        //ts = (mktime(&tm))*1000 + tval.tv_usec;
        printf("%ld ts rts\n", ts);

        return ts;
    }
    return -1;
}

int main() {
    char buf[] = "2021-09-02 13:33:08.994";
    // verify here: https://www.unixtimestamp.com/index.php https://www.epochconverter.com/
    const time_t tsm0 = 1630589588994;  // Thu Sep 02 2021 13:33:08 GMT+0000
    const time_t tsm1 = 1630585988994;  // Thu Sep 02 2021 12:33:08 GMT+0000
    const time_t ts0  = 1630589588;     // Thu Sep 02 2021 13:33:08 GMT+0000

    assert(read_ts(buf) == tsm0);
    //assert(read_ts(buf) == tsm1);

    const time_t epoch_plus_11h = 60 * 60 * 11;
    const int local_time = localtime(&epoch_plus_11h)->tm_hour;
    const int gm_time = gmtime(&epoch_plus_11h)->tm_hour;
    const int tz_diff = local_time - gm_time;
    printf("%d tz_diff\n", tz_diff);

    const time_t local_time0 = mktime(localtime(&epoch_plus_11h));
    const time_t gm_time0 = mktime(gmtime(&epoch_plus_11h));
    printf("local_time0 %ld mktime gmt %ld, local - gmt = %ld\n", local_time0, gm_time0, local_time0 - gm_time0);


    // !!! THIS CORRECTS FOR DST !!!
    //time_t rawtime = time(NULL); 1630603988
    time_t rawtime = 1630603988;
    struct tm* ptm = gmtime(&rawtime);
    time_t gmt = mktime(ptm);
    ptm = localtime(&rawtime);
    time_t offset = rawtime - gmt + (ptm->tm_isdst ? 3600 : 0);

    printf("%i sec => %i hr %d isdst\n", (int)offset, (int)offset / 60 / 60, ptm->tm_isdst);

    printf("local_utc_offset_minutes() %d\n", local_utc_offset_minutes());

    return 0;
}