// LinkedList.cc for Blackbox - an X11 Window manager
// Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the 
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in 
// all copies or substantial portions of the Software. 
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
  
// stupid macros needed to access some functions in version 2 of the GNU C 
// library
#ifndef   _GNU_SOURCE
#define   _GNU_SOURCE
#endif // _GNU_SOURCE

#include "LinkedList.hh"

#ifdef    HAVE_CONFIG_H
#  include "../config.h"
#endif // HAVE_CONFIG_H

#ifdef    HAVE_STDIO_H
#  include <stdio.h>
#endif // HAVE_STDIO_H


__llist_iterator::__llist_iterator(__llist *l) {
  // initialize the iterator...
  list = l;

  if (list) {
    if (! list->iterators)
      list->iterators = new __llist;

    list->iterators->insert(this);
  }

  reset();
}


__llist_iterator::~__llist_iterator(void) {
  if (list && list->iterators)
    list->iterators->remove(this);
}


void *__llist_iterator::current(void) {
  // return the current node data... if any
  return ((node) ? node->getData() : 0);
}


void __llist_iterator::reset(void) {
  // update the iterator's current node to the first node in the linked list
  if (list)
    node = list->_first;
}


const int __llist_iterator::set(const int index) {
  // set the current node to index
  if (list) {
    if (index < list->elements && index >= 0 && list->_first) {
      node = list->_first;
      
      for (register int i = 0; i < index; i++)
	node = node->getNext();
      
      return 1;
    }
  }
  
  node = (__llist_node *) 0;
  return 0;
}


void __llist_iterator::operator++(void) {
  // iterate to the next node in the list...
  node = ((node) ? node->getNext() : 0);
}     


void __llist_iterator::operator++(int) {
  // iterate to the next node in the list...
  node = ((node) ? node->getNext() : 0);
}


__llist::__llist(void *d) {
  // initialize the linked list...
  _first = (__llist_node *) 0;
  _last = (__llist_node *) 0;
  iterators = (__llist *) 0;
  elements = 0;
  
  if (d) insert(d);
}


__llist::~__llist(void) {
  // remove all the items in the list...
  for (register int i = 0, r = elements; i < r; i++)
    remove(0);

  if (iterators) {
    __llist_node *n = iterators->_first;

    while (n) {
      ((__llist_iterator *) n->getData())->list = (__llist *) 0;
      ((__llist_iterator *) n->getData())->node = (__llist_node *) 0;

      n = n->getNext();
    }

    delete iterators;
  }
}


const int __llist::insert(void *d, int index) {
  // insert item into linked list at specified index...
  
  if ((! _first) || (! _last)) {
    // list is empty... insert the item as the first item, regardless of the
    // index given
    _first = new __llist_node;
    _first->setData(d);
    _first->setNext((__llist_node *) 0);
    _last = _first;
  } else {
    if (index == 0) {
      // if index is 0... prepend the data on the list
      __llist_node *nnode = new __llist_node;
      
      nnode->setData(d);
      nnode->setNext(_first);
      
      _first = nnode;
    } else if ((index == -1) || (index == elements)) {
      // if index is -1... append the data on the list
      __llist_node *nnode = new __llist_node;
      
      nnode->setData(d);
      nnode->setNext((__llist_node *) 0);
      _last->setNext(nnode);

      _last = nnode;
    } else if (index < elements) {
      // otherwise... insert the item at the position specified by index
      __llist_node *nnode = new __llist_node, *inode = _first->getNext();
      
      if (! nnode)
	return -1;
      
      nnode->setData(d);
      
      for (register int i = 1; i < index; i++)
	if (inode)
	  inode = inode->getNext();
	else {
	  delete nnode;
	  return -1;
	}
      
      if ((! inode) || inode == _last) {
	nnode->setNext((__llist_node *) 0);
	_last->setNext(nnode);
	
	_last = nnode;
      } else {
	nnode->setNext(inode->getNext());
	inode->setNext(nnode);
      }
    }
  }
  
  return ++elements;
}


const int __llist::remove(void *d) {
  // remove list item whose data pointer address matches the pointer address
  // given

  if ((! _first) || (! _last))
    return -1;
  else if (_first->getData() == d) {
    // remove the first item in the list...
    __llist_node *node = _first;
    _first = _first->getNext();

    if (iterators && iterators->_first) {
      __llist_node *n = iterators->_first;
      while (n) {
        ((__llist_iterator *) n->getData())->reset();
        n = n->getNext();
      }
    }
 
    --elements;
    delete node;
    return 0;
  } else {
    // iterate through the list and remove the first occurance of the item
    
    // NOTE: we don't validate _first in this assignment, because it is checked
    // for validity above...
    __llist_node *rnode = _first->getNext(), *prev = _first;
    
    for (register int i = 1; i < elements; i++)
      if (rnode)
	if (rnode->getData() == d) {
	  // we found the item... update the previous node and delete the
	  // now useless rnode...
	  prev->setNext(rnode->getNext());
	  
	  if (rnode == _last)
	    _last = prev;

          if (iterators && iterators->_first) {
            __llist_node *n = iterators->_first;
            while (n) {
              ((__llist_iterator *) n->getData())->reset();
              n = n->getNext();
            }
          }

	  --elements;
	  delete rnode;
	  return i;
	} else {
	  prev = rnode;
	  rnode = rnode->getNext();
	}
    
    return -1;
  }
}


void *__llist::remove(const int index) {
  if (index >= elements || index < 0 || (! _first) || (! _last))
    return (void *) 0;

  // remove list item at specified index within the list
  if (index == 0) {
    // remove the first item in the list...
    __llist_node *node = _first;
    void *data_return = _first->getData();
    
    _first = _first->getNext();

    if (iterators && iterators->_first) {
      __llist_node *n = iterators->_first;
      while (n) {
        ((__llist_iterator *) n->getData())->reset();
        n = n->getNext();
      }
    }

    --elements;
    delete node;

    return data_return;
  } else {
    __llist_node *rnode = _first->getNext(), *prev = _first;
    void *data_return = (void *) 0;
    
    for (register int i = 1; i < index; i++)
      if (rnode) {
	prev = rnode;
	rnode = rnode->getNext();
      } else
	return (void *) 0;
    
    if (! rnode) return (void *) 0;
    
    prev->setNext(rnode->getNext());
    data_return = rnode->getData();
    
    if (rnode == _last)
      _last = prev;

    if (iterators && iterators->_first) {
      __llist_node *n = iterators->_first;
      while (n) {
        ((__llist_iterator *) n->getData())->reset();
        n = n->getNext();
      }
    }

    --elements;
    data_return = rnode->getData();
    delete rnode;
    return data_return;
  }
  
  return (void *) 0;
}

void *__llist::find(const int index) {
  if (index >= elements || index < 0 || (! _first) || (! _last))
    return (void *) 0;

  if (index == 0) {
    // return the first item
    return first();
  } else if (index == (elements - 1)) {
    // return the last item
    return last();
  } else {
    __llist_node *fnode = _first->getNext();
    
    for (register int i = 1; i < index; i++)
      if (fnode)
	fnode = fnode->getNext();
      else
	return (void *) 0;
    
    return fnode->getData();
  }
  
  return (void *) 0;
}


void *__llist::first(void) {
  if (_first)
    return _first->getData();
  
  return (void *) 0;
}


void *__llist::last(void) {
  if (_last)
    return _last->getData();
  
  return (void *) 0;
}