#include "general-list.h"
#include <math.h>
#include "smartall.h"

/****
 *
 * Implementation of general-list.h.
 *
 * This is the solution to the deliverable for Lab 2 -- the translation of
 * GeneralList.java into C.  All other supporting C files are provided as part
 * of the lab materials, including general-list.h.
 * 
 * This version of the solution is compiled with the smartalloc functions to
 * provide information about memory leaks.
 */

GeneralList* newGeneralList() {

    /*
     * Allocate the new list.
     */
    GeneralList* newlist = newc(GeneralList);

    /*
     * Initialize the front and back pointers to null.
     */
    newlist->first = null;
    newlist->last = null;

    /*
     * Set the length to 0.
     */
    newlist->length = 0;

    /*
     * Set sorted to false.
     */
    newlist->sorted = false;

    /*
     * Return the new list.
     */
    return newlist;
}

GeneralList* newGeneralListArray(char* array[], int numElems) {

    GeneralList* newlist =  newc(GeneralList);  /* new list */
    int i;                          /* Array traversal index */

    /*
     * Copy in the array elements into the list.
     */
    for (i = 0; i < numElems; i++) {
        putLast(newlist, array[i]);
    }

    /*
     * Set sorted to false.
     */
    newlist->sorted = false;


    /*
     * Return the new list.
     */
    return newlist;
}


/*-*
 * Constructive (i.e., storage) methods
 */

GeneralList* putFirst(GeneralList* list, char* element) {

    /*
     * If the list is currently empty, make a GeneralListNode for the given
     * element and make it the first and last in the list.
     */
    if (list->first == null) {
        list->first = list->last = newGeneralListNode(element);
        list->length = 1;
    }

    /*
     * Otherwise, make a new node and splice it into the front.
     */
    else {
        list->first = list->first->prev = newGeneralListNodeLinked(element, null, list->first);
        list->length++;
    }

    /*
     * Set sorted false in case it is.
     */
    list->sorted = false;

    return list;

}

GeneralList* putLast(GeneralList* list, char* element) {

    /*
     * If the list is currently empty, make a GeneralListNode for the given
     * element and make it the first and last in the list.
     */
    if (list->first == null) {
        list->first = list->last = newGeneralListNode(element);
        list->length = 1;
    }

    /*
     * Otherwise, make a new node and splice it into the back.
     */
    else {
        list->last = list->last->next = newGeneralListNodeLinked(element, list->last, null);
        list->length++;
    }

    /*
     * Set sorted false in case it is.
     */
    list->sorted = false;

    return list;

 }

GeneralList* put(GeneralList* list, char* element, int i) {

    GeneralListNode* ithNode;                /* The located ith node */
    GeneralListNode* newNode;                /* New node to splice in */

    /*
     * Outta here if i < 0 or i > length.
     */
    if ((i < 0) || (i > list->length)) {
        return list;
    }

    /*
     * If i = 0 or length, call putFirst putLast, resp.
     */
    if (i == 0) {
        return putFirst(list, element);
    }
    if (i == list->length) {
        return putLast(list, element);
    }

    /*
     * Get the ith node via the O(N/2) method.
     */
    ithNode = getIthNode(list, i);

    /*
     * Splice in a new node before the ith spot.
     */
    newNode = newGeneralListNodeLinked(element, ithNode->prev, ithNode);
    ithNode->prev = ithNode->prev->next = newNode;
    list->length++;

    /*
     * Set sorted false in case it is.
     */
    list->sorted = false;

    return list;
}

GeneralList* set(GeneralList* list, char* element, int i) {

    GeneralListNode* ithNode;                /* The located ith node */

    /*
     * Get the ith node via the O(N/2) method.
     */
    ithNode = getIthNode(list, i);

    /*
     * Set the ith value.
     */
    ithNode->value = element;

    /*
     * Set sorted false in case it is.
     */
    list->sorted = false;

    return list;
}

