/*
 *                **** LINKED LIST ****
 *
 * This file implements a generic linked list
 *
 */

#include <stdio.h>

#include "linklist.h"

#include "linklist.pro"

LinkList *
create_link_list(void)
{
  LinkList *list;

  if ((list = (LinkList *)malloc(sizeof(LinkList))) != NULL)
    { /* list created - initialize it */
      initialize_link_list(list);
    }

  return list;
}

void
initialize_link_list(LinkList *list)
{
  list->head = list->tail = list->current = NULL;
}

Link *
create_link_entry(void *data)
{ /* create a link entry pointing to data */
  Link *link;

  if ((link = (Link *)malloc(sizeof(Link))) != NULL)
    { /* got memory for link - fill it in */
      link->next = link->prev = NULL; /* not connected yet */
      link->data = data;
    }
  return link;
}

int
add_link_to_head(LinkList *list, Link *entry)
{ /* add entry to head of linked list - zero is normal return */
  if (entry == NULL || list == NULL) return 1;

  entry->next = list->head;	/* point to current head of list */
  entry->prev = NULL;		/* nothing in front of head */
  if (list->head != NULL) list->head->prev = entry; /* bwd link */
  list->head = entry;		/* make new head of list */
  if (list->tail == NULL) list->tail = entry; /* was empty - now tail */

  return 0;
}

int
add_link_to_tail(LinkList *list, Link *entry)
{ /* add entry to tail of linked list - zero is normal return */
  if (entry == NULL || list == NULL) return 1;

  entry->prev = list->tail;	/* point to current head of list */
  entry->next = NULL;		/* nothing in front of head */
  if (list->tail != NULL) list->tail->next = entry; /* fwd link */
  list->tail = entry;		/* make new tail of list */
  if (list->head == NULL) list->head = entry; /* was empty - now head */

  return 0;
}

void
process_link_list(LinkList *list,
		  void (*process_function)(LinkList *list, Link *ent))
{ /* loop thru list doing something */
  Link *link;

  for (link = list->head; link != NULL; link = link->next)
    {
      (process_function)(list, link);
    }
}

void
free_link(Link *link)
{
  free(link);
}

int
remove_link(LinkList *list, Link *link)
{ /* remove link from list */
  if (list == NULL || link == NULL) return 1; /* bad input */

  /* check for entry being the head and/or the tail */
  if (list->head == link) list->head = link->next; /* was the head */
  if (list->tail == link) list->tail = link->prev; /* was the tail */

  /* now remove link from the chain */
  if (link->prev != NULL) link->prev->next = link->next;
  if (link->next != NULL) link->next->prev = link->prev;

  /* see if we are deleting the current entry */
  if (list->current == link) list->current = NULL; /* no current */
  return 0;
}

void *
delete_link(LinkList *list, Link *link)
{ /* remove link from list and delete it */
  void *data;			/* return value */

  if (list == NULL || link == NULL) return NULL; /* bad input */
  data = link->data;		/* value to return */
  /* now delete the link itself (but not the data) */
  if (remove_link(list, link) == 0) free_link(link);
  return data;
}

Link *
find_link_entry(LinkList *list, void *key,
		int (*find_function)(Link *link, void *key))
{ /* find entry in link list matching key, using find_function */
  Link *link;

  /* loop thru linked list until match or end of list */
  for (link = list->head;
       link != NULL && (find_function)(link, key); link = link->next);

  return link; }

Link *
find_next_link_entry(LinkList *list, Link *link, void *key,
		int (*find_function)(Link *link, void *key))
{ /* find entry after link in link list matching key, using find_function */

  /* loop thru linked list until match or end of list */
  while (link != NULL && (find_function)(link, key)) link = link->next;

  return link;
}

