#include "config.h"
#include "cunit/unit.h"
#include "prot.h"
#include "lib/libcyr_cfg.h"
#include "lib/libconfig.h"
#include "imap/dlist.h"
#include "util.h"

#define DBDIR           "test-dbdir"

static int set_up(void)
{
    /* need basic configuration for getxstring */
    libcyrus_config_setstring(CYRUSOPT_CONFIG_DIR, DBDIR);
    config_read_string(
        "configdirectory: "DBDIR"/conf\n"
    );

    return 0;
}

static int tear_down(void)
{
    int r;

    config_reset();

    r = system("rm -rf " DBDIR);

    return r;
}

/* XXX - need LOTS of dlist tests */

static void test_nil(void)
{
    struct buf b = BUF_INITIALIZER;
    struct dlist *dl = dlist_newlist(NULL, NULL);
    struct dlist *item;
    int r;

    dlist_setatom(dl, "STRING", "NIL");
    dlist_setatom(dl, "EMPTY", NULL);
    dlist_setatom(dl, "DOUBLEWHAMMY", "\"NIL\"");
    dlist_printbuf(dl, 0, &b);

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b), "(\"NIL\" NIL {5+}\r\n\"NIL\")");
    dlist_free(&dl);

    r = dlist_parsemap(&dl, 0, b.s, b.len);
    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NOT_NULL(dl);

    item = dlist_getchildn(dl, 0);
    CU_ASSERT_PTR_NOT_NULL(item);
    CU_ASSERT_EQUAL(item->type, DL_ATOM);
    CU_ASSERT_STRING_EQUAL(item->sval, "NIL");

    item = dlist_getchildn(dl, 1);
    CU_ASSERT_PTR_NOT_NULL(item);
    CU_ASSERT_EQUAL(item->type, DL_NIL);
    CU_ASSERT_PTR_NULL(item->sval);

    item = dlist_getchildn(dl, 2);
    CU_ASSERT_PTR_NOT_NULL(item);
    CU_ASSERT_EQUAL(item->type, DL_BUF);
    CU_ASSERT_STRING_EQUAL(item->sval, "\"NIL\"");

    dlist_free(&dl);
    buf_free(&b);
}

static void test_splice_middle(void)
{
    struct dlist *dl = dlist_newlist(NULL, "NAME");
    struct dlist *dl2 = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int i;

    for (i = 1; i <= 10; i++) {
        dlist_setnum32(dl, "NUM", i);
    }

    dl2 = dlist_splice(dl, 5);

    dlist_printbuf(dl, 0, &b);
    dlist_printbuf(dl2, 0, &b2);

    CU_ASSERT_STRING_EQUAL(dl->name, "NAME");
    CU_ASSERT_STRING_EQUAL(dl2->name, "NAME");

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b), "(6 7 8 9 10)");
    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "(1 2 3 4 5)");

    dlist_free(&dl);
    dlist_free(&dl2);
    buf_free(&b);
    buf_free(&b2);
}

static void test_splice_none(void)
{
    struct dlist *dl = dlist_newlist(NULL, "NAME");
    struct dlist *dl2 = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int i;

    for (i = 1; i <= 10; i++) {
        dlist_setnum32(dl, "NUM", i);
    }

    dl2 = dlist_splice(dl, 0);

    dlist_printbuf(dl, 0, &b);
    dlist_printbuf(dl2, 0, &b2);

    CU_ASSERT_STRING_EQUAL(dl->name, "NAME");
    CU_ASSERT_STRING_EQUAL(dl2->name, "NAME");

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b), "(1 2 3 4 5 6 7 8 9 10)");
    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "()");

    dlist_free(&dl);
    dlist_free(&dl2);
    buf_free(&b);
    buf_free(&b2);
}

