/*
 * Copyright 2011 Steven Gribble
 *
 *  This file is part of the UW CSE 333 course project sequence
 *  (333proj).
 *
 *  333proj is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  333proj is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with 333proj.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _HW1_LL_H_
#define _HW1_LL_H_

// A LinkedList is a doubly-linked list.  We provide the interface to
// the LinkedList; your job is to fill in the implementation.


// To hide the implementation of LinkedList, we'll just define it to
// be a (void *), i.e., a pointer of generic type void.  In ll.c,
// we'll actually declare what the structure contains and we'll use
// casting back and forth between (void *) and the actual structure.
//
// By using this trick, customers of the ll.h package don't see
// needless implementation details of our linked list.
typedef void *LinkedList;

// Customers of ll.h occasionally need to pass a function pointer as
// an argument to one of our functions.
//
// When freeing a LinkedList, they need to pass a pointer to a
// function that frees the payload of a linked list node. The
// pointed-to function is invoked once for each node in the
// linked list before the LinkedList itself is freed.
typedef void(*PayloadFreeFnPtr)(void *payload);

// When sorting a linked list or comparing two elements of a linked
// list, customers must pass in a comparator function.  The
// function accepts two payload pointers as arguments, and returns
// an integer that is:
//    -1  if payload_a < payload_b
//     0  if payload_a == payload_b
//     1  if payload_a > payload_b
typedef int(*PayloadComparatorFnPtr)(void *payload_a,
                                     void *payload_b);

// Allocate and return a new linked list.
//
// Arguments:  none.
//
// Returns NULL on error, non-NULL on success.
LinkedList AllocateLinkedList(void);

// Free a linked list.
//
// Arguments:
//
// - list:  the LinkedList to free.  It is unsafe to use list
//   after this function returns.
//
// - payload_free_function:  this argument is a pointer to a payload
//   freeing function; see above for details.
void FreeLinkedList(LinkedList list,
                    PayloadFreeFnPtr payload_free_function);

// Figure out the number of elements in the linked list.
//
// Arguments:
//
// - list:  the list to query
//
// Returns:
//
// - list length (>=0) for a valid list
//
// - -1 on error
unsigned int NumElementsInLinkedList(LinkedList list);

// Inserts a payload as a new element on the head of the linked list.
//
// Arguments:
//
// - list: the LinkedList to push onto
//
// - payload: the payload to add; it's up to the caller to
//   interpret and manage the memory of the payload.
//
// Returns 0 on failure, +1 on success.
int PushLinkedList(LinkedList list, void *payload);

// Pops an element from the head of the linked list.
//
// Arguments:
//
// - list: the LinkedList to pop from
//
// - payload_ptr: a return parameter that is set by the callee;
//   on success, the payload is returned through this parameter.
//
// Returns 0 on failure, +1 on success.
int PopLinkedList(LinkedList list, void **payload_ptr);

// Inserts a payload as a new element on the tail of the linked list.
//
// Arguments:
//
// - list: the LinkedList to append to
//
// - payload: the payload to add; it's up to the caller to
//   interpret and manage the memory of the payload.
//
// Returns 0 on failure, +1 on success.
int AppendLinkedList(LinkedList list, void *payload);

// Slices an element off the tail of the linked list.
//
// Arguments:
//
// - list: the LinkedList to slice from
//
// - payload_ptr: a return parameter that is set by the callee;
//   on success, the payload is returned through this parameter.
//
// Returns 0 on failure, +1 on success.
int SliceLinkedList(LinkedList list, void **payload_ptr);

// Sorts a LinkedList in place.
//
// Arguments:
//
// - list: the list to sort
//
// - ascending: if 0, sorts descending, else sorts ascending.
//
// - comparator_function:  this argument is a pointer to
//   a payload comparator function; see above.
void SortLinkedList(LinkedList list, unsigned int ascending,
                    PayloadComparatorFnPtr comparator_function);


// Linked lists support the notion of an iterator, similar to
// Java iterators.  You use an iterator to navigate back and
// forth through the linked list and insert/remove elements
// from the list.  You use LinkedListMakeIterator() to
// manufacture a new iterator, and LLIteratorFree() to
// free an iterator when you're done with it.
//
// If you use a LinkedList*() function to mutate
// a linked list, any iterators you have on that list become dangerous
// to use (so dangerous that arbitrary memory corruption
// can occur). Thus, we recommend that you only use LLIterator*()
// functions in between the manufacturing and freeing of an iterator.

typedef void *LLIter;  // same trick to hide implementation.

// Manufacture an iterator for the list.  Customers can
// use an iterator to navigate through the list; an
// iterator has the ability to both advance and
// to go backwards.
//
// Arguments:
//
// - list:  the list from which to return an iterator
//
// - pos:  where the iterator should start.  0: head. +1: tail.
//
// Returns NULL on failure, non-NULL on success.
LLIter LinkedListMakeIterator(LinkedList list, int pos);

// When you're done with an iterator, you need to free it
// by calling this function.
//
// Arguments:
//
// - iter: the iterator to free.  It is not safe to use
// the iterator after freeing it.
void LLIteratorFree(LLIter iter);

// Move the iterator to the next element of the list.
//
// Arguments:
//
// - iter: the iterator to move
//
// Returns:
//
// - +1 on success
//
// - 0 if the iterator is already at the tail end of the list; if so,
//   the iterator is not modified and can still be used.
int LLIteratorNext(LLIter iter);

// Tests to see whether the iterator is at the tail
// end of the list.
//
// Arguments:
//
// - iter: the iterator to test
//
// Returns:
//
// - +1 if iter is at the tail of the list or the list is empty.
//
// - 0 if iter is not at the tail of the list (and therefore the list
//   is non-empty).
int LLIteratorAtTail(LLIter iter);

// Move the iterator to the previous element of the list.
//
// Arguments:
//
// - iter: the iterator to move
//
// Returns:
//
// - +1 on success
//
// - 0 if the iterator is already at the head end of the list; if so,
//   the iterator is not modified and can still be used.
int LLIteratorPrev(LLIter iter);

// Tests to see whether the iterator is at the head
// end of the list.
//
// Arguments:
//
// - iter: the iterator to test
//
// Returns:
//
// - +1 if iter is at the head of the list or the list is empty.
//
// - 0 if iter is not at the head of the list (and therefore the list
//   is non-empty).
int LLIteratorAtHead(LLIter iter);

// Returns the payload of the list node that the iterator currently
// points at.
//
// Arguments:
//
// - iter: the iterator to fetch the payload from.
//
// - payload: a "return parameter" through which the payload
//   is returned.
//
// Returns:
//
// - 0 if the list is empty and therefore there is no payload.
//
// - +1 on success.
int LLIteratorGetPayload(LLIter iter, void **payload);

// Deletes the node the iterator is currently pointing to.
// After deletion, the iterator points to:
//  - the remaining element, if only one element remains
//  - the successor of the deleted node, if there is a
//    successor
//  - the predecessor of the deleted node, if the iterator
//    was pointing at the tail (i.e., there is no successor)
//
// If there is only a single element in the list, then after
// invoking this deletion function, that element is deleted
// and the iterator is no longer usable.
//
// Arguments:
//
//  - iter: the iterator to delete from
//
//  - payload_free_function: invoked to free the payload
//
// Returns:
//
// - -1 if the list is empty and the delete failed.
//
// - 0 if the delete succeeded and the modified list is now empty.
//
// - +1 if the delete succeeded and the modified list is non-empty.
int LLIteratorDelete(LLIter iter,
                     PayloadFreeFnPtr payload_free_function);

// Inserts a new element before the node the iterator points at.
// If the list is empty, inserts the first element.
//
// Arguments:
//
//  - iter:  the iterator to insert before
//
//  - payload:  the payload to insert
//
// Returns:
//
// - 0 on failure
//
// - +1 on success; the iterator still points to the current
// node, not the inserted node.
int LLIteratorInsertBefore(LLIter iter,
                           void *payload);

#endif  // _HW1_LL_H_