//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#ifndef LLIST_H
#define LLIST_H

//***************************************************************************************
//   Linked List
//***************************************************************************************
// This template has an overhead of size LListNode for each entry in the linked list -
//    be aware of this for very large linked lists.
//
// This template has no destructor!  You must explicitly call free() if you want the
//    contents of the list to be destroyed.
//
//
// WARNING - this template has not been thoroughly tested so there may be bugs in it!
//
// -Bramage
//---------------------------------------------------------------------------------------


//---------------------------------------------------------------------------------------
// LListNode template data node
//---------------------------------------------------------------------------------------
template <class T> class LListNode
{
public:
   LListNode<T> *   Next;
   LListNode<T> *   Prev;
   T *            Data;

   LListNode()
   {
      Next = NULL;
      Prev = NULL;
      Data = NULL;
   }

};


//---------------------------------------------------------------------------------------
// LList template
//---------------------------------------------------------------------------------------
template <class T> class LList
{
protected:
   LListNode< T > *first_entry;
   LListNode< T > *last_entry;
   int cnt;

public:

   //---------------------------------------------------------------------------------------
   // Constructor initializes empty list
   //---------------------------------------------------------------------------------------
   LList()
   {
      reset();
   }

   //---------------------------------------------------------------------------------------
   // Reset list to empty state by abandoning contents
   //---------------------------------------------------------------------------------------
   void reset(void)
   {
      first_entry = NULL;
      last_entry  = NULL;
      cnt         = 0;
   }

   //---------------------------------------------------------------------------------------
   // Return entry count
   //---------------------------------------------------------------------------------------
   int size(void) const
   {
      return cnt;
   }

   //---------------------------------------------------------------------------------------
   // Return first list entry (NULL if list empty)
   //---------------------------------------------------------------------------------------
   T *first(void) const
   {
      if( first_entry ){
         return first_entry->Data;
      }
      else
      {
         return NULL;
      }
   }

   //---------------------------------------------------------------------------------------
   // Return last list entry (NULL if list empty)
   //---------------------------------------------------------------------------------------
   T *last(void) const
   {
      if( last_entry )
      {
         return last_entry->Data;
      }
      else
      {
         return NULL;
      }
   }

   //---------------------------------------------------------------------------------------
   // Returns next entry - returns first entry if current == NULL
   //---------------------------------------------------------------------------------------
   T *next( T* current )
   {
      if( current == NULL )
      {
         return first();
      }

      LListNode<T> *next = findNode( current )->Next;
      if( next )
      {
         return next->Data;
      }
      else
      {
         return NULL;
      }
   }

   //---------------------------------------------------------------------------------------
   // Returns prev entry - returns last entry if current == NULL
   //---------------------------------------------------------------------------------------
   T *prev( T *current )
   {
      if( current == NULL )
      {
         return last();
      }

      LListNode<T> *prev = findNode( current )->Prev;
      if( prev )
      {
         return prev->Data;
      }
      else
      {
         return NULL;
      }
   }

   //---------------------------------------------------------------------------------------
   // Link new item into list before specified entry
   // If specified entry==NULL, insert at end of list
   //---------------------------------------------------------------------------------------
   T *link(T *entry, T *next = NULL)
   {
      LListNode<T> *prevNode = NULL;
      LListNode<T> *nextNode = findNode( next );
      LListNode<T> *newNode = new LListNode<T>;

      newNode->Data = entry;

      if( nextNode == NULL)
      {
         prevNode = last_entry;
         last_entry = newNode;
      }
      else
      {
         prevNode = nextNode->Prev;
         nextNode->Prev = newNode;
      }

      if( prevNode == NULL )
      {
         first_entry = newNode;
      }
      else
      {
         prevNode->Next = newNode;
      }

      newNode->Next = nextNode;
      newNode->Prev = prevNode;

      ++cnt;

      return entry;
   }

   //---------------------------------------------------------------------------------------
   // Link new item into list before specified entry
   // If specified entry==NULL, insert at end of list
   //---------------------------------------------------------------------------------------
   T *link(T &entry, T *next = NULL)
   {
      T *newEntry = new T;
      *newEntry = entry;

      return link( newEntry, next );
   }

   //---------------------------------------------------------------------------------------
   // Unlink item from list (without destroying it)
   //---------------------------------------------------------------------------------------
   void unlink(T *entry)
   {
      LListNode<T> *entryNode = findNode( entry );
      if( !entryNode ) return;


      if( entryNode->Prev == NULL )
      {
         first_entry = entryNode->Next;
      }
      else
      {
         entryNode->Prev->Next = entryNode->Next;
      }

      if( entryNode->Next == NULL )
      {
         last_entry = entryNode->Prev;
      }
      else
      {
         entryNode->Next->Prev = entryNode->Prev;
      }

      delete entryNode;

      --cnt;
   }

   //---------------------------------------------------------------------------------------
   // Allocate entry and insert before specified entry
   // If specified entry==NULL, insert at end of list
   //---------------------------------------------------------------------------------------
   T * alloc( T *next = NULL )
   {
      T *entry = new T;

      if( entry == NULL )
      {
         return NULL;
      }

      return link( entry, next );
   }

   //---------------------------------------------------------------------------------------
   // Unlink item from list and destroy it
   //---------------------------------------------------------------------------------------
   void free(T *entry)
   {
      unlink(entry);
      delete entry;
   }

   //---------------------------------------------------------------------------------------
   // Unlink and destroy all list items
   //---------------------------------------------------------------------------------------
   void free(void)
   {
      LListNode<T> *node = NULL;

      while( (node = iterate( node ) )!=NULL )
      {
         LListNode<T> *nodeToKill = node;
         node = node->Prev;
         free( nodeToKill->Data );
      }
   }

   //---------------------------------------------------------------------------------------
   // Find node
   //---------------------------------------------------------------------------------------
   LListNode<T> *findNode( T *entry )
   {
      LListNode<T> *it = NULL;
      while( (it = iterate( it ) )!=NULL )
      {
         if( it->Data == entry )
         {
            return it;
         }
      }
      return NULL;
   }

   //---------------------------------------------------------------------------------------
   // Returns the next node in list
   //---------------------------------------------------------------------------------------
   LListNode<T> *iterate( LListNode<T> *entry = NULL )
   {
      if( entry == NULL ) return first_entry;
      return entry->Next;
   }

};


#endif