int
move_link_up(LinkList *list, Link *link)
{ /* move link towards top (or front) of list */
  Link *prev;			/* entry to add in front of */

  if (list == NULL || link == NULL) return 1; /* bad return */
  if (link == list->head) return 0; /* already at head */

  prev = link->prev;		/* move before this entry */
  if (prev == list->head) list->head = link; /* new head */

  remove_link(list, link);	/* delete link */
  return add_link_before_link(list, link, prev);  /* add in front of previous link */
}

int
move_link_down(LinkList *list, Link *link)
{ /* move link towards bottom (or back) of list */
  Link *next;			/* entry to add after */

  if (list == NULL || link == NULL) return 1; /* bad return */
  if (link == list->tail) return 0; /* already at bottom */

  next = link->next;		/* move after this entry */
  if (next == list->tail) list->tail = link; /* new tail */

  remove_link(list, link);	/* delete link */
  return add_link_after_link(list, link, next);  /* add after link */
}

int
add_link_before_link(LinkList *list, Link *new, Link *exist)
{ /* add new link before existing link */
  if (list == NULL || new == NULL || exist == NULL) return 1;

  /* valid arguments - add before link, updating head if needed */
  new->next = exist;
  new->prev = exist->prev;
  exist->prev = new;
  if (new->prev == NULL) list->head = new; /* new head */
  else new->prev->next = new;

  return 0;
}

int
add_link_after_link(LinkList *list, Link *new, Link *exist)
{ /* add new link after existing link */
  if (list == NULL || new == NULL || exist == NULL) return 1;

  /* valid arguments - add after link, updating tail if needed */
  new->prev = exist;
  new->next = exist->next;
  exist->next = new;
  if (new->next == NULL) list->tail = new; /* new tail */
  else new->next->prev = new;

  return 0;
}

int
set_current_link(LinkList *list, Link *link)
{ /* make given link the current entry in the list */
  if (list == NULL) return 0;

  list->current = link;		/* link can be NULL */
  return 1;
}

int
set_head_current_link(LinkList *list)
{ /* make the head the current entry */
  if (list == NULL) return 0;	/* bad list */
  list->current = list->head;
  return 1;
}

int
set_tail_current_link(LinkList *list)
{ /* make the head the current entry */
  if (list == NULL) return 0;	/* bad list */
  list->current = list->tail;
  return 1;
}

int
add_link_before_current(LinkList *list, Link *link)
{ /* add this link before the current entry */
  return add_link_before_link(list, link, list->current);
}

int
add_link_after_current(LinkList *list, Link *link)
{ /* add this link after the current entry in the list */
  return add_link_after_link(list, link, list->current);
}

int
move_current_link_up(LinkList *list)
{ /* move the current link up in list, leaving current pointer alone */
  int ret;
  Link *link;

  if (list == NULL) return 1;
  link = list->current;		/* remember current */
  ret = move_link_up(list, list->current); /* this deletes current */
  list->current = link;		/* reset current */

  return ret;
}

int
move_current_link_down(LinkList *list)
{ /* move the current link down in list, leaving current pointer alone */
  int ret;
  Link *link;

  if (list == NULL) return 1;
  link = list->current;		/* remember current link */
  ret = move_link_down(list, list->current); /* this will delete current */
  list->current = link;		/* restore current link */

  return ret;
}

int
set_current_link_next(LinkList *list)
{ /* move the current pointer to next entry, leaving links alone */
  if (list == NULL || list->current == NULL) return 1;

  if (list->current->next != NULL) list->current = list->current->next;
  return 0;
}

int
cycle_current_link_next(LinkList *list)
{
  if (list == NULL) return 1;	/* bad arg */
  if (list_is_empty(list)) return 2; /* fail on empty list */
  if (list_is_single_link(list)) return 0; /* nothing to do */

  /* if current link not set, make it the head */
  if (list->current == NULL) return set_head_current_link(list);

  /* point to next link.  If at tail, then point to head */
  if (current_is_tail(list)) return set_head_current_link(list);
  return set_current_link_next(list);
}