static void test_splice_one(void)
{
    struct dlist *dl = dlist_newlist(NULL, "NAME");
    struct dlist *dl2 = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int i;

    for (i = 1; i <= 10; i++) {
        dlist_setnum32(dl, "NUM", i);
    }

    dl2 = dlist_splice(dl, 1);

    dlist_printbuf(dl, 0, &b);
    dlist_printbuf(dl2, 0, &b2);

    CU_ASSERT_STRING_EQUAL(dl->name, "NAME");
    CU_ASSERT_STRING_EQUAL(dl2->name, "NAME");

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b), "(2 3 4 5 6 7 8 9 10)");
    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "(1)");

    dlist_free(&dl);
    dlist_free(&dl2);
    buf_free(&b);
    buf_free(&b2);
}

static void test_splice_allbut(void)
{
    struct dlist *dl = dlist_newlist(NULL, "NAME");
    struct dlist *dl2 = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int i;

    for (i = 1; i <= 10; i++) {
        dlist_setnum32(dl, "NUM", i);
    }

    dl2 = dlist_splice(dl, 9);

    dlist_printbuf(dl, 0, &b);
    dlist_printbuf(dl2, 0, &b2);

    CU_ASSERT_STRING_EQUAL(dl->name, "NAME");
    CU_ASSERT_STRING_EQUAL(dl2->name, "NAME");

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b), "(10)");
    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "(1 2 3 4 5 6 7 8 9)");

    dlist_free(&dl);
    dlist_free(&dl2);
    buf_free(&b);
    buf_free(&b2);
}

static void test_splice_all(void)
{
    struct dlist *dl = dlist_newlist(NULL, "NAME");
    struct dlist *dl2 = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int i;

    for (i = 1; i <= 10; i++) {
        dlist_setnum32(dl, "NUM", i);
    }

    dl2 = dlist_splice(dl, 10);

    dlist_printbuf(dl, 0, &b);
    dlist_printbuf(dl2, 0, &b2);

    CU_ASSERT_STRING_EQUAL(dl->name, "NAME");
    CU_ASSERT_STRING_EQUAL(dl2->name, "NAME");

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b), "()");
    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "(1 2 3 4 5 6 7 8 9 10)");

    dlist_free(&dl);
    dlist_free(&dl2);
    buf_free(&b);
    buf_free(&b2);
}

static void test_splice_over(void)
{
    struct dlist *dl = dlist_newlist(NULL, "NAME");
    struct dlist *dl2 = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int i;

    for (i = 1; i <= 10; i++) {
        dlist_setnum32(dl, "NUM", i);
    }

    dl2 = dlist_splice(dl, 11);

    dlist_printbuf(dl, 0, &b);
    dlist_printbuf(dl2, 0, &b2);

    CU_ASSERT_STRING_EQUAL(dl->name, "NAME");
    CU_ASSERT_STRING_EQUAL(dl2->name, "NAME");

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b), "()");
    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "(1 2 3 4 5 6 7 8 9 10)");

    dlist_free(&dl);
    dlist_free(&dl2);
    buf_free(&b);
    buf_free(&b2);
}

static void test_splat_start(void)
{
    struct dlist *dl = NULL;
    struct dlist *sub = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int r;

    buf_printf(&b, "((a b) (c d) (e f))");

    r = dlist_parsemap(&dl, 0, b.s, b.len);

    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NOT_NULL(dl);

    sub = dlist_getchildn(dl, 0);

    dlist_splat(dl, sub);

    dlist_printbuf(dl, 0, &b2);

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "(a b (c d) (e f))");

    dlist_free(&dl);
    buf_free(&b);
    buf_free(&b2);
}

static void test_splat_middle(void)
{
    struct dlist *dl = NULL;
    struct dlist *sub = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int r;

    buf_printf(&b, "((a b) (c d) (e f))");

    r = dlist_parsemap(&dl, 0, b.s, b.len);

    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NOT_NULL(dl);

    sub = dlist_getchildn(dl, 1);

    dlist_splat(dl, sub);

    dlist_printbuf(dl, 0, &b2);

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "((a b) c d (e f))");

    dlist_free(&dl);
    buf_free(&b);
    buf_free(&b2);
}

