#ifndef _LIST_H
#define _LIST_H

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

template<class T>
class ListEl
{
public:
  ListEl(T el, ListEl *Next)
    { elem = el; next = Next; }
  ~ListEl() {}

  T elem;
  ListEl<T> *next;
};

template<class T>
class Lister;

template<class T>
class List
{
public:
  List() { head = NULL; } // makes a new empty list
  ~List() {}

  void Insert(T el)
    { ListEl<T> *elem = new ListEl<T>(el, head); head = elem; }

  // searches for el in the list and deletes it
  // fails if it didn't find it
  int Delete(T el)
    { ListEl<T> **prev = &head; ListEl<T> *ptr;
      while ((ptr = *prev) != NULL && ptr->elem != el)
	prev = &(ptr->next);
      if (ptr != NULL) {
	*prev = ptr->next;
	delete ptr;
      }
      return ptr != NULL;
    }

  void Delete() { // deletes whole list
    ListEl<T> *ptr = head;
    while ((head = ptr) != NULL) {
      ptr = head->next;
      delete head;
    }
  }

  int Empty() { return head == NULL; }

  operator int() { return !Empty(); }

  T First() { return head->elem; }

  T RemoveHead() { // bad things will happend if there isn't a head
    ListEl<T> *ptr = head;
    T el = ptr->elem;
    head = ptr->next;
    delete ptr;
    return el;
  }

  int Count() {
    ListEl<T> *ptr = head;
    for (int n = 0; ptr != NULL; ++n) ptr = ptr->next;
    return n;
  }

friend class Lister<T>;

private:
  ListEl<T> *head;
};

// if two listers are working on the same list and you
// delete the list element one lister is pointing to, you are going
// to be in big trouble, don't do it
// inserts are all right, you can do them concurrently with no problems
template<class T>
class Lister
{
public:
  Lister(List<T> &l) { curr = &l.head; }
  Lister() { curr = NULL; } // uninitialized lister, be careful
  ~Lister() {}

  void operator=(List<T> &l) { curr = &l.head; }
  void operator=(Lister<T> &l) { curr = l.curr; }

  T Elem() {
    if (*curr == NULL) Error("Elem: Empty List");
    return (*curr)->elem;
  }

  // allows for in place modification
  T &RefElem() {
    if (*curr == NULL) Error("Elem: Empty List");
    return (*curr)->elem;
  }

  // inserts el before current element
  void Insert(T el)
    { ListEl<T> *elem = new ListEl<T>(el, *curr); *curr = elem; }

  // deletes the current element and returns it
  T Delete()
    { if (*curr == NULL) Error("Delete: Empty List");
      ListEl<T> *ptr = *curr;
      *curr = ptr->next;
      T el = ptr->elem;
      delete ptr;
      return el;
    }

  // returns true if there is a next, otherwise false
  void Next() {
    if (*curr == NULL) Error("Next: Empty List");
    else curr = &((*curr)->next);
  }

  int Empty() { return *curr == NULL; }

  operator int() { return !Empty(); }

private:
  ListEl<T> **curr;

  void Error(char *str) { fputs(str, stderr); exit(1); }
};

#endif
