/*
 GNU Maverik - a system for managing display and interaction in 
               Virtual Environment applications.
 Copyright (C) 1999 Advanced Interfaces Group

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA


 The authors can be contacted via:
 www   - http://aig.cs.man.ac.uk
 email - maverik@aig.cs.man.ac.uk
 mail  - Advanced Interfaces Group, Room 2.94, Computer Science Building, 
         University of Manchester, Manchester, M13 9PL, UK
*/


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



/* Routine to get and return a new list segment */

MAV_listSegment *mavlib_listAlloc(void)
{
  MAV_listSegment *newsegment;

  newsegment= mav_malloc (sizeof (MAV_listSegment));

  return newsegment;
}



/* Routine to create a new list */

MAV_list *mav_listNew(void) 
{
  MAV_list *new_list= (MAV_list *) mav_malloc(sizeof(MAV_list));;

  /* get an empty segment */
  new_list->first= mavlib_listAlloc();

  /* set empty list values */
  new_list->length= 0;
  new_list->ordered= MAV_FALSE;
  new_list->last= new_list->first; 
  new_list->first->num_items= 0;
  new_list->first->next= NULL;
  new_list->first->previous= NULL;

  /* get and reset a list pointer */
  new_list->pointer= (MAV_listPointer *) mav_malloc(sizeof(MAV_listPointer));
  new_list->pointer->next= NULL;
  mav_listPointerReset(new_list);

  return new_list;
}



/* Routine to create a new ordered list */

MAV_list *mav_listOrderedNew (void)
{
  MAV_list *new_list;

  new_list= mav_listNew ();
  new_list->ordered= MAV_TRUE;

  return new_list;
}



/* Routine to reset a list pointer */

void mav_listPointerReset(MAV_list *the_list) 
{
  the_list->pointer->count= 0;
  the_list->pointer->the_segment= the_list->first;
  the_list->pointer->current= the_list->first->contents;
}



/* Routine to push a list pointer */

void mav_listPointerPush(MAV_list *l)
{
  MAV_listPointer *lp= (MAV_listPointer *) mav_malloc(sizeof(MAV_listPointer));

  *lp= *l->pointer;

  lp->next= l->pointer;
  l->pointer= lp;
}



/* Routine to pop a list pointer */

void mav_listPointerPop(MAV_list *l)
{
  if (l->pointer->next) 
  {
    MAV_listPointer *n= l->pointer->next;
    mav_free(l->pointer);
    l->pointer= n;
  }
  else
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: list pointer stack empty\n");
  }
}



/* Routine to fetch the next item in a list */

int mav_listItemNext (MAV_list *ml, void **next_elem) 
{
  MAV_listPointer *mlp= ml->pointer;

  if ((ml->length==0)||(mlp->count== -1)) return 0;
  else {
    *next_elem= *(mlp->current);
    mlp->count ++;
    mlp->current ++;
    if (mlp->count==mlp->the_segment->num_items) {
      if (mlp->the_segment->next != NULL) {
	mlp->the_segment= mlp->the_segment->next;
	mlp->current= mlp->the_segment->contents;
	mlp->count= 0;
      } else mlp->count= -1;
    }
  }
  
  return 1;
}



/* Routine to add a (void) item to a list */

void mav_listItemAdd(MAV_list *ml, void *mo) 
{
  MAV_listSegment *curr_segment= ml->first;
  MAV_listSegment *previous_segment;
  int count= 0;

  if (ml->length == 0) {
    /* empty list */
    ml->first->contents[0]= mo;
    ml->first->num_items= 1;
    ml->length= 1;
  } else {
    /* look for last segment */
    curr_segment= ml->last;

    /* get length of this segment */
    count= curr_segment->num_items;

    if (count < MAV_LIST_SIZE) {
      /* there is room in this segment */
      curr_segment->contents[count]= mo;
      curr_segment->num_items ++;
    } else {
      /* no room so grab another one */
      curr_segment->next= mavlib_listAlloc ();
      curr_segment= curr_segment->next;
      previous_segment= ml->last;
      ml->last= curr_segment;
      curr_segment->previous= previous_segment;
      curr_segment->next= NULL;
      /* add item */
      curr_segment->contents[0]= mo;
      curr_segment->num_items= 1;
    }
    
    /* update list length */
    ml->length ++;
  }
}



/* Routine to return size of a list */

int mav_listSize (MAV_list *ml) 
{
  if (ml != NULL)
    return ml->length;
  else
    return 0;
}



/* Routine to remove a (void) item from a list */

