#include "mlist.h"
#include "mdebug.h"
#include <stdlib.h>

static list_it*
list_it_allocate(void* item, list_it* prev_it, list_it* next_it)
{
   list_it* self = (list_it*)MALLOC(sizeof(list_it));
      
   self->mItem = item;
   self->mPrevIt = prev_it;
   self->mNextIt = next_it;
   
   return self;
}

static inline list_it* list_it_release(list_it* self)
{
   FREE(self);
}

list_obj* list_new()
{
   list_obj* self = (list_obj*)MALLOC(sizeof(list_obj));
   
   self->mEntryIt = NULL;
   self->mLastIt = NULL;
   self->mCount = 0;
   
   return self;
}
      
void list_delete(list_obj* self)
{
   list_it* it = self->mEntryIt;
   while(it) {
      list_it* next_it = it->mNextIt;
      list_it_release(it);
      it = next_it;
   }

   FREE(self);
}

int list_size(list_obj* self)
{
    return self->mCount;
}

BOOL list_empty(list_obj* self)
{
    return self->mEntryIt == NULL;
}

int list_index_of(list_obj* self, void* item)
{
   int i = 0;
   list_it* it = self->mEntryIt;
   
   while(it) {
      if(it->mItem == item) return i;
      it = it->mNextIt;
      i++;
   }
   
   return -1;
}

void list_push_front(list_obj* self, void* item)
{
   list_it* new_it = list_it_allocate(item, NULL, self->mEntryIt);
   if(self->mEntryIt) self->mEntryIt->mPrevIt = new_it;
   if(self->mEntryIt == NULL) self->mLastIt = new_it;
   self->mEntryIt = new_it;
   
   self->mCount++;
}
         
void* list_pop_front(list_obj* self)
{
   void* result;
   list_it* new_entry;

   if(self->mEntryIt == NULL) return NULL;

   if(self->mLastIt == self->mEntryIt) self->mLastIt = NULL;
   
   result = self->mEntryIt->mItem;
   
   new_entry = self->mEntryIt->mNextIt;
   if(new_entry) new_entry->mPrevIt = NULL;
   
   list_it_release(self->mEntryIt);
   self->mEntryIt = new_entry;
   
   self->mCount--;
   
   return result;
}

void list_push_back(list_obj* self, void* item)
{
    list_it* new_it = list_it_allocate(item, self->mLastIt, NULL);
    if(self->mLastIt) self->mLastIt->mNextIt = new_it;
    if(self->mLastIt == NULL) self->mEntryIt = new_it;
    self->mLastIt = new_it;

    self->mCount++;
}
         
void* list_pop_back(list_obj* self)
{
    void* result;
    list_it* new_last;

    if(self->mLastIt == NULL) return NULL;

    if(self->mEntryIt == self->mLastIt) self->mEntryIt = NULL;

    result = self->mLastIt->mItem;

    new_last = self->mLastIt->mPrevIt;
    if(new_last) new_last->mNextIt = NULL;

    list_it_release(self->mLastIt);
    self->mLastIt = new_last;

    self->mCount--;

    return result;
}

void list_clear(list_obj* self)
{
   list_it* it = self->mEntryIt;

   while(it) {
      list_it* next_it = it->mNextIt;
      list_it_release(it);
      it = next_it;
      }
   
   self->mEntryIt = NULL;
   self->mLastIt = NULL;
   self->mCount = 0;
}



list_it* list_begin(list_obj* self)
{
    return self->mEntryIt;
}

list_it* list_last(list_obj* self)
{
    return self->mLastIt;
}
      
list_it* list_at(list_obj* self, int index)
{
   int i = 0;
   list_it* it = self->mEntryIt;
   while(it) {
      if(i == index) return it;
         
      it = it->mNextIt;
      i++;
   }
   
   return NULL;
}
   
list_it* list_find(list_obj* self, void* item)
{
   list_it* it = self->mEntryIt;
   while(it) {
      if(it->mItem == item) return it;
      it = it->mNextIt;
   }
   return NULL;
}

void* list_item(list_it* self)
{
    return self->mItem;
}

list_it* list_next(list_it* self)
{
    return self->mNextIt;
}

list_it* list_prev(list_it* self)
{
    return self->mPrevIt;
}

void list_replace(list_it* self, void* item)
{
    self->mItem = item;
}
   
void list_erase(list_it* self, list_obj* owner)
{
   if(self->mPrevIt) self->mPrevIt->mNextIt = self->mNextIt;
   if(self->mNextIt) self->mNextIt->mPrevIt = self->mPrevIt;
   
   if(owner->mEntryIt == self) owner->mEntryIt = self->mNextIt;
   if(owner->mLastIt == self) owner->mLastIt = self->mPrevIt;
   owner->mCount--;

   list_it_release(self);
}
      
void list_insert_front(list_it* self, void* item, list_obj* owner)
{
   list_it* new_it = list_it_allocate(item, self->mPrevIt, self);
   if(self->mPrevIt) self->mPrevIt->mNextIt = new_it;
   self->mPrevIt = new_it;

   if(self == owner->mEntryIt) owner->mEntryIt = new_it;
   owner->mCount++;
}

void list_insert_back(list_it* self, void* item, list_obj* owner)
{
   list_it* new_it = list_it_allocate(item, self, self->mNextIt);
   if(self->mNextIt) self->mNextIt->mPrevIt = new_it;
   self->mNextIt = new_it;

   if(self == owner->mLastIt) owner->mLastIt = new_it;      
   owner->mCount++;
}