static void test_splat_end(void)
{
    struct dlist *dl = NULL;
    struct dlist *sub = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int r;

    buf_printf(&b, "((a b) (c d) (e f))");

    r = dlist_parsemap(&dl, 0, b.s, b.len);

    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NOT_NULL(dl);

    sub = dlist_getchildn(dl, 2);

    dlist_splat(dl, sub);

    dlist_printbuf(dl, 0, &b2);

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "((a b) (c d) e f)");

    dlist_free(&dl);
    buf_free(&b);
    buf_free(&b2);
}

static void test_splat_short(void)
{
    struct dlist *dl = NULL;
    struct dlist *sub = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int r;

    buf_printf(&b, "((a b) (c) (d e))");

    r = dlist_parsemap(&dl, 0, b.s, b.len);

    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NOT_NULL(dl);

    sub = dlist_getchildn(dl, 1);

    dlist_splat(dl, sub);

    dlist_printbuf(dl, 0, &b2);

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "((a b) c (d e))");

    dlist_free(&dl);
    buf_free(&b);
    buf_free(&b2);
}

static void test_splat_empty(void)
{
    struct dlist *dl = NULL;
    struct dlist *sub = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int r;

    buf_printf(&b, "((a b) () (c d))");

    r = dlist_parsemap(&dl, 0, b.s, b.len);

    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NOT_NULL(dl);

    sub = dlist_getchildn(dl, 1);

    dlist_splat(dl, sub);

    dlist_printbuf(dl, 0, &b2);

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "((a b) (c d))");

    dlist_free(&dl);
    buf_free(&b);
    buf_free(&b2);
}

static void test_splat_nonlist(void)
{
    struct dlist *dl = NULL;
    struct dlist *sub = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int r;

    buf_printf(&b, "(a b c d e)");

    r = dlist_parsemap(&dl, 0, b.s, b.len);

    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NOT_NULL(dl);

    sub = dlist_getchildn(dl, 2);

    dlist_splat(dl, sub);

    dlist_printbuf(dl, 0, &b2);

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), "(a b d e)");

    dlist_free(&dl);
    buf_free(&b);
    buf_free(&b2);
}

static void test_deepstructure(void)
{
    struct dlist *dl = NULL;
    struct dlist *di = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    int r;

    buf_setcstr(&b, "%(toplevel %(sub thing) ANOTHER (value is %(list with interesting things) (in it)) ExTrA ExTrA READ \"all about it\" EMPTYLIST () EMPTYKVLIST %() INCLUDING 123456)");

    r = dlist_parsemap(&dl, 0, b.s, b.len);

    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NOT_NULL(dl);

    di = dlist_getchild(dl, "INCLUDING");
    CU_ASSERT_EQUAL(dlist_num(di), 123456);

    dlist_printbuf(dl, 0, &b2);

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), buf_cstring(&b));

    dlist_free(&dl);
    buf_free(&b);
    buf_free(&b2);
}

static void test_printiter(void)
{
    struct dlist *dl = NULL;
    struct dlist_print_iter *iter = NULL;
    struct buf b = BUF_INITIALIZER;
    struct buf b2 = BUF_INITIALIZER;
    struct buf tmp = BUF_INITIALIZER;
    int r;

    buf_setcstr(&b, "%(toplevel %(sub thing) ANOTHER (value is %(list with interesting things) (in it)) ExTrA ExTrA READ \"all about it\" EMPTYLIST () EMPTYKVLIST %() INCLUDING 123456)");

    r = dlist_parsemap(&dl, 0, b.s, b.len);

    CU_ASSERT_EQUAL(r, 0);
    CU_ASSERT_PTR_NOT_NULL(dl);

    iter = dlist_print_iter_new(dl, /*printkeys*/ 0);
    CU_ASSERT_PTR_NOT_NULL(iter);

    while (dlist_print_iter_step(iter, &tmp)) {
        buf_append(&b2, &tmp);
    }

    CU_ASSERT_STRING_EQUAL(buf_cstring(&b2), buf_cstring(&b));

    dlist_print_iter_free(&iter);
    dlist_free(&dl);
    buf_free(&b);
    buf_free(&b2);
    buf_free(&tmp);
}

/* vim: set ft=c: */
