#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "cunit/unit.h"

#include "imap/ical_support.h"

// caldav_db.h defines these

extern time_t caldav_epoch;
extern time_t caldav_eternity;

static void init_caldav()
{
    if (caldav_epoch == -1) caldav_epoch = INT_MIN;
    if (caldav_eternity == -1) caldav_eternity = INT_MAX;
}

static void test_icalrecurrenceset_get_utc_timespan(void)
{
    init_caldav();

    char *eternitystr = icaltime_as_ical_string_r(
        icaltime_from_timet_with_zone(caldav_eternity, 0, NULL));

    struct testcase {
        const char *desc;
        const char *icalstr;
        const char *start;
        const char *end;
        int recurring;
    };

    struct testcase tcs[] = {{
        "not recurring",
        "BEGIN:VCALENDAR\r\n"
        "VERSION:2.0\r\n"
        "PRODID:-//foo//bar\r\n"
        "CALSCALE:GREGORIAN\r\n"
        "BEGIN:VEVENT\r\n"
        "DTSTART:20160928T160000Z\r\n"
        "DURATION:PT1H\r\n"
        "UID:123456789\r\n"
        "DTSTAMP:20150928T132434Z\r\n"
        "SUMMARY:test\r\n"
        "END:VEVENT\r\n"
        "END:VCALENDAR\r\n",
        "20160928T160000Z",
        "20160928T170000Z",
        0
    }, {
        "eternal rrule",
        "BEGIN:VCALENDAR\r\n"
        "VERSION:2.0\r\n"
        "PRODID:-//foo//bar\r\n"
        "CALSCALE:GREGORIAN\r\n"
        "BEGIN:VEVENT\r\n"
        "DTSTART:20160928T160000Z\r\n"
        "DURATION:PT1H\r\n"
        "RRULE:FREQ=WEEKLY\r\n"
        "UID:123456789\r\n"
        "DTSTAMP:20150928T132434Z\r\n"
        "SUMMARY:test\r\n"
        "END:VEVENT\r\n"
        "END:VCALENDAR\r\n",
        "20160928T160000Z",
        eternitystr,
        1
    }, {
        "bounded rrule",
        "BEGIN:VCALENDAR\r\n"
        "VERSION:2.0\r\n"
        "PRODID:-//foo//bar\r\n"
        "CALSCALE:GREGORIAN\r\n"
        "BEGIN:VEVENT\r\n"
        "DTSTART:20160928T160000Z\r\n"
        "DURATION:PT1H\r\n"
        "RRULE:FREQ=WEEKLY;COUNT=3\r\n"
        "UID:123456789\r\n"
        "DTSTAMP:20150928T132434Z\r\n"
        "SUMMARY:test\r\n"
        "END:VEVENT\r\n"
        "END:VCALENDAR\r\n",
        "20160928T160000Z",
        "20161012T170000Z",
        1
    }, {
        "one bounded rrule, one eternal rrule",
        "BEGIN:VCALENDAR\r\n"
        "VERSION:2.0\r\n"
        "PRODID:-//foo//bar\r\n"
        "CALSCALE:GREGORIAN\r\n"
        "BEGIN:VEVENT\r\n"
        "DTSTART:20160928T160000Z\r\n"
        "DURATION:PT1H\r\n"
        "RRULE:FREQ=WEEKLY;COUNT=3\r\n"
        "RRULE:FREQ=MONTHLY\r\n"
        "UID:123456789\r\n"
        "DTSTAMP:20150928T132434Z\r\n"
        "SUMMARY:test\r\n"
        "END:VEVENT\r\n"
        "END:VCALENDAR\r\n",
        "20160928T160000Z",
        eternitystr,
        1
    }, {
        "two bounded rrules",
        "BEGIN:VCALENDAR\r\n"
        "VERSION:2.0\r\n"
        "PRODID:-//foo//bar\r\n"
        "CALSCALE:GREGORIAN\r\n"
        "BEGIN:VEVENT\r\n"
        "DTSTART:20160928T160000Z\r\n"
        "DURATION:PT1H\r\n"
        "RRULE:FREQ=WEEKLY;COUNT=3\r\n"
        "RRULE:FREQ=MONTHLY;UNTIL=20170228T160000Z\r\n"
        "UID:123456789\r\n"
        "DTSTAMP:20150928T132434Z\r\n"
        "SUMMARY:test\r\n"
        "END:VEVENT\r\n"
        "END:VCALENDAR\r\n",
        "20160928T160000Z",
        "20170228T170000Z",
        1
    }};
    const size_t n_tcs = sizeof(tcs) / sizeof(tcs[0]);

    struct buf buf = BUF_INITIALIZER;
    unsigned i;
    for (i = 0; i < n_tcs; i++) {
        const struct testcase *tc = &tcs[i];
        buf_setcstr(&buf, tc->icalstr);
        icalcomponent *ical = ical_string_as_icalcomponent(&buf);
        CU_ASSERT_PTR_NOT_NULL(ical);
        unsigned _recurring = 0;
        struct icalperiodtype span = icalrecurrenceset_get_utc_timespan(ical,
                ICAL_VEVENT_COMPONENT, NULL, &_recurring, NULL, NULL);
        CU_ASSERT_STRING_EQUAL(icaltime_as_ical_string(span.start), tc->start);
        CU_ASSERT_STRING_EQUAL(icaltime_as_ical_string(span.end), tc->end);
        CU_ASSERT_EQUAL(_recurring, tc->recurring);
        icalcomponent_free(ical);
    }
    buf_free(&buf);

    free(eternitystr);
}