char* getFirst(GeneralList* list) {
    return list->first->value;
}

char* getLast(GeneralList* list) {
    return list->last->value;
}

char* get(GeneralList* list, int i) {
    return getIthNode(list, i)->value;
}

char* removeFirst(GeneralList* list) {

    GeneralListNode* node;           /* Node to be removed */

    /*
     * Outta here if we're empty.
     */
    if (list->first == null) {
        return null;
    }

    /*
     * Handle length = 1 case separate since it alone affects value of
     * last pointer.
     */
    if (list->length == 1) {
        node = list->first;
        list->first = list->last = null;
        list->length = 0;
        return node->value;
    }

    /*
     * Splice out the first node and return its value.
     */
    node = list->first;
    list->first = list->first->next;
    list->first->prev = null;
    list->length--;
    return node->value;

}

char* removeLast(GeneralList* list) {

    GeneralListNode* node;           /* Node to be removed */

    /*
     * Outta here if we're empty.
     */
    if (list->first == null) {
        return null;
    }

    /*
     * Handle length = 1 case separate since it alone affects value of
     * last pointer.
     */
    if (list->length == 1) {
        node = list->first;
        list->first = list->last = null;
        list->length = 0;
        return node->value;
    }

    /*
     * Splice out the last node and return its value.
     */
    node = list->last;
    list->last = list->last->prev;
    list->last->next = null;
    list->length--;
    return node->value;

}

char* removeIth(GeneralList* list, int i) {

    GeneralListNode* node;           /* Node to be removed */

    /*
     * Outta here if i < 0 or i > length.
     */
    if ((i < 0) || (i >= list->length)) {
        return null;
    }

    /*
     * If i = 0 or length, call removeFirst removeLast, resp.
     */
    if (i == 0) {
        return removeFirst(list);
    }
    if (i == list->length - 1) {
        return removeLast(list);
    }

    /*
     * Get the ith node, splice it out, and return its value.
     */
    node = getIthNode(list, i);
    node->prev->next = node->next;
    node->next->prev = node->prev;
    list->length--;
    return node->value;

}


/*-*
 * Searching and sorting methods.
 */

bool elementOf(GeneralList* list, char* element) {
    if (list->sorted) {
        return (binarySearch(list, element) >= 0);
    }
    else {
        return (linearSearch(list, element) >= 0);
    }
}

int findIndex(GeneralList* list, char* element) {
    if (list->sorted) {
        return (binarySearch(list, element));
    }
    else {
        return linearSearch(list, element);
    }
}

GeneralList* sort(GeneralList* list) {

    int i, j;                       /* Traversal indices */
    GeneralListNode* nodeI, *nodeJ;   /* Traversal pointers */

    /*
     * Outta here if this is empty.
     */
    if (list->length == 0) {
        return list;
    }

    /*
     * Create the parallel array.
     */
    list->sortedArray = newblock(GeneralListNode*, (list->length + 1));

    /*
     * Use a basic bubble sort algorithm.
     */
    for (i=0, nodeI=list->first; i<list->length-1; i++, nodeI=nodeI->next) {
      for (j=list->length-1, nodeJ=list->last->prev; i<j; j--, nodeJ=nodeJ->prev) {

        if (nodeCompareTo(nodeJ, nodeJ->next) > 0) {
            swapNodeValues(nodeJ, nodeJ->next);
        }

      }
      list->sortedArray[i] = nodeI;

    }
    list->sortedArray[list->length-1] = list->last;

    /*
     * Set sorted to true for use by searching methods.
     */
    list->sorted = true;

    return list;

}

/*-*
 * Utility methods
 */

