/*------------------------------------------------------------------*/
/* list.c (Version 3: Internal Selected Node)                       */
/*------------------------------------------------------------------*/

#include "list.h"
#include <assert.h>
#include <stdlib.h>

struct Node
{
      void *pvItem;
      struct Node *psNextNode;
      struct Node *psPrevNode;
};

struct List
{
      unsigned long ulLength;
      struct Node *psMarkerNode;
      struct Node *psSelectedNode;
};

struct List *List_new()
{
   struct Node *psMarkerNode;
   struct List *psList;
   
   psMarkerNode = (struct Node*)malloc(sizeof(struct Node));
   assert(psMarkerNode != NULL);
   psMarkerNode->psNextNode = psMarkerNode;
   psMarkerNode->psPrevNode = psMarkerNode;
   psMarkerNode->pvItem = NULL;

   psList = (struct List*)malloc(sizeof(struct List));
   assert(psList != NULL);
   psList->ulLength = 0UL;
   psList->psMarkerNode = psMarkerNode;
   psList->psSelectedNode = psMarkerNode;

   return psList;
}

void List_free(struct List *psList)
{
   struct Node *psNode;
   struct Node *psNextNode;
   struct Node *psMarkerNode;

   assert(psList != NULL);

   psMarkerNode = psList->psMarkerNode;
   
   for (psNode = psMarkerNode->psNextNode;
        psNode != psMarkerNode;
        psNode = psNextNode)
   {
      psNextNode = psNode->psNextNode;
      free(psNode);
   }
   free(psMarkerNode);
   free(psList);
}

unsigned long List_length(struct List *psList)
{
   assert(psList != NULL);
   return psList->ulLength;
}

void *List_getFirst(struct List *psList)
{
   assert(psList != NULL);
   assert(psList->ulLength > 0UL);
   return psList->psMarkerNode->psNextNode->pvItem;
}

void *List_getLast(struct List *psList)
{
   assert(psList != NULL);
   assert(psList->ulLength > 0UL);
   return psList->psMarkerNode->psPrevNode->pvItem;
}

void List_addFirst(struct List *psList, void *pvItem)
{
   struct Node *psNewNode;
   struct Node *psMarkerNode;
   struct Node *psFirstNode;

   assert(psList != NULL);

   psMarkerNode = psList->psMarkerNode;
   psFirstNode = psMarkerNode->psNextNode;

   psNewNode = (struct Node*)malloc(sizeof(struct Node));
   assert(psNewNode != NULL);
   
   psNewNode->pvItem = pvItem;
   psNewNode->psPrevNode = psMarkerNode;
   psNewNode->psNextNode = psFirstNode;
   
   psFirstNode->psPrevNode = psNewNode;
   psMarkerNode->psNextNode = psNewNode;
   
   ++psList->ulLength;
}

void List_addLast(struct List *psList, void *pvItem)
{
   struct Node *psNewNode;
   struct Node *psMarkerNode;
   struct Node *psLastNode;

   assert(psList != NULL);
   
   psMarkerNode = psList->psMarkerNode;
   psLastNode = psMarkerNode->psPrevNode;

   psNewNode = (struct Node*)malloc(sizeof(struct Node));
   assert(psNewNode != NULL);

   psNewNode->pvItem = pvItem;
   psNewNode->psNextNode = psMarkerNode;
   psNewNode->psPrevNode = psMarkerNode->psPrevNode;

   psLastNode->psNextNode = psNewNode;
   psMarkerNode->psPrevNode = psNewNode;

   ++psList->ulLength;
}

void List_removeFirst(struct List *psList)
{
   struct Node *psFirstNode;
   struct Node *psMarkerNode;

   assert(psList != NULL);
   assert(psList->ulLength > 0UL);

   psMarkerNode = psList->psMarkerNode;
   psFirstNode = psMarkerNode->psNextNode;
   
   psMarkerNode->psNextNode = psFirstNode->psNextNode;
   psFirstNode->psNextNode->psPrevNode = psMarkerNode;
   
   free(psFirstNode);
   --psList->ulLength;
}

void List_removeLast(struct List *psList)
{
   struct Node *psLastNode;
   struct Node *psMarkerNode;

   assert(psList != NULL);
   assert(psList->ulLength > 0UL);

   psMarkerNode = psList->psMarkerNode;
   psLastNode = psMarkerNode->psPrevNode;
   
   psMarkerNode->psPrevNode = psLastNode->psPrevNode;
   psLastNode->psPrevNode->psNextNode = psMarkerNode;
   
   free(psLastNode);
   --psList->ulLength;
}

void List_toArray(struct List *psList, void **ppvArray)
{
   struct Node *psNode;
   struct Node *psMarkerNode;
   unsigned long ul = 0UL;

   assert(psList != NULL);
   assert(ppvArray != NULL);

   psMarkerNode = psList->psMarkerNode;
   
   for (psNode = psMarkerNode->psNextNode;
        psNode != psMarkerNode;
        psNode = psNode->psNextNode)
   {
      ppvArray[ul] = psNode->pvItem;
      ++ul;
   }
}

void List_map(struct List *psList, 
              void (*pfApply)(void **ppvItem, void *pvExtra),
              void *pvExtra)
{
   struct Node *psNode;
   struct Node *psMarkerNode;

   assert(psList != NULL);
   assert(pfApply != NULL);
   
   psMarkerNode = psList->psMarkerNode;
   
   for (psNode = psMarkerNode->psNextNode;
        psNode != psMarkerNode;
        psNode = psNode->psNextNode)
      (*pfApply)(&(psNode->pvItem), pvExtra);
}

void List_selectFirst(struct List *psList)
{
   assert(psList != NULL);
   psList->psSelectedNode = psList->psMarkerNode->psNextNode;
}

void List_selectLast(struct List *psList)
{
   assert(psList != NULL);
   psList->psSelectedNode = psList->psMarkerNode->psPrevNode;
}

void List_selectNext(struct List *psList)
{
   assert(psList != NULL);
   assert(psList->psSelectedNode != psList->psMarkerNode);
   psList->psSelectedNode = psList->psSelectedNode->psNextNode;
}

void List_selectPrev(struct List *psList)
{
   assert(psList != NULL);
   assert(psList->psSelectedNode != psList->psMarkerNode);
   psList->psSelectedNode = psList->psSelectedNode->psPrevNode;
}

int List_selectionValid(struct List *psList)
{
   assert(psList != NULL);
   return psList->psSelectedNode != psList->psMarkerNode;
}

void *List_selectedItem(struct List *psList)
{
   assert(psList != NULL);
   assert(psList->psSelectedNode != psList->psMarkerNode);
   return psList->psSelectedNode->pvItem;
}