static int icalcomponent_myforeach_duplicate_rrule_cb(icalcomponent *comp __attribute__((unused)),
                                                      icaltimetype start __attribute__((unused)),
                                                      icaltimetype end __attribute__((unused)),
                                                      icaltimetype recurid,
                                                      int is_standalone __attribute__((unused)),
                                                      void *data)
{
    strarray_t *recurids = data;
    strarray_append(recurids, icaltime_as_ical_string(recurid));
    return 1;
}

static void test_icalcomponent_myforeach_duplicate_rrule(void)
{
    init_caldav();

    struct buf buf = BUF_INITIALIZER;
    buf_setcstr(&buf,
            "BEGIN:VCALENDAR\r\n"
            "VERSION:2.0\r\n"
            "PRODID:-//Foo//Bar//EN\r\n"
            "CALSCALE:GREGORIAN\r\n"
            "BEGIN:VEVENT\r\n"
            "UID:574E2CD0-2D2A-4554-8B63-C7504481D3A9\r\n"
            "SUMMARY:test\r\n"
            "DTSTART:20240101T010203Z\r\n"
            "SEQUENCE:0\r\n"
            "RRULE:FREQ=DAILY;COUNT=3\r\n"
            "RRULE:FREQ=DAILY;COUNT=3\r\n"
            "RRULE:FREQ=WEEKLY;COUNT=2\r\n"
            "END:VEVENT\r\n"
            "END:VCALENDAR");

    icalcomponent *ical = ical_string_as_icalcomponent(&buf);
    CU_ASSERT_PTR_NOT_NULL(ical);

    strarray_t recurids = STRARRAY_INITIALIZER;
    struct icalperiodtype range = ICALPERIODTYPE_INITIALIZER;
    int r = icalcomponent_myforeach(ical, range, NULL,
        icalcomponent_myforeach_duplicate_rrule_cb, &recurids);
    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_EQUAL(strarray_size(&recurids), 4);
    CU_ASSERT_STRING_EQUAL(strarray_nth(&recurids, 0), "20240101T010203Z");
    CU_ASSERT_STRING_EQUAL(strarray_nth(&recurids, 1), "20240102T010203Z");
    CU_ASSERT_STRING_EQUAL(strarray_nth(&recurids, 2), "20240103T010203Z");
    CU_ASSERT_STRING_EQUAL(strarray_nth(&recurids, 3), "20240108T010203Z");

    strarray_fini(&recurids);
    icalcomponent_free(ical);
    buf_free(&buf);
}

/* vim: set ft=c: */
