////////////////////////////////////////////////////////////////////////////
//Tom Valesky
//CS-752
//Dr. Chen, instructor
//LIST.CPP - Implementation for list class (recycled from previous assignment)
///////////////////////////////////////////////////////////////////////////

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

////////////////////////////////////////////////////////////////////////////
//List::set_head() - Sets current to head of list.
////////////////////////////////////////////////////////////////////////////
int List::set_head(void)
{
   if (current == NULL)
      {
      printf( "List::set_head -- current == NULL\n");
      return FALSE;
      }
   current = head;
   //printf("after set_head(); current = %p; head = %p; tail = %p\n", current, head, tail);
   return TRUE;
}
////////////////////////////////////////////////////////////////////////////
//List::set_tail() - sets current to end of list.
////////////////////////////////////////////////////////////////////////////
int List::set_tail(void)
{
   if (current == NULL)
      {
      printf( "List::set_tail -- current == NULL\n");
      return FALSE;
      }
   current = tail;
   return TRUE;
}
////////////////////////////////////////////////////////////////////////////
//List::set_next() - sets current to next node in list.
////////////////////////////////////////////////////////////////////////////
int List::set_next(void)
{
   if (current == NULL)
      {
      printf( "List::set_next -- current == NULL\n");
      return FALSE;
      }

   if (current->next != 0)
     current = current->next;
   else
      {
      //printf( "set_next error: tried to read past end of list\n");
	  //printf("current = %p; head = %p; tail = %p\n", current, head, tail);
	  //printf("current->next = %p; head->next = %p; tail->next = %p\n", current->next, head->next, tail->next);
      return FALSE;
      }
   return TRUE;
}
////////////////////////////////////////////////////////////////////////////
//List::set_prev() - sets current to previous node in list.
////////////////////////////////////////////////////////////////////////////
int List::set_prev(void)
{
   if (current == NULL)
      {
      printf( "List::set_prev -- current == NULL\n");
      return FALSE;
      }
   if (current->prev != 0)
      current = current->prev;
   else
      {
      printf( "set_prev error: tried to read past head of list\n");
      return FALSE;
      }
   return TRUE;
}
////////////////////////////////////////////////////////////////////////////
//List::at_head() - are we at the head of the list?
////////////////////////////////////////////////////////////////////////////
int List::at_head(void)
{
   return(current == head);
}
////////////////////////////////////////////////////////////////////////////
//List::at_tail() - are we at the end of the list?
////////////////////////////////////////////////////////////////////////////
int List::at_tail(void)
{
   return(current == tail);
}
////////////////////////////////////////////////////////////////////////////
//List::is_empty() - is the list empty?
////////////////////////////////////////////////////////////////////////////
int List::is_empty(void)
{
   return(number_of_elements == 0);
}
////////////////////////////////////////////////////////////////////////////
//List::size() - how big is the list?
////////////////////////////////////////////////////////////////////////////
int List::size(void)
{
   return(number_of_elements);
}
////////////////////////////////////////////////////////////////////////////
//List::display_node() - displays internal data for current node
////////////////////////////////////////////////////////////////////////////
void List::display_node(void)
{
   printf( "--------------------\n");
   printf("current = %p;", current);
   current->print();
   printf( "--------------------\n");
}
////////////////////////////////////////////////////////////////////////////
//List::display_list() - calls display_node() for each node in list.
////////////////////////////////////////////////////////////////////////////
void List::display_list(void)
{
   if (!is_empty())
      {
      assert(set_head());                         //display all but the last node
      while (!at_tail())
         {
         display_node();
//         assert(set_next());
         if (!set_next())
                {
                printf("List::display_list(); couldn't set next\n");
                current->print();
                exit(0);
                }
         }
      display_node();                     //display last node
      }
   else
      {
      printf( "List is empty\n");
      }
}



////////////////////////////////////////////////////////////////////////////
//List::list() - constructor; initializes list
////////////////////////////////////////////////////////////////////////////
List::List()
{
   number_of_elements = 0;
   head = tail = current = NULL;
}

////////////////////////////////////////////////////////////////////////////
//List::~list() - destructor; frees all nodes
////////////////////////////////////////////////////////////////////////////
List::~List()
{
        printf("called list destructor\n");
   delete_list();
}