int
set_current_link_prev(LinkList *list)
{ /* move the current pointer to next entry, leaving links alone */
  if (list == NULL || list->current == NULL) return 1;

  if (list->current->prev != NULL) list->current = list->current->prev;
  return 0;
}

int
cycle_current_link_prev(LinkList *list)
{
  if (list == NULL) return 1;	/* bad arg */
  if (list_is_empty(list)) return 0; /* fail on empty list */
  if (list_is_single_link(list)) return 1; /* nothing to do */

  /* if current link not set, make it the tail */
  if (list->current == NULL) return set_tail_current_link(list);

  /* point to previous link; if at head, then point to tail */
  if (current_is_head(list)) return set_tail_current_link(list);
  return set_current_link_prev(list);
}

int
list_is_empty(LinkList *list)
{
  if (list == NULL || list->head == NULL) return 1; /* nothing there */
  return 0;
}

int
list_is_single_link(LinkList *list)
{
  if (list_is_empty(list)) return 0;
  if (list->head == list->tail) return 1;
  return 0;
}

int
link_is_current(LinkList *list, Link *link)
{
  if (list == NULL) return 1;	/* bad list */
  return (link == list->current);
}

int
link_is_head(LinkList *list, Link *link)
{
  if (list == NULL) return 1;	/* bad list */
  return (link == list->head);
}

int
link_is_tail(LinkList *list, Link *link)
{
  if (list == NULL) return 1;	/* bad list */
  return (link == list->tail);
}

int
current_is_head(LinkList *list)
{
  if (list == NULL) return 0;	/* bad list */
  return (list->current == list->head);
}

int
head_is_current(LinkList *list)
{
  return current_is_head(list);
}

int 
current_is_tail(LinkList *list)
{
  if (list == NULL) return 0;	/* bad list */
  return (list->current == list->tail);
}

int
tail_is_current(LinkList *list)
{
  return current_is_tail(list);
}

int
add_new_current_link_before(LinkList *list, Link *link)
{ /* add a new entry before the current & make it the new current */
  int ret;

  if (list->current == NULL)
    { /* no current - add this to head and make it current */
      if ((ret = add_link_to_head(list, link))) return ret;
    }
  else
    { /* current exists - just add it before */
      if (add_link_before_current(list, link)) return 1; /* bad args */
    }
 return set_current_link(list, link);
}

int
add_new_current_link_after(LinkList *list, Link *link)
{ /* add a new entry after the current & make it the new current */
  int ret;

  if (list->current == NULL)
    { /* no current - add to tail and make it current */
      if ((ret = add_link_to_tail(list, link))) return ret;
    }
  else
    { /* current exists - just add it after */
      if (add_link_after_current(list, link)) return 1; /* bad args */
    }
  return set_current_link(list, link);
}

void *
get_data_from_link(Link *link)
{
  if (link == NULL) return NULL;
  return link->data;
}

void *
get_data_from_current(LinkList *list)
{
  if (list == NULL || list->current == NULL) return NULL;
  return list->current->data;
}

void *
get_data_from_head(LinkList *list)
{
  if (list == NULL || list->head == NULL) return NULL;
  return list->head->data;
}

void *
get_data_from_tail(LinkList *list)
{
  if (list == NULL || list->tail == NULL) return NULL;
  return list->tail->data;
}

Link *
get_head_link(LinkList *list)
{
  if (list == NULL) return NULL;
  return list->head;
}

Link *
get_tail_link(LinkList *list)
{
  if (list == NULL) return NULL;
  return list->tail;
}

Link *
get_current_link(LinkList *list)
{
  if (list == NULL) return NULL;
  return list->current;
}

Link *
get_next_link(Link *link)
{
  if (link == NULL) return NULL;
  return link->next;
}

Link *
get_prev_link(Link *link)
{
  if (link == NULL) return NULL;
  return link->prev;
}