GeneralList* subList(GeneralList* list, int i, int j) {

    GeneralList* subList;            /* Return value */
    int k;                          /* Sublist creation loop index */

    /*
     * Return null if either index is out of range or i > j.
     */
    if ((i > j) || (i < 0) || (j < 0) || (i >= list->length) || (j >= list->length)) {
        return null;
    }

    /*
     * Make a new list for the return value.
     */
    subList = newGeneralList();

    /*
     * Get the ith through jth elements, put each in the return list, and
     * return the list.
     */
    for (k = i; k <= j; k++) {
        putLast(subList, get(list, k));
    }
    return subList;

}

int length(GeneralList* list) {
    return list->length;
}

bool isEmpty(GeneralList* list) {
    return (list->length == 0);
}

bool equals(GeneralList* list1, GeneralList* list2) {

    GeneralListNode* node1, *node2;     /* Traversal pointers */

    /*
     * Return false immediately if list sizes are different.
     */
    if (list1->length != list2->length) {
        return false;
    }

    for (node1 = list1->first, node2 = list2->first;
            node1 != null; 
                node1 = node1->next, node2 = node2->next) {

        if (! nodeEquals(node1, node2)) {
            return false;
        }

    }
    return true;

}

char* toString(GeneralList* list) {

    char* rtn = "[";                 /* Return value */
    GeneralListNode* node;           /* Traversal pointer */

    /*
     * Return "[]" immediately if list is empty.
     */
    if (list->length == 0) {
        return newstr("[]");
    }

    /*
     * Cruise the list, concatenating of each element onto the return value.
     */
    for (node = list->first; node != list->last; node = node->next) {
        rtn = newstrcat(rtn, newstrcat(node->value, ", "));
    }

    /*
     * Handle the last element, which has no following comma.
     */
    rtn = newstrcat(rtn, newstrcat(node->value, "]"));

    return rtn;
}


/*-*
 * Internal methods
 */

GeneralListNode* getIthNode(GeneralList* list, int i) {

    int j;                           /* Traversal counter */
    GeneralListNode* node;           /* Traversal pointer */

    /*
     * If i is in first half of the list, locate the node starting from the
     * front, else from the back.
     */
    if (i < list->length / 2) {
        for (j = 0, node = list->first; j < i; j++, node = node->next) ;
    }
    else {
        for (j = list->length-1, node = list->last; j > i; j--, node = node->prev) ;
    }

    return node;

}

int linearSearch(GeneralList* list, char* element) {

    GeneralListNode* node;           /* Traversal pointer */
    int i;                           /* Traversal index */

    for (node = list->first, i = 0; node != null; node = node->next, i++) {
        if (valueEquals(element, node->value)) {
            return i;
        }
    }
    return -1;
}

int binarySearch(GeneralList* list, char* element) {

    int midpoint;                   /* Midpoint of startPos/endPos interval */
    int startPos = 0;               /* Initial start position */
    int endPos = list->length - 1;  /* Initial end position */

    /*
     * Iterate through the list while the length of the startPos/endPos
     * interval is >= 1 and we haven't yet found the searched-for element.
     * For each loop iteration, compute the interval midpoint and then do
     * one of the following:
     *
     *     (a) If the searched-for element is at the midpoint, return
     *         successfully.
     *
     *     (b) If the searched-for element is less than the midpoint
     *         element, search the lower half of the list.
     *
     *     (c) If the searched-for element is greater than the midpoint
     *         element, search the upper half of the list.
     */
    while (startPos <= endPos) {
        midpoint = (startPos + endPos) / 2;
        if (valueEquals(element, list->sortedArray[midpoint]->value)) {
            return midpoint;
        }
        else if (valueCompareTo(element, list->sortedArray[midpoint]->value) < 0 ) {
            endPos = midpoint - 1;
        }
        else
            startPos = midpoint + 1;
    }

    /*
     * Fail if we never find the element.
     */
    return -1;

}

void swapNodeValues(GeneralListNode* n1, GeneralListNode* n2) {

    char* temp;            /* Temp value; */

    /*
     * Use standard swap logic.
     */
    temp = n1->value;
    n1->value = n2->value;
    n2->value = temp;

}