////////////////////////////////////////////////////////////////////////////
//List::insert_node() - Inserts node 'n' into the list
////////////////////////////////////////////////////////////////////////////
void List::insert_node(Node *n)
{
   number_of_elements++;
   if (current == NULL)                //case of empty list
      {
      head = tail = current = n;
      return;
      }
                                       //case of one-node list
   if ((current == head) && (current == tail))
      {
      n->prev = current;
      n->next = current->next;
      if (current->next != NULL)
         current->next->prev = n;
      current->next = n;
      tail = current = n;
      return;
      }
   if (current == head)                //first node in list
      {
      n->prev = current;
      n->next = current->next;
      if (current->next != NULL)
         current->next->prev = n;
      current->next = n;
      current = n;
      return;
      }

   if (current == tail)                //last node in list
      {
      n->prev = current;
      n->next = NULL;
      current->next = n;
      tail = current = n;
      return;
      }

   n->prev = current;                  //else node is an interior node
   n->next = current->next;
   current->next->prev = n;
   current->next = n;
   current = n;
   return;
}

////////////////////////////////////////////////////////////////////////////
//List::delete_node() - deletes the current node
////////////////////////////////////////////////////////////////////////////
void List::delete_node(void)
{
   if (!is_empty())
      {
      number_of_elements--;
      if (current == NULL)
         {
         printf( "List::delete error! Trying to delete a NULL node\n");
         printf("number_of_elements = %d\n", number_of_elements);
//         printf("printing head and tail\n");
//         head->print();
//         tail->print();
         return;
         }
                                       //if at head,
      if (current == head)             //set following node to be the head
         {                             //and delete the current node
         head = current->next;
      if (current->data != NULL)
        delete (current->data);

         delete(current);
         current = head;
         return;
         }
                                       //if at tail,
      if (current == tail)             //set prev node to be the tail
         {                             //and delete the current node
         tail = current->prev;
        if (current->data != NULL)
                delete (current->data);
         delete(current);
         current = tail;
         return;
         }

      Node *temp = current->next;      //normal interior node
      current->prev->next = current->next;
      current->next->prev = current->prev;
      if (current->data != NULL)
        delete (current->data);
      delete (current);
      current = temp;

      }
   else
      {
      printf( "List::delete_node error -- Trying to delete from empty list\n");
      return;
      }

}
////////////////////////////////////////////////////////////////////////////
//List::detach_node_from_list() - Removes current node from list, but leaves
//                                the data in existence.
////////////////////////////////////////////////////////////////////////////
Node * List::detach_node_from_list(void)
{
   Node *old_node = current;
   Node *temp = NULL;

   if (!is_empty())
      {
      number_of_elements--;
      if (current == NULL)             //empty list or pointer error
         {
         printf( "List::detach_node_from_list error! Trying to delete");
         printf( "a NULL node\n");
         return old_node;
         }
                                       //if at head,
      if (current == head)             //set following node to be the head
         {                             //and delete the current node
         head = current->next;
         current = head;
		 return old_node;
         }

	  if ((current->next == NULL) && (current != tail))
	  {

		  print("list is messed up");
		  exit(0);
	  }
                                       //if at tail
      if (current == tail)             //set prev node to be the tail
         {                             //and delete the current node
         tail = current->prev;
         current = tail;
		 tail->next = NULL;
         return old_node;
         }

	      temp = current->next;      //normal interior node
      current->prev->next = current->next;
      current->next->prev = current->prev;
      current = temp;

      }
   else
      {                                //no nodes left in list
      return NULL;
      }

   return old_node;
}


////////////////////////////////////////////////////////////////////////////
//List::insert_node_at_head() - Inserts 'n' as the new head of the list.
////////////////////////////////////////////////////////////////////////////
void List::insert_node_at_head(Node *n)
{

   number_of_elements++;
                                       //if list is empty
   if (head == NULL)
   {
      current = head = tail = n;
      return;
   }
   assert(set_head());

   if (current == head)
      {
      n->prev = NULL;                  //put new node into list
      n->next = current;
      current->prev = n;

      if (head == NULL)                //set head,tail,and current
         tail = n;
      head = n;
      current = n;
      return;
      }

   printf( "List::insert_node_at_head error -- couldn't set head");
}
////////////////////////////////////////////////////////////////////////////
//List::delete_list() - deletes all nodes in the list
////////////////////////////////////////////////////////////////////////////
void List::delete_list(void)
{
   while (!is_empty())
        {
        delete_node();
        }
}

Node *List::add(Node *n)
{
		n->prev = n->next = 0;
        insert_node(n);
        return n;
}

Node *List::del(void)
{
        Node *retval;
        retval = detach_node_from_list();
        retval->prev=retval->next = 0;
        //delete_node();
        return retval;
}

void List::print(char *comment)
{
        printf("%s\n", comment);
        printf("number of elements in list = %d\n", number_of_elements);
		printf("head=%p;tail=%p;current=%p\n", head, tail, current);
        display_list();
        fflush(stdout);

}
