#include "test.h"
#include <catch2/reporters/catch_reporter_registrars.hpp>
#include <catch2/reporters/catch_reporter_event_listener.hpp>

static unordered_string_map<string, unordered_string_map<string, string>> test_dates =
    #include "time/gendata.icc"
;

struct MyListener : Catch::EventListenerBase {
    using EventListenerBase::EventListenerBase;

    void testRunStarting (Catch::TestRunInfo const&) override {
        tzembededdir("zoneinfo");
        use_embed_timezones();
        if (tzget("Europe/Moscow")->name != "Europe/Moscow") {
            throw std::runtime_error("no embeded zones");
        }
    }

    void testCaseStarting (Catch::TestCaseInfo const&) override {
        tzset("Europe/Moscow");
    }

    void testCaseEnded (Catch::TestCaseStats const&) override {
        tzset("Europe/Moscow");
    }
};
CATCH_REGISTER_LISTENER(MyListener);


static void _get_dates (std::vector<TestMoment>& ret, const string& data) {
    size_t pos = 0, cur;
    std::vector<int64_t> tmp;
    auto ptr = data.data();
    while (1) {
        cur = data.find(',', pos);
        int64_t val;
        auto res = from_chars(ptr + pos, (cur == string::npos ? (ptr + data.length()) : (ptr + cur)), val);
        if (res.ec) throw std::invalid_argument("bad dataset content");
        tmp.push_back(val);
        if (cur == string::npos) break;
        pos = cur + 1;
    }

    if (tmp.size() % 10) throw std::invalid_argument("bad dataset content");

    for (size_t i = 0; i < tmp.size(); i += 10) {
        TestMoment e;
        int64_t epoch = tmp[i];
        if (sizeof(ptime_t) < 8 && (epoch >= 2147483648 || epoch < -2147483648)) continue; // skip large numbers for 32bit systems

        e.epoch    = tmp[i];
        e.dt.sec   = tmp[i+1];
        e.dt.min   = tmp[i+2];
        e.dt.hour  = tmp[i+3];
        e.dt.mday  = tmp[i+4];
        e.dt.mon   = tmp[i+5];
        e.dt.year  = tmp[i+6];
        e.dt.wday  = tmp[i+7];
        e.dt.yday  = tmp[i+8];
        e.dt.isdst = tmp[i+9];

        ret.push_back(e);
    }
}

unordered_string_map<string, std::vector<TestMoment>> get_dates (string_view dataset) {
    unordered_string_map<string, std::vector<TestMoment>> ret;
    auto& data = test_dates.at(dataset);
    for (auto& row : data) _get_dates(ret[row.first], row.second);
    return ret;
}

void CHECK_DATETIME (const datetime& d1, const datetime& d2) {
    CHECK_DATETIME(d1, d2.year, d2.mon, d2.mday, d2.hour, d2.min, d2.sec, d2.isdst, d2.wday, d2.yday);
}

void CHECK_DATETIME (const datetime& d1, int32_t year, ptime_t mon, ptime_t mday, ptime_t hour, ptime_t min, ptime_t sec, int32_t isdst, int32_t wday, int32_t yday) {
    std::vector<ptime_t> dt1{d1.year, d1.mon, d1.mday, d1.hour, d1.min, d1.sec, isdst == -1 ? isdst : d1.isdst, wday == -1 ? wday : d1.wday, yday == -1 ? yday : d1.yday};
    std::vector<ptime_t> dt2{year, mon, mday, hour, min, sec, isdst, wday, yday};
    CHECK(dt1 == dt2);
}

void CHECK_LOCALTIME (ptime_t epoch, const datetime& d1) {
    CHECK_LOCALTIME(epoch, d1.year, d1.mon, d1.mday, d1.hour, d1.min, d1.sec, d1.isdst, d1.wday, d1.yday);
}

void CHECK_LOCALTIME (ptime_t epoch, int32_t year, ptime_t mon, ptime_t mday, ptime_t hour, ptime_t min, ptime_t sec, int32_t isdst, int32_t wday, int32_t yday) {
    datetime res;
    CHECK(localtime(epoch, &res));
    CHECK_DATETIME(res, year, mon, mday, hour, min, sec, isdst, wday, yday);
}