// Tester for linked list.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "linkedlist.h"
#include "schemeitem.h"
#include "talloc.h"

void testForward(scheme_item_t *head, int correctLength, bool shouldTestAdvanced)
{
    scheme_item_t *item = head;
    assert(CONS_TYPE == item->type);
    assert(REAL_TYPE == car(item)->type);
    assert(1 == car(item)->r);

    if (shouldTestAdvanced)
    {
        item = cdr(item);
        assert(CONS_TYPE == item->type);
        assert(STR_TYPE == car(item)->type);
        assert(!strcmp("2.0s", car(item)->s));

        item = cdr(item);
        assert(CONS_TYPE == item->type);
        assert(STR_TYPE == car(item)->type);
        assert(!strcmp("3.0s", car(item)->s));
    }

    item = cdr(item);
    assert(CONS_TYPE == item->type);
    assert(REAL_TYPE == car(item)->type);
    assert(4 == car(item)->r);

    if (shouldTestAdvanced)
    {
        item = cdr(item);
        assert(CONS_TYPE == item->type);
        assert(STR_TYPE == car(item)->type);
        assert(!strcmp("5.0s", car(item)->s));
    }

    item = cdr(item);
    assert(CONS_TYPE == item->type);
    assert(REAL_TYPE == car(item)->type);
    assert(6 == car(item)->r);

    item = cdr(item);
    assert(CONS_TYPE == item->type);
    assert(INT_TYPE == car(item)->type);
    assert(7 == car(item)->i);

    item = cdr(item);
    assert(NULL_TYPE == item->type);

    assert(correctLength == length(head));
    assert(!isNull(head));
}

void testBackward(scheme_item_t *head, int correctLength, bool shouldTestAdvanced)
{
    scheme_item_t *item = head;

    assert(CONS_TYPE == item->type);
    assert(INT_TYPE == car(item)->type);
    assert(7 == car(item)->i);

    item = cdr(item);
    assert(CONS_TYPE == item->type);
    assert(REAL_TYPE == car(item)->type);
    assert(6 == car(item)->r);

    if (shouldTestAdvanced)
    {
        item = cdr(item);
        assert(CONS_TYPE == item->type);
        assert(STR_TYPE == car(item)->type);
        assert(!strcmp("5.0s", car(item)->s));
    }

    item = cdr(item);
    assert(CONS_TYPE == item->type);
    assert(REAL_TYPE == car(item)->type);
    assert(4 == car(item)->r);

    if (shouldTestAdvanced)
    {
        item = cdr(item);
        assert(CONS_TYPE == item->type);
        assert(STR_TYPE == car(item)->type);
        assert(!strcmp("3.0s", car(item)->s));

        item = cdr(item);
        assert(CONS_TYPE == item->type);
        assert(STR_TYPE == car(item)->type);
        assert(!strcmp("2.0s", car(item)->s));
    }

    item = cdr(item);
    assert(CONS_TYPE == item->type);
    assert(REAL_TYPE == car(item)->type);
    assert(1 == car(item)->r);

    item = cdr(item);
    assert(NULL_TYPE == item->type);

    assert(correctLength == length(head));
    assert(!isNull(head));
}

int main(int argc, char **argv) {
    bool shouldTestAdvanced = false;
    if (argc == 2 && !strcmp(argv[1], "AD"))
    {
        shouldTestAdvanced = true;
    }

    scheme_item_t *head = makeNull();
    int correctLength = 0;

    scheme_item_t *item1 = talloc(sizeof(scheme_item_t));
    item1->type = INT_TYPE;
    item1->i = 7;
    head = cons(item1,head);
    correctLength++;

    scheme_item_t *item2 = talloc(sizeof(scheme_item_t));
    item2->type = REAL_TYPE;
    item2->r = 6.00;
    head = cons(item2,head);
    correctLength++;

    if (shouldTestAdvanced)
    {
        scheme_item_t *item3 = talloc(sizeof(scheme_item_t));
        item3->type = STR_TYPE;
        char *text = "5.0s";
        item3->s = talloc(sizeof(char)*(strlen(text) + 1));
        strcpy(item3->s, text);
        head = cons(item3,head);
        correctLength++;
    }

    scheme_item_t *item4 = talloc(sizeof(scheme_item_t));
    item4->type = REAL_TYPE;
    item4->r = 4.00000;
    head = cons(item4,head);
    correctLength++;

    if (shouldTestAdvanced)
    {
        scheme_item_t *item5 = talloc(sizeof(scheme_item_t));
        item5->type = STR_TYPE;
        char *text = "3.0s";
        item5->s = talloc(sizeof(char)*(strlen(text) + 1));
        strcpy(item5->s, text);
        head = cons(item5,head);
        correctLength++;

        scheme_item_t *item6 = talloc(sizeof(scheme_item_t));
        item6->type = STR_TYPE;
        text = "2.0s";
        item6->s = talloc(sizeof(char)*(strlen(text) + 1));
        strcpy(item6->s, text);
        head = cons(item6,head);
        correctLength++;
    }

    scheme_item_t *item7 = talloc(sizeof(scheme_item_t));
    item7->type = REAL_TYPE;
    item7->r = 1.0;
    head = cons(item7,head);
    correctLength++;
        
    display(head);
    testForward(head, correctLength, shouldTestAdvanced);

    scheme_item_t *rev = reverse(head);
    display(rev);

    testBackward(rev, correctLength, shouldTestAdvanced);

    // Verify that neither of the lists shares identical cons cells
    scheme_item_t *headcur = head;
    while (headcur->type != NULL_TYPE)
    {
        scheme_item_t *revcur = rev;
        while (revcur->type != NULL_TYPE)
        {
            assert(headcur != revcur);
            revcur = revcur->cdr;
        }

        headcur = headcur->cdr;
    }

    if (shouldTestAdvanced)
    {
        printf(" -=- EMPTY LIST -=- \n");
        scheme_item_t *emptyList = makeNull();
        assert(0 == length(emptyList));
        assert(isNull(emptyList));

        printf(" -=- REVERSE EMPTY LIST -=- \n");
        scheme_item_t *reverseEmpty = reverse(emptyList);
        assert(0 == length(reverseEmpty));
        assert(isNull(reverseEmpty));

        scheme_item_t *justOneByte = talloc(1);
    }

    tfree();

    scheme_item_t *justOneByte = talloc(1);
    texit(0);
}
