/*------------------------------------------------------------------------------
 src/lib/dj/djList_.h
 
 Copyright (c) 2004-2006 Darby Johnston.
 All rights reserved.
 Use is subject to license terms.
------------------------------------------------------------------------------*/

//#include <stdio.h>

namespace dj {

template <class T>
ListNode<T>::ListNode() :
  prev(this), next(this)
{}

template <class T>
ConstListIt<T>::ConstListIt(
  const ListNode<T> * in, const ListNode<T> * stub
) :
  _node(in), _stub(stub),
  _count(0)
{}

template <class T>
bool ConstListIt<T>::operator == (const ConstListIt & in) const
{
  return _node == in._node;
}

template <class T>
bool ConstListIt<T>::operator != (const ConstListIt & in) const
{
  return _node != in._node;
}
  
template <class T>
ListIt<T>::ListIt(ListNode<T> * in, const ListNode<T> * stub) :
  ConstListIt<T>(in, stub)
{}

template <class T>
void ListIt<T>::operator () (const T & in)
{
  const_cast<ListNode<T> *>(ConstListIt<T>::_node)->data = in;
}

template <class T>
T & ListIt<T>::operator () ()
{
  return const_cast<ListNode<T> *>(ConstListIt<T>::_node)->data;
}

template <class T>
const T & ListIt<T>::operator () () const
{
  return ConstListIt<T>::_node->data;
}

template <class T>
_List<T>::_List()
{
  //printf("dj::_List<T>::_List(%d)\n", int(this));
  
  _stub = new StubNode<T>;
}

template <class T>
_List<T>::_List(const _List<T> & in) :
  _stub(0)
{
  //printf("dj::_List<T>::_List(%d)\n", int(this));
  //printf("  in = %d\n", int(&in));
  
  _stub = in._stub;
  ++_stub->ref;
}

template <class T>
_List<T>::~_List()
{
  //printf("dj::_List<T>::~_List(%d)\n", int(this));
  //printf("  ref = %d\n", _stub->ref);
  
  _del();
}

template <class T>
template <class U>
_List<T>::StubNode<U>::StubNode() :
  size(0),
  ref(1)
{}

template <class T>
void _List<T>::_del()
{
  --_stub->ref;
  if (! _stub->ref) {
    for (ListNode<T> * p = _stub->next; p != _stub;) {
      ListNode<T> * tmp = p;
      p = p->next;
      delete tmp;
    }
    delete _stub;
  }
  _stub = 0;
}

template <class T>
void _List<T>::detach()
{
  if (_stub->ref <= 1) return;
  
  //printf("dj::_List<T>::detach\n");
  //printf("  ref = %d\n", _stub->ref);
  
  StubNode<T> * tmp = _stub;
  _stub = new StubNode<T>;
  add(tmp->next, tmp->size);
  --tmp->ref;
}

template <class T>
void _List<T>::set(const T & in, int index)
{
  detach();
  node(index)->data = in;
}

template <class T>
_List<T> & _List<T>::operator = (const _List<T> & in)
{
  //printf("dj::_List<T>::operator = (%d)\n", int(this));
  //printf("  in = %d\n", int(&in));
  //printf("  ref = %d\n", _stub->ref);
  
  if (&in != this) {
    if (_stub) _del();
    _stub = in._stub;
    ++_stub->ref;
  }
  return *this;
}

template <class T>
T & _List<T>::operator [] (int index)
{
  detach();
  return node(index)->data;
}

template <class T>
void _List<T>::size(int in)
{
  const int size = _stub->size;
  if (in < 0) ;
  else if (in < size)
    while (in++ < size) shift();
  else if (in > size)
    while (in-- > size) add(T());
}

template <class T>
const T & _List<T>::get(int index) const { return node(index)->data; }

template <class T>
const T & _List<T>::operator [] (int index) const
{
  return node(index)->data;
}

template <class T>
int _List<T>::insert(const T & in, int index)
{
  detach();

  ListNode<T> * p = _stub;
  for (int i = 0; i < index && p->next != _stub; ++i, p = p->next) ;
  
  ListNode<T> * tmp = new ListNode<T>;
  tmp->data = in;
  tmp->prev = p;
  tmp->next = p->next;
  p->next->prev = tmp;
  p->next = tmp;
  ++_stub->size;
  
  return _stub->size;
}

template <class T>
int _List<T>::insert(const _List<T> & in, int index)
{
  detach();

  ListNode<T> * outP = _stub;
  for (int i = 0; i < index && outP->next != _stub; ++i, outP = outP->next) ;
  
  for (
    const ListNode<T> * inP = in._stub->next;
    inP != in._stub;
    inP = inP->next, outP = outP->next
  ) {
    ListNode<T> * tmp = new ListNode<T>;
    tmp->data = inP->data;
    tmp->prev = outP;
    tmp->next = outP->next;
    outP->next->prev = tmp;
    outP->next = tmp;
    ++_stub->size;
  }
  
  return _stub->size;
}

template <class T>
int _List<T>::insert(const T * in, int index, int size)
{
  detach();

  ListNode<T> * outP = _stub;
  for (int i = 0; i < index && outP->next != _stub; ++i, outP = outP->next) ;
  
  for (int i = 0; i < size; ++i, outP = outP->next) {
    ListNode<T> * tmp = new ListNode<T>;
    tmp->data = in[i];
    tmp->prev = outP;
    tmp->next = outP->next;
    outP->next->prev = tmp;
    outP->next = tmp;
    ++_stub->size;
  }
  
  return _stub->size;
}

template <class T>
int _List<T>::push(const T & in)
{
  detach();

  ListNode<T> * tmp = new ListNode<T>;
  tmp->data = in;
  tmp->prev = _stub;
  tmp->next = _stub->next;
  _stub->next->prev = tmp;
  _stub->next = tmp;
  ++_stub->size;
  
  return _stub->size;
}

template <class T>
int _List<T>::add(const T & in)
{
  detach();

  ListNode<T> * tmp = new ListNode<T>;
  tmp->data = in;
  tmp->prev = _stub->prev;
  tmp->next = _stub;
  _stub->prev->next = tmp;
  _stub->prev = tmp;
  ++_stub->size;
  
  return _stub->size;
}

template <class T>
int _List<T>::add(const _List<T> & in)
{
  return add(in._stub->next, in._stub->size);
}

template <class T>
int _List<T>::add(const T * in, int size)
{
  detach();

  for (int i = 0; i < size; ++i) {
    ListNode<T> * tmp = new ListNode<T>;
    tmp->data = in[i];
    tmp->prev = _stub->prev;
    tmp->next = _stub;
    _stub->prev->next = tmp;
    _stub->prev = tmp;
    ++_stub->size;
  }
  
  return _stub->size;
}

template <class T>
int _List<T>::add(ConstListIt<T> it, ConstListIt<T> end)
{
  detach();

  for (; it != end; ++it) {
    ListNode<T> * tmp = new ListNode<T>;
    tmp->data = it();
    tmp->prev = _stub->prev;
    tmp->next = _stub;
    _stub->prev->next = tmp;
    _stub->prev = tmp;
    ++_stub->size;
  }
  
  return _stub->size;
}

template <class T>
int _List<T>::add(const ListNode<T> * in, int size)
{
  detach();

  for (int i = 0; i < size; ++i, in = in->next) {
    ListNode<T> * tmp = new ListNode<T>;
    tmp->data = in->data;
    tmp->prev = _stub->prev;
    tmp->next = _stub;
    _stub->prev->next = tmp;
    _stub->prev = tmp;
    ++_stub->size;
  }
  
  return _stub->size;
}

template <class T>
void _List<T>::operator += (const _List<T> & in)
{
  add(in._stub->next, in._stub->size);
}

template <class T>
void _List<T>::operator += (const T & in) { add(in); }

template <class T>
const T _List<T>::pop()
{
  detach();
  if (! _stub->size) return T();

  ListNode<T> * p = _stub->next;
  p->next->prev = _stub;
  _stub->next = p->next;
  --_stub->size;
  const T out = p->data;
  delete p;
  
  return out;
}

template <class T>
const T _List<T>::shift()
{
  detach();
  if (! _stub->size) return T();

  ListNode<T> * p = _stub->prev;
  p->prev->next = _stub;
  _stub->prev = p->prev;
  --_stub->size;
  const T out = p->data;
  delete p;
  
  return out;
}

template <class T>
const T _List<T>::del(int index)
{
  detach();
  if (index < 0 || index >= _stub->size) return T();
  
  ListNode<T> * p = _stub->next;
  for (int i = 0; i < index; ++i) p = p->next;
  
  p->prev->next = p->next;
  p->next->prev = p->prev;
  --_stub->size;
  const T out = p->data;
  delete p;
  
  return out;
}

template <class T>
int _List<T>::del(int index, int size)
{
  detach();
  if (index < 0 || index >= _stub->size) return _stub->size;
  
  ListNode<T> * p = _stub->next;
  for (int i = 0; i < index; ++i) p = p->next;
  
  while (size-- && p != _stub) {
    ListNode<T> * tmp = p;
    p = tmp->next;
    tmp->prev->next = tmp->next;
    tmp->next->prev = tmp->prev;
    --_stub->size;
    delete tmp;
  }
  
  return _stub->size;
}

template <class T>
void _List<T>::del()
{
  detach();

  for (ListNode<T> * p = _stub->next; p != _stub;) {
    ListNode<T> * tmp = p;
    p = p->next;
    delete tmp;
  }
  
  _stub->next = _stub;
  _stub->prev = _stub;
  _stub->size = 0;
}

template <class T>
int _List<T>::replace(const T & in, int index, int size)
{
  return replace(List<T>() + in, index, size);
}

template <class T>
int _List<T>::replace(const _List<T> & in, int index, int size)
{
  del(index, size);
  return insert(in, index);
}

template <class T>
const ConstListIt<T> _List<T>::start() const
{
  return ConstListIt<T>(_stub->next, _stub);
}

template <class T>
const ListIt<T> _List<T>::start()
{
  detach();
  return ListIt<T>(_stub->next, _stub);
}

template <class T>
const ConstListIt<T> _List<T>::end() const
{
  return ConstListIt<T>(_stub->prev, _stub);
}

template <class T>
const ListIt<T> _List<T>::end()
{
  detach();
  return ListIt<T>(_stub->prev, _stub);
}

template <class T>
const ConstListIt<T> _List<T>::it(int index) const
{
  return ConstListIt<T>(node(index), _stub);
}

template <class T>
const ListIt<T> _List<T>::it(int index)
{
  detach();
  return ListIt<T>(node(index), _stub);
}

template <class T>
ListNode<T> * _List<T>::node(int index)
{
  ListNode<T> * p = _stub;
  if (index > (_stub->size / 2)) {
    index = _stub->size - 1 - index;
    while (index-- >= 0) p = p->prev;
  } else
    while (index-- >= 0) p = p->next;
  return p;
}

template <class T>
const ListNode<T> * _List<T>::node(int index) const
{
  const ListNode<T> * p = _stub;
  if (index > (_stub->size / 2)) {
    index = _stub->size - 1 - index;
    while (index-- >= 0) p = p->prev;
  } else
    while (index-- >= 0) p = p->next;
  return p;
}

}
