// dl_list.c - example implementation for a double linked list

#include "dl_list.h"
#include "dl_list_private.h"

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

//============================================================
// access functions for DLList

//------------------------------------------------------------
DLList *newDLList()
{
  DLList *list = malloc(sizeof(DLList));
  if (!list)
    exitOnFatalErrorOutOfMemory();
  list->first = list->last = NULL;
  return(list);
}

//------------------------------------------------------------
void deleteDLList(DLList *list)
{
  DLListNode *current = list->first;
  if (!list)
    exitOnFatalErrorIllegalArgument("deleteDLList");
  while(current)
  {
    DLListNode *next = getNextFromDLListNode(current);
    deleteDLListNode(current);
    current = next;
  }
  free(list);
}

//------------------------------------------------------------
void setFirstInDLList(DLList *list,DLListNode *node)
{
  if (!list)
    exitOnFatalErrorIllegalArgument("setFirstInDLList");
  list->first = node;
}

//------------------------------------------------------------
DLListNode *getFirstFromDLList(DLList *list)
{
  if (!list)
    exitOnFatalErrorIllegalArgument("getFirstFromDLList");
  return(list->first);
}

//------------------------------------------------------------
void setLastInDLList(DLList *list,DLListNode *node)
{
  if (!list)
    exitOnFatalErrorIllegalArgument("setLastInDLList");
  list->last = node;
}

//------------------------------------------------------------
DLListNode *getLastFromDLList(DLList *list)
{
  if (!list)
    exitOnFatalErrorIllegalArgument("getLastFromDLList");
  return(list->last);
}


//============================================================
// all DLListNode access functions

//------------------------------------------------------------
DLListNode *newDLListNode(void *content)
{
  DLListNode *node = malloc(sizeof(DLListNode));
  if (!node)
    exitOnFatalErrorOutOfMemory();
  node->content = content;
  node->previous = node->next = NULL;
  return(node);
}


//------------------------------------------------------------
void deleteDLListNode(DLListNode *node)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("deleteDLListNode");
  if (node->content)
    free(node->content);
  free(node);
  return;
}

//------------------------------------------------------------
void setNextInDLListNode(DLListNode *node,DLListNode *next)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("setNextInDLListNode");
  node->next = next;
}

//------------------------------------------------------------
DLListNode *getNextFromDLListNode(DLListNode *node)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("getNextFromDLListNode");
  return(node->next);
}

//------------------------------------------------------------
void setPreviousInDLListNode(DLListNode *node,DLListNode *previous)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("setPreviousInDLListNode");
  node->previous = previous;
}

//------------------------------------------------------------
DLListNode *getPreviousFromDLListNode(DLListNode *node)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("getPreviousFromDLListNode");
  return(node->previous);
}

//------------------------------------------------------------
void setContentInDLListNode(DLListNode *node,void *content)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("setContentInDLListNode");
  if (node->content)
    free(node->content);
  node->content = content;
}

//------------------------------------------------------------
void *getContentFromDLListNode(DLListNode *node)
{
  if (!node)
    return(NULL);
  return(node->content);
}

//============================================================
// functions for the list iterator

//------------------------------------------------------------
DLListIterator *newDLListIterator(DLList *list)
{
  DLListIterator *iterator = malloc(sizeof(DLListIterator));
  if (!list)
    exitOnFatalErrorIllegalArgument("newDLListIterator");
  if (!iterator)
    exitOnFatalErrorOutOfMemory();
  iterator->list = list;
  iterator->current = getFirstFromDLList(list);
  return(iterator);
}

//------------------------------------------------------------
void deleteDLListIterator(DLListIterator *iterator)
{
  if (!iterator)
    exitOnFatalErrorIllegalArgument("deleteDLListIterator");
  free(iterator);
}

