#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>

#include "./include/list.h"
static listEntry_t *listEntry_create(void *data) {
    listEntry_t *createdListEntry = malloc(sizeof(listEntry_t));
    if(createdListEntry == NULL) {
        // TODO Malloc Failure
        return NULL;
    }
    createdListEntry->data = data;

    return createdListEntry;
}

void listEntry_free(listEntry_t *listEntry, void *additionalData) {
    free(listEntry);
}

void listEntry_printAsInt(listEntry_t *listEntry, void *additionalData) {
    printf("%i\n", *((int *) (listEntry->data)));
}

list_t *list_create() {
    list_t *createdList = malloc(sizeof(list_t));
    if(createdList == NULL) {
        // TODO Malloc Failure
        return NULL;
    }
    createdList->firstEntry = NULL;
    createdList->lastEntry = NULL;
    createdList->count = 0;

    return createdList;
}

void list_iterateThroughListForward(list_t *list,
                                    void (*operationToPerform)(
                                        listEntry_t *,
                                        void *callbackFunctionUsedData),
                                    void *additionalDataNeededForCallbackFunction)
{
    listEntry_t *currentListEntry = list->firstEntry;
    listEntry_t *nextListEntry;

    if(currentListEntry == NULL) {
        return;
    }

    nextListEntry = currentListEntry->nextListEntry;

    operationToPerform(currentListEntry,
                       additionalDataNeededForCallbackFunction);
    currentListEntry = nextListEntry;

    while(currentListEntry != NULL) {
        nextListEntry = currentListEntry->nextListEntry;
        operationToPerform(currentListEntry,
                           additionalDataNeededForCallbackFunction);
        currentListEntry = nextListEntry;
    }
}

void list_iterateThroughListBackward(list_t *list,
                                     void (*operationToPerform)(
                                         listEntry_t *,
                                         void *callbackFunctionUsedData),
                                     void *additionalDataNeededForCallbackFunction)
{
    listEntry_t *currentListEntry = list->lastEntry;
    listEntry_t *nextListEntry = currentListEntry->prevListEntry;

    if(currentListEntry == NULL) {
        return;
    }

    operationToPerform(currentListEntry,
                       additionalDataNeededForCallbackFunction);
    currentListEntry = nextListEntry;

    while(currentListEntry != NULL) {
        nextListEntry = currentListEntry->prevListEntry;
        operationToPerform(currentListEntry,
                           additionalDataNeededForCallbackFunction);
        currentListEntry = nextListEntry;
    }
}

void list_free(list_t *list) {
    list_iterateThroughListForward(list, listEntry_free, NULL);
    free(list);
}

void list_addElement(list_t *list, void *dataToAddInList) {
    listEntry_t *newListEntry = listEntry_create(dataToAddInList);
    if(newListEntry == NULL) {
        // TODO Malloc Failure
        return;
    }
    if(list->firstEntry == NULL) {
        list->firstEntry = newListEntry;
        list->lastEntry = newListEntry;

        newListEntry->prevListEntry = NULL;
        newListEntry->nextListEntry = NULL;

        list->count++;

        return;
    }

    list->lastEntry->nextListEntry = newListEntry;
    newListEntry->prevListEntry = list->lastEntry;
    newListEntry->nextListEntry = NULL;
    list->lastEntry = newListEntry;

    list->count++;
}

void list_removeElement(list_t *list, listEntry_t *elementToRemove) {
    listEntry_t *elementBeforeElementToRemove =
        elementToRemove->prevListEntry;
    listEntry_t *elementAfterElementToRemove =
        elementToRemove->nextListEntry;

    if(elementBeforeElementToRemove != NULL) {
        elementBeforeElementToRemove->nextListEntry =
            elementAfterElementToRemove;
    } else {
        list->firstEntry = elementAfterElementToRemove;
    }

    if(elementAfterElementToRemove != NULL) {
        elementAfterElementToRemove->prevListEntry =
            elementBeforeElementToRemove;
    } else {
        list->lastEntry = elementBeforeElementToRemove;
    }

    listEntry_free(elementToRemove, NULL);

    list->count--;
}

listEntry_t *list_getElementAt(list_t *list, long indexOfElement) {
    listEntry_t *currentListEntry;

    if((list->count / 2) > indexOfElement) {
        currentListEntry = list->firstEntry;

        for(int i = 0; i < indexOfElement; i++) {
            currentListEntry = currentListEntry->nextListEntry;
        }

        return currentListEntry;
    } else {
        currentListEntry = list->lastEntry;

        for(int i = 1; i < (list->count - indexOfElement); i++) {
            currentListEntry = currentListEntry->prevListEntry;
        }

        return currentListEntry;
    }
}