void mav_listItemRmv(MAV_list *ml, void *mo) 
{
  int count= 0;
  MAV_listSegment *curr_segment= ml->first;
  void **current= ml->first->contents;
  void **target= NULL;
  void *last_item;
  int not_found= 1;
  int not_finished= 1;

/* find last element and object */
  if (curr_segment->num_items==0) not_finished= 0;

  while ((not_finished) && (not_found)) {
    if (*current == mo) {
      /* we've located the item */
      target= current;
      not_found= 0;
    } else {
      /* step to next item */
      current ++;
      count ++;
      if (count==curr_segment->num_items) {
        if (curr_segment->next != NULL) {
  	  curr_segment= curr_segment->next;
  	  current= curr_segment->contents;
	  count= 0;
        } else not_finished= 0;
      }
    }
  }

  if (target != NULL) {
    if (ml->ordered) {
      /* shunt items in this segment back one place */
      while (count < (curr_segment->num_items - 1)) {
	*target= curr_segment->contents[count+1];
	target ++;
	count ++;
      }

      curr_segment->num_items --;

      if (curr_segment->num_items==0) {
	if (curr_segment != ml->first) {
	  /* empty seg so delete it */
	  curr_segment->previous->next= curr_segment->next;
	  if (curr_segment->next != NULL)
	    curr_segment->next->previous= curr_segment->previous;
	  else
	    ml->last= curr_segment->previous;

	  mav_free (curr_segment);
	} else {
	  /* first seg so only delete if there is another none-empty one */
	  if (curr_segment->next != NULL) {
	    ml->first= curr_segment->next;
	    curr_segment->next->previous= NULL;
	    mav_free (curr_segment);
	  }
	}
      }
    } else {
      /* find end of list */
      curr_segment= ml->last;
    
      /* curr_segment now points to last segment ... find last element */
      last_item= curr_segment->contents[curr_segment->num_items-1];

      /* delete object (overwrite with last object in list) */
      if (*target != last_item)
        *target= last_item;
      curr_segment->num_items --;
    
      if (curr_segment->num_items == 0) {
        /* segment now empty */
	if (curr_segment!=ml->first) {
          ml->last= curr_segment->previous;
          curr_segment->previous->next= NULL;
          mav_free (curr_segment);
	}
      }
    }

    /* update length */
    ml->length --;
  }
}



/* Routine to dump the contents of a list */

void mav_listPrint(char *s, MAV_list *ml) 
{
  void *current;
  
  printf("%s", s);
  printf ("Generic ");

  if (ml->ordered) printf("ordered ");
  else printf ("unordered ");

  printf ("list (%p) contains %i items: ",ml, ml->length);
  mav_listPointerReset (ml);
  while (mav_listItemNext (ml, &current)) {
    printf ("%p ",current);
  }
  printf ("\n");
}



/* Routine to empty a list */

void mav_listEmpty (MAV_list *ml) 
{
  /* empty a list */
  void **current= ml->first->contents;
  MAV_listSegment *curr_seg= ml->first;
  MAV_listSegment *last_seg= ml->first;
  int count= 0;
  int not_finished= 1;

  if (curr_seg->num_items==0) not_finished= 0;
  
  while (not_finished) {
    /* step to next item */
    current ++;
    count ++;
    
    /* check if at end of this segment */
    if (count==curr_seg->num_items) {
      if (curr_seg->next != NULL) {
	curr_seg= curr_seg->next;
	current= curr_seg->contents;
	/* dump previous segment */
	mav_free (last_seg);
	last_seg= curr_seg;
	count= 0;
      } else not_finished= 0;
    }
  }

  /* dump final segment */
  mav_free (last_seg);

  /* free list pointer if it has been set */
  if (ml->pointer != NULL)
    mav_free (ml->pointer);

  /* reset list head to empty values */
  ml->length= 0;
  ml->first= mavlib_listAlloc ();
  ml->last= ml->first;
  ml->first->num_items= 0;
  ml->first->next= NULL;
  ml->first->previous= NULL;
  ml->pointer= (MAV_listPointer *) mav_malloc(sizeof(MAV_listPointer));
  ml->pointer->next= NULL;
  mav_listPointerReset(ml);
}



/* Routine to delete a list */

void mav_listDelete(MAV_list *ml)
{
  /* remove objects from list */
  mav_listEmpty (ml);

  /* remove pointer */
  if (ml->pointer) mav_free (ml->pointer);

  /* now delete list head */
  mav_free (ml->first);
  mav_free (ml);
}



/* Routine to return if a list contains an item */

int mav_listItemContains (MAV_list *ml, void *mo) 
{
  int result= 0;
  int count= 0;
  void **current= ml->first->contents;
  MAV_listSegment *curr_segment= ml->first;
  int not_finished= 1;

  if (curr_segment->num_items==0) not_finished= 0;
  
  while (not_finished) {
    if (*current == mo) {
      result= 1; /* found it */
      not_finished= 0;
    } else {
      /* step to next item */
      current ++;
      count ++;
      if (count==curr_segment->num_items) {
        if (curr_segment->next != NULL) {
	  curr_segment= curr_segment->next;
	  current= curr_segment->contents;
        } else not_finished= 0;
        count= 0;
      }
    }
  }

  return result;
}