//------------------------------------------------------------
void insertElementAfterCurrent(DLListIterator *iterator,
                               void *content)
{
  DLListNode *node = newDLListNode(content);
  DLListNode *current = NULL;
  DLListNode *next = NULL;

  if (!iterator)
    exitOnFatalErrorIllegalArgument("insertElementAfterCurrent");

  current = iterator->current;
  next = current ? getNextFromDLListNode(current) : NULL;

  if (!current) // either first node or illegal state
  {
    if (getFirstFromDLList(iterator->list))
      exitOnFatalErrorIllegalState("insertElementAfterCurrent");
    setFirstInDLList(iterator->list,node);
    setLastInDLList(iterator->list,node);
    iterator->current = node;
    return;
  }
  setPreviousInDLListNode(node,current);
  setNextInDLListNode(node,next);
  setNextInDLListNode(current,node);
  if (next)
    setPreviousInDLListNode(next,node);
  else
    setLastInDLList(iterator->list,node);
  iterator->current = node;
}

//------------------------------------------------------------
void insertElementBeforeCurrent(DLListIterator *iterator,
                                void *content)
{
  DLListNode *node = newDLListNode(content);
  DLListNode *current = NULL;
  DLListNode *previous = NULL;

  if (!iterator)
    exitOnFatalErrorIllegalArgument("insertElementBeforeCurrent");

  current = iterator->current;
  previous = current ? getPreviousFromDLListNode(current) : NULL;

  if (!current) // either first node or illegal state
  {
    if (getFirstFromDLList(iterator->list))
      exitOnFatalErrorIllegalState("insertElementAfterCurrent");
    setFirstInDLList(iterator->list,node);
    setLastInDLList(iterator->list,node);
    iterator->current = node;
    return;
  }
  setPreviousInDLListNode(node,previous);
  setNextInDLListNode(node,current);
  setPreviousInDLListNode(current,node);
  if (previous)
    setNextInDLListNode(previous,node);
  else
    setFirstInDLList(iterator->list,node);
  iterator->current = node;
}

//------------------------------------------------------------
void deleteCurrentElement(DLListIterator *iterator)
{
  DLListNode *current = NULL;
  DLListNode *previous = NULL;
  DLListNode *next = NULL;

  if (!iterator)
    exitOnFatalErrorIllegalArgument("deleteCurrentElement");

  current = iterator->current;

  if (!current)
    exitOnFatalErrorIllegalState("deleteCurrentElement");

  previous = getPreviousFromDLListNode(current);
  next = getNextFromDLListNode(current);

  if (!previous) // first node is deleted
    setFirstInDLList(iterator->list,next);
  else
    setNextInDLListNode(previous,next);
  if (!next) // last node is deleted
    setLastInDLList(iterator->list,previous);
  else
    setPreviousInDLListNode(next,previous);
  deleteDLListNode(current);
  iterator->current = next;
}

//------------------------------------------------------------
void *setCurrentToFirstElement(DLListIterator *iterator)
{
  if (!iterator)
    exitOnFatalErrorIllegalArgument("setCurrentToFirstElement");
  iterator->current = getFirstFromDLList(iterator->list);
  return(getContentFromDLListNode(iterator->current));
}

//------------------------------------------------------------
void *setCurrentToLastElement(DLListIterator *iterator)
{
  if (!iterator)
    exitOnFatalErrorIllegalArgument("setCurrentToFirstElement");
  iterator->current = getLastFromDLList(iterator->list);
  return(getContentFromDLListNode(iterator->current));
}

//------------------------------------------------------------
void *gotoNextElement(DLListIterator *iterator)
{
  if (!iterator)
    exitOnFatalErrorIllegalArgument("gotoNextElement");
  iterator->current = getNextFromDLListNode(iterator->current);
  return(getContentFromDLListNode(iterator->current));
}

//------------------------------------------------------------
void *gotoPreviousElement(DLListIterator *iterator)
{
  if (!iterator)
    exitOnFatalErrorIllegalArgument("gotoPreviousElement");
  iterator->current = getPreviousFromDLListNode(iterator->current);
  return(getContentFromDLListNode(iterator->current));
}

//------------------------------------------------------------
void *getCurrentElement(DLListIterator *iterator)
{
  if (!iterator)
    exitOnFatalErrorIllegalArgument("gotoPreviousElement");
  return(getContentFromDLListNode(iterator->current));
}

