#include "config.h"
#include "cunit/cyrunit.h"
#include "xmalloc.h"
#include "util.h"
#include "hash.h"
#include "imap/jmap_util.h"

static void test_patchobject_create(void)
{
#define TESTCASE(src, dst, want) \
    { \
        json_t *jsrc = json_loads(src, JSON_DECODE_ANY, NULL); \
        json_t *jdst = json_loads(dst, JSON_DECODE_ANY, NULL); \
        \
        json_t *jwant = json_loads(want, JSON_DECODE_ANY, NULL); \
        json_t *jdiff = jmap_patchobject_create(jsrc, jdst); \
        CU_ASSERT_PTR_NOT_NULL(jdiff); \
        \
        char *swant = json_dumps(jwant, JSON_SORT_KEYS|JSON_ENCODE_ANY); \
        char *sdiff = json_dumps(jdiff, JSON_SORT_KEYS|JSON_ENCODE_ANY); \
        CU_ASSERT_STRING_EQUAL(swant, sdiff); \
        \
        free(sdiff); \
        free(swant); \
        json_decref(jdiff); \
        json_decref(jwant); \
        json_decref(jdst); \
        json_decref(jsrc); \
    }

    const char *src, *dst, *want;

    /* Remove one property at top-level */
    src = "{"
    "  \"a\": 1,"
    "  \"b\": 1"
    "}";
    dst = "{"
    "  \"a\": 1"
    "}";
    want = "{"
    "  \"b\": null"
    "}";
    TESTCASE(src, dst, want);

    /* Add one property at top-level */
    src = "{"
    "  \"a\": 1"
    "}";
    dst = "{"
    "  \"a\": 1,"
    "  \"b\": 1"
    "}";
    want = "{"
    "  \"b\": 1"
    "}";
    TESTCASE(src, dst, want);

    /* Replace one scalar property at top-level with another */
    src = "{"
    "  \"a\": 1"
    "}";
    dst = "{"
    "  \"a\": 2"
    "}";
    want = "{"
    "  \"a\": 2"
    "}";
    TESTCASE(src, dst, want);

    /* Replace one object property at top-level with a scalar */
    src = "{"
    "  \"a\": {"
    "    \"b\": 1"
    "  }"
    "}";
    dst = "{"
    "  \"a\": 2"
    "}";
    want = "{"
    "  \"a\": 2"
    "}";
    TESTCASE(src, dst, want);

    /* Replace one scalar property at top-level with an object */
    src = "{"
    "  \"a\": {"
    "    \"b\": 1"
    "  }"
    "}";
    dst = "{"
    "  \"a\": 2"
    "}";
    want = "{"
    "  \"a\": 2"
    "}";
    TESTCASE(src, dst, want);

    /* Add a nested property */
    src = "{"
    "  \"a\": {"
    "    \"b\": 1"
    "  }"
    "}";
    dst = "{"
    "  \"a\": {"
    "    \"b\": 1,"
    "    \"c\": 2"
    "  }"
    "}";
    want = "{"
    "  \"a/c\": 2"
    "}";
    TESTCASE(src, dst, want);

    /* Remove a nested property */
    src = "{"
    "  \"a\": {"
    "    \"b\": 1,"
    "    \"c\": 2"
    "  }"
    "}";
    dst = "{"
    "  \"a\": {"
    "    \"b\": 1"
    "  }"
    "}";
    want = "{"
    "  \"a/c\": null"
    "}";
    TESTCASE(src, dst, want);


#undef TESTCASE
}

static void test_decode_to_utf8(void)
{
    struct testcase {
        const char *data;
        const char *charset;
        int encoding;
        float confidence;
        const char *want_val;
        int want_alloc;
        int want_is_encoding_problem;
    };

    struct testcase tcs[] = {{
        // ISO-8859-1 encoded data claims to be UTF-8, confidence 0.51
#ifdef HAVE_LIBCHARDET
        "Ad""\xe9""la""\xef""de",
        "utf-8",
        ENCODING_NONE,
        0.51,
        "Ad""\xc3\xa9""la""\xc3\xaf""de",
        1,
        1
#else
        "Ad""\xe9""la""\xef""de",
        "utf-8",
        ENCODING_NONE,
        0.51,
        "Ad""\xef\xbf\xbd""la""\xef\xbf\xbd""de",
        1,
        1
#endif
    }, {
        // ISO-8859-1 encoded data claims to be UTF-8, confidence 1.0
        "Ad""\xe9""la""\xef""de",
        "utf-8",
        ENCODING_NONE,
        1.0,
        "Ad""\xef\xbf\xbd""la""\xef\xbf\xbd""de",
        1,
        1
    }, {
        // Fast-path valid UTF-8
        "Ad""\xc3\xa9""la""\xc3\xaf""de",
        "utf-8",
        ENCODING_NONE,
        0.51,
        "Ad""\xc3\xa9""la""\xc3\xaf""de",
        0,
        0
    }, {
        // Fast-path valid UTF-8 with replacement chars
        "Ad""\xef\xbf\xbd""la""\xef\xbf\xbd""de",
        "utf-8",
        ENCODING_NONE,
        0.51,
        "Ad""\xef\xbf\xbd""la""\xef\xbf\xbd""de",
        0,
        0
    }, {
        NULL, NULL, ENCODING_UNKNOWN, 0.0, NULL, 0, 0
    }};

    struct testcase *tc;
    for (tc = tcs; tc->data; tc++) {
        char *cbuf = NULL;
        int is_problem = 0;
        const char *cval = jmap_decode_to_utf8(tc->charset, tc->encoding,
            tc->data, strlen(tc->data), tc->confidence, &cbuf, &is_problem);
        if (tc->want_val)
            CU_ASSERT_STRING_EQUAL(tc->want_val, cval);
        else
            CU_ASSERT_PTR_NULL(cval);
        if (tc->want_alloc)
            CU_ASSERT_PTR_NOT_NULL(cbuf);
        else
            CU_ASSERT_PTR_NULL(cbuf);
        CU_ASSERT_EQUAL(tc->want_is_encoding_problem, is_problem);
        free(cbuf);
    }
}

/* vim: set ft=c: */
