/*	Copyright (C) 2004 Garrett A. Kajmowicz

	This file is part of the uClibc++ Library.

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

	This library 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
	Lesser General Public License for more details.

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

#include <memory>
#include <iterator>
#include <algorithm>

#ifndef __STD_HEADER_LIST
#define __STD_HEADER_LIST 1

#pragma GCC visibility push(default)

namespace std{

	template <class T, class Allocator = allocator<T> > class _UCXXEXPORT list {
	public:
		typedef typename Allocator::reference		reference;
		typedef typename Allocator::const_reference	const_reference;
		typedef typename Allocator::size_type		size_type;
		typedef typename Allocator::difference_type	difference_type;
		typedef T					value_type;
		typedef Allocator				allocator_type;
		typedef typename Allocator::pointer		pointer;
		typedef typename Allocator::const_pointer	const_pointer;

	protected:
		class node;
		class iter_list;

		node * list_start;
		node * list_end;
		size_type elements;
		Allocator a;

	public:

		typedef iter_list				iterator;
		typedef iter_list				const_iterator;
		typedef std::reverse_iterator<iterator>		reverse_iterator;
		typedef std::reverse_iterator<const_iterator>	const_reverse_iterator;

		explicit list(const Allocator& = Allocator());
		explicit list(size_type n, const T& value = T(), const Allocator& = Allocator());
		template <class InputIterator> list(InputIterator first, InputIterator last,
			const Allocator& al= Allocator());
		list(const list<T,Allocator>& x);
		~list();

		list<T,Allocator>& operator=(const list<T,Allocator>& x){
			if(&x == this){
				return *this;
			}
			clear();
			iterator i = x.begin();
			while(i != x.end()){
				push_back(*i);
				++i;
			}
			return *this;
		}

		template <class InputIterator> void assign(InputIterator first, InputIterator last);
		template <class Size, class U> void assign(Size n, const U& u = U());
		allocator_type get_allocator() const;

		iterator		begin();
		const_iterator		begin() const;
		iterator		end();
		const_iterator		end() const;
		reverse_iterator	rbegin();
		const_reverse_iterator	rbegin() const;
		reverse_iterator	rend();
		const_reverse_iterator	rend() const;

		bool      empty() const;
		size_type size() const;
		size_type max_size() const;
		void      resize(size_type sz, T c = T());

		reference       front();
		const_reference front() const;
		reference       back();
		const_reference back() const;

		void push_front(const T& x);
		void pop_front();
		void push_back(const T& x);
		void pop_back();
		iterator insert(iterator position, const T& x = T());
		void     insert(iterator position, size_type n, const T& x);
		template <class InputIterator> void insert(iterator position, InputIterator first, InputIterator last);
		iterator erase(iterator position);
		iterator erase(iterator position, iterator last);
		void     swap(list<T,Allocator>&);
		void     clear();

		void splice(iterator position, list<T,Allocator>& x);
		void splice(iterator position, list<T,Allocator>& x, iterator i);
		void splice(iterator position, list<T,Allocator>& x, iterator first, iterator last);
		void remove(const T& value);
		template <class Predicate> void remove_if(Predicate pred);
		void unique();
		template <class BinaryPredicate> void unique(BinaryPredicate binary_pred);
		void merge(list<T,Allocator>& x);
		template <class Compare> void merge(list<T,Allocator>& x, Compare comp);
		void sort();
		template <class Compare> void sort(Compare comp);
		void reverse();
	protected:
		void swap_nodes(node * x, node * y);
	};


	//Implementations of List

	//List node
	template <class T, class Allocator> class _UCXXEXPORT list<T, Allocator>::node{
	public:
		node * previous;
		node * next;
		T * val;

		node(): previous(0), next(0), val(0){ }
		node(const T & t ): previous(0), next(0), val(0) {
			val = new T(t);
			//FIXME use allocator somehow but only call constructor once
		}
		node(const T & t, node * p, node * n): previous(p), next(n), val(0) {
			val = new T(t);
		}
		~node(){ }
	};

	//List iterator
	template <class T, class Allocator> class _UCXXEXPORT list<T, Allocator>::iter_list
		: public std::iterator<
			bidirectional_iterator_tag,
			T,
			typename Allocator::difference_type,
			typename Allocator::pointer,
			typename Allocator::reference
		>
	{
	private:
		node * current;
	public:
		iter_list():current(0) { }
		iter_list( typename list<T, Allocator>::node * n): current(n) { }
		iter_list(const list<T, Allocator>::iter_list & l): current(l.current) { }
		~iter_list(){ }

		iter_list & operator=(const list<T, Allocator>::iter_list & right ){
			current = right.current;
			return *this;
		}

		T & operator*(){
			return *(current->val);
		}
		T * operator->(){
			return current->val;
		}
		const T & operator*() const{
			return *current->val;
		}
		const T * operator->() const{
			return current->val;
		}

		bool operator==(const list<T, Allocator>::iter_list & right) const {
			return (current == right.current);
		}

		bool operator!=(const list<T, Allocator>::iter_list & right) const {
			return (current != right.current);
		}

		iter_list & operator++(){
			current = current->next;
			return *this;
		}

		iter_list operator++(int){
			iter_list temp(current);
			current = current->next;
			return temp;
		}
		iter_list & operator--(){
			current = current->previous;
			return *this;
		}

		iter_list operator--(int){
			iter_list temp(current);
			current = current->previous;
			return temp;
		}
		node * link_struct(){
			return current;
		}
		iter_list & operator+=(unsigned int n){
			for(unsigned int i = 0; i < n; ++i){
				current = current->next;
			}
			return *this;
		}
		iter_list & operator-=(unsigned int n){
			for(unsigned int i = 0; i < n; ++i){
				current = current->previous;
			}
			return *this;
		}
	};


	template<class T, class Allocator> list<T, Allocator>::list(const Allocator& al)
		:list_start(0), list_end(0), elements(0), a(al)
	{
		//End node
		list_start = new node();
		list_end = list_start;
		return;
	}

	template<class T, class Allocator> list<T, Allocator>::list
		(typename Allocator::size_type n, const T& value, const Allocator& al)
		:list_start(0), list_end(0), elements(0), a(al)
	{
		//End node
		list_start = new node();
		list_end = list_start;

		for(typename Allocator::size_type i = 0; i < n ; ++i){
			push_back(value);
		}
	}

	template<class T, class Allocator> template <class InputIterator>
		list<T, Allocator>::list
		(InputIterator first, InputIterator last, const Allocator& al)
		: list_start(0), list_end(0), elements(0), a(al)
	{
		list_start = new node();
		list_end = list_start;
		while(first != last){
			push_back(*first);
			++first;
		}
	}

	template<class T, class Allocator> list<T, Allocator>::list(const list<T,Allocator>& x)
		: list_start(0), list_end(0), elements(0), a(x.a)
	{
		list_start = new node();
		list_end = list_start;

		iterator i = x.begin();
		while(i != x.end()){
			push_back( *i);
			++i;
		}
	}

	template<class T, class Allocator> list<T, Allocator>::~list(){
		while(elements > 0){
			pop_front();
		}
		delete list_start->val;
#if UCLIBCXX_DEBUG
		list_start->val = 0;
#endif
		delete list_start;
#if UCLIBCXX_DEBUG
		list_start = 0;
		list_end = 0;
#endif
	}


	template<class T, class Allocator> void list<T, Allocator>::swap_nodes(node * x, node * y){
		T * v = x->val;
		x->val = y->val;
		y->val = v;
	}

	template<class T, class Allocator> typename list<T, Allocator>::iterator
		list<T, Allocator>::begin()
	{
		return iterator(list_start);
	}


	template<class T, class Allocator> typename list<T, Allocator>::const_iterator
		list<T, Allocator>::begin() const
	{
		return const_iterator(list_start);
	}


	template<class T, class Allocator> typename list<T, Allocator>::iterator
		list<T, Allocator>::end()
	{
		return iterator(list_end);
	}

	template<class T, class Allocator> typename list<T, Allocator>::const_iterator
		list<T, Allocator>::end() const
	{
		return const_iterator(list_end);
	}

	template<class T, class Allocator> typename list<T, Allocator>::reverse_iterator
		list<T, Allocator>::rbegin()
	{
		return reverse_iterator(end());
	}

	template<class T, class Allocator> typename list<T, Allocator>::const_reverse_iterator
		list<T, Allocator>::rbegin() const
	{
		return const_reverse_iterator(end());
	}

	template<class T, class Allocator> typename list<T, Allocator>::reverse_iterator
		list<T, Allocator>::rend()
	{
		return reverse_iterator(begin());
	}

	template<class T, class Allocator> typename list<T, Allocator>::const_reverse_iterator
		list<T, Allocator>::rend() const
	{
		return const_reverse_iterator(begin());
	}

	template<class T, class Allocator> bool list<T, Allocator>::empty() const{
		return (elements == 0);
	}
	template<class T, class Allocator> typename list<T, Allocator>::size_type list<T, Allocator>::size() const{
		return elements;
	}
	template<class T, class Allocator> typename list<T, Allocator>::size_type list<T, Allocator>::max_size() const{
		return ((size_type)(-1)) / (sizeof(T) + sizeof(node));
	}
	template<class T, class Allocator> void list<T, Allocator>::resize(typename Allocator::size_type sz, T c){
//		if(sz > elements){
			for(typename Allocator::size_type i = elements; i < sz; ++i){
				push_back(c);
			}
//		}
//		if(sz < elements){
			for(typename Allocator::size_type i = elements; i > sz; --i){
				pop_back();
			}
//		}
	}

	template<class T, class Allocator> typename list<T, Allocator>::reference list<T, Allocator>::front(){
		return *(list_start->val);
	}
	template<class T, class Allocator> typename list<T, Allocator>::const_reference list<T, Allocator>::front() const{
		return *(list_start->val);
	}
	template<class T, class Allocator> typename list<T, Allocator>::reference list<T, Allocator>::back(){
		return *(list_end->previous->val);
	}
	template<class T, class Allocator> typename list<T, Allocator>::const_reference list<T, Allocator>::back() const{
		return *(list_end->previous->val);
	}


	template<class T, class Allocator> void list<T, Allocator>::push_front(const T& x){
		node * temp = new node(x);
		list_start->previous = temp;
		temp->previous = 0;
		temp->next = list_start;
		list_start = temp;
		++elements;
	}

	template<class T, class Allocator> void list<T, Allocator>::pop_front(){
		if(elements > 0){
			list_start = list_start->next;
			delete list_start->previous->val;
#if UCLIBCXX_DEBUG
			list_start->previous->val = 0;
			list_start->previous->next = 0;
			list_start->previous->previous = 0;
#endif
			delete list_start->previous;
			list_start->previous = 0;
			--elements;
		}
	}

	template<class T, class Allocator> void list<T, Allocator>::push_back(const T& x){
		if(elements == 0){
			//The list is completely empty
			list_start = new node(x);
			list_end->previous = list_start;
			list_start->previous = 0;
			list_start->next = list_end;
			elements = 1;
		}else{
			node * temp = new node(x);
			temp->previous = list_end->previous;
			temp->next = list_end;
			list_end->previous->next = temp;
			list_end->previous = temp;
			++elements;
		}
	}

	template<class T, class Allocator> void list<T, Allocator>::pop_back(){
		if(elements > 0){
			node * temp = list_end->previous;
			if(temp == list_start){
				list_end->previous = 0;
				list_start = list_end;
			}else{
				temp->previous->next = temp->next;
				list_end->previous = temp->previous;
			}
			delete temp->val;
#if UCLIBCXX_DEBUG
			temp->val = 0;
			temp->next = 0;
			temp->previous = 0;
#endif
			delete temp;
#if UCLIBCXX_DEBUG
			temp = 0;
#endif
			--elements;
		}
	}


	template<class T, class Allocator> typename list<T, Allocator>::iterator
		list<T, Allocator>::insert(iterator position, const T& x)
	{
		node * temp = new node(x);

		temp->previous = position.link_struct()->previous;
		temp->next = position.link_struct();

		if(temp->previous == 0){
			list_start = temp;
		}else{
			position.link_struct()->previous->next = temp;
		}

		position.link_struct()->previous = temp;

		++elements;
		--position;
		return position;
	}

	template<class T, class Allocator> void list<T, Allocator>::insert(iterator position, size_type n, const T& x){
		for(typename list<T, Allocator>::size_type i = 0; i < n; ++i){
			position = insert(position, x);
		}
	}

	template<class T, class Allocator> template <class InputIterator> void
		list<T, Allocator>::insert(iterator position, InputIterator first, InputIterator last)
	{
		while(first !=last){
			insert(position, *first);
			++first;
		}
	}
	template<class T, class Allocator> typename list<T, Allocator>::iterator
		list<T, Allocator>::erase(iterator position)
	{
		if(position != end() ){
			node * temp = position.link_struct();
			if(temp == list_start){
				++position;
				temp->next->previous = 0;
				list_start = temp->next;
			}else{
				--position;
				temp->next->previous = temp->previous;
				temp->previous->next = temp->next;
				++position;
			}
			delete temp->val;
#if UCLIBCXX_DEBUG
			temp->next = 0;
			temp->previous = 0;
			temp->val = 0;
#endif
			delete temp;
#if UCLIBCXX_DEBUG
			temp = 0;
#endif
			--elements;
		}
		return position;
	}
	template<class T, class Allocator> typename list<T, Allocator>::iterator
		list<T, Allocator>::erase(iterator position, iterator last)
	{
		iterator temp = position;
		while(position !=last){
			position = erase(position);
		}
		return position;
	}
	template<class T, class Allocator> void list<T, Allocator>::swap(list<T,Allocator>& l){
		node * temp;
		size_type tempel;

		temp = list_start;
		list_start = l.list_start;
		l.list_start = temp;

		temp = list_end;
		list_end = l.list_end;
		l.list_end = temp;

		tempel = elements;
		elements = l.elements;
		l.elements = tempel;
	}
	template<class T, class Allocator> void list<T, Allocator>::clear(){
		while(elements > 0){
			pop_front();
		}
	}

	template<class T, class Allocator>
		void list<T, Allocator>::splice(iterator position, list<T,Allocator>& x)
	{

		//Can't add non-existant elements
		if(x.elements == 0){
			return;
		}

		elements += x.elements;
		x.elements = 0;


		//Chaining to the begining
		if(position == begin()){
			x.list_end->previous->next = list_start;
			list_start->previous = x.list_end->previous;

			list_start = x.list_start;

			x.list_start = x.list_end;
			x.list_end->previous = 0;

			return;
		}

		//Link everything we need
		x.list_start->previous = position.link_struct()->previous;
		position.link_struct()->previous->next = x.list_start;

		position.link_struct()->previous = x.list_end->previous;
		x.list_end->previous->next = position.link_struct();

		//Clean up the other list

		x.list_start = x.list_end;
		x.list_end->previous=0;

	}

	template<class T, class Allocator>
		void list<T, Allocator>::splice(iterator position, list<T,Allocator>& x, iterator i)
	{
		//Invalid conditions
		if( x.elements == 0 || i == position || position.link_struct() == i.link_struct()->next ){
			return;
		}

		//Do we need to adjust the begining pointer?
		if(i == x.begin()){
			x.list_start = x.list_start->next;
			x.list_start->previous = 0;
		}


		//Insert at begining special case
		if(position == begin()){

			i.link_struct()->previous->next = i.link_struct()->next;
			i.link_struct()->next->previous = i.link_struct()->previous;

			i.link_struct()->previous = 0;
			i.link_struct()->next = position.link_struct();
			position.link_struct()->previous = i.link_struct();

			list_start = i.link_struct();

			--x.elements;
			++elements;
			return;
		}

		if( i.link_struct()->previous != 0){
			i.link_struct()->previous->next = i.link_struct()->next;
			i.link_struct()->next->previous = i.link_struct()->previous;
		}else{
			i.link_struct()->next->previous = 0;
			x.list_start = i.link_struct()->next;
		}

		i.link_struct()->previous = position.link_struct()->previous;
		position.link_struct()->previous->next = i.link_struct();

		i.link_struct()->next = position.link_struct();
		position.link_struct()->previous = i.link_struct();

		--x.elements;
		++elements;
	}

	template<class T, class Allocator>
		void list<T, Allocator>::splice(iterator position, list<T,Allocator>& x,
			iterator first, iterator last)
	{
		if(x.elements == 0){
			return;
		}

		iterator temp;
		while(first != last){
			temp = first;
			++first;
			splice(position, x, temp);
		}
	}


	template<class T, class Allocator> void list<T, Allocator>::remove(const T& value){
		iterator temp = begin();
		while( temp != end() ){
			if(*temp == value){
				temp = erase(temp);
			}else{
				++temp;
			}
		}
	}


	template<class T, class Allocator> template <class Predicate> void list<T, Allocator>::remove_if(Predicate pred){
		iterator temp = begin();
		while( temp != end() ){
			if( pred(*temp) ){
				temp = erase(temp);
			}else{
				++temp;
			}
		}
	}


	template<class T, class Allocator> void list<T, Allocator>::unique(){
		equal_to<typename iterator_traits<iterator>::value_type> p;
		unique(p);
	}

	template<class T, class Allocator> template <class BinaryPredicate>
		void list<T, Allocator>::unique(BinaryPredicate binary_pred)
	{
		iterator temp1 = begin();
		iterator temp2;
		++temp1;
		while( temp1 != end() ){
			temp2 = temp1;
			--temp2;
			if( binary_pred(*temp1, *temp2) ){
				erase(temp1);
				temp1 = temp2;
			}
			++temp1;
		}
	}

	template<class T, class Allocator> void list<T, Allocator>::merge(list<T,Allocator>& x){
		less<typename iterator_traits<typename list<T, Allocator>::iterator>::value_type> c;
		merge(x, c);
	}

	template<class T, class Allocator> template <class Compare>
		void list<T, Allocator>::merge(list<T,Allocator>& x, Compare comp)
	{
		iterator source = x.begin();
		iterator temp;
		iterator dest  = begin();

		while(source != x.end()){
			while( dest != end() && comp (*dest, *source) ){
				++dest;
			}
			++elements;
			--x.elements;

			temp = source;
			++temp;

			if(dest == begin()){
				dest.link_struct()->previous = source.link_struct();
				source.link_struct()->next = dest.link_struct();
				source.link_struct()->previous = 0;
				list_start = source.link_struct();
			}else{
				source.link_struct()->previous = dest.link_struct()->previous;
				dest.link_struct()->previous->next = source.link_struct();
				source.link_struct()->next = dest.link_struct();
				dest.link_struct()->previous = source.link_struct();
			}
			source = temp;
		}

		//Fix up x;
		x.list_start = x.list_end;
		x.list_start->previous = 0;
	}

	template<class T, class Allocator> void list<T, Allocator>::sort(){
		less<typename iterator_traits<typename list<T, Allocator>::iterator>::value_type> c;
		sort(c);
	}

	template<class T, class Allocator> template <class Compare>
		void list<T, Allocator>::sort(Compare comp)
	{
		typename list<T, Allocator>::iterator i, j, k;

		//FIXME - bubble sort

		if(elements == 0){
			return;
		}

		i = end();
		--i;
		while(i != begin()){
			j = begin();
			k = j;
			++k;
			while(j != i){
				if( comp(*k, *j) ){
					swap_nodes(k.link_struct(), j.link_struct());
				}
				++j;
				++k;
			}
			--i;
		}
	}


	template<class T, class Allocator> void list<T, Allocator>::reverse(){
		if(elements == 0){
			return;
		}

		node * current;
		node * following;
		node * temp;

		//Need to move the list_end element to the begining

		temp = list_end;
		list_end = temp->previous;
		list_end->next = 0;

		list_start->previous = temp;
		temp->previous = 0;
		temp->next = list_start;
		list_start = temp;

		current = list_start;

		while( current != list_end ){
			following = current->next;

			//Swap the values pointed to/at with the current node
			temp = current->next;
			current->next = current->previous;
			current->previous = temp;

			current = following;
		}

		//Swap pointers on the end node
		temp = list_end->next;
		list_end->next = list_end->previous;
		list_end->previous = temp;


		//Swap the fixed pointers
		temp = list_start;
		list_start = list_end;
		list_end = temp;

	}

	template <class T, class Allocator>
	bool operator==(const list<T,Allocator>& x, const list<T,Allocator>& y){
		if(x.size() != y.size()){
			return false;
		}
		typename list<T,Allocator>::const_iterator i = x.begin();
		typename list<T,Allocator>::const_iterator j = y.begin();

		while(i != x.end()){
			if( *i != *j){
				return false;
			}
			++i;
			++j;
		}
		return true;
	}

	template <class T, class Allocator>
	bool operator< (const list<T,Allocator>& x, const list<T,Allocator>& y){
		typename list<T,Allocator>::const_iterator i = x.begin();
		typename list<T,Allocator>::const_iterator j = y.begin();
		while(i != x.end() && j != y.end()){
			if( *i < *j){
				return true;
			}
			if(*j < *i){
				return false;
			}
			++i;
			++j;
		}
		return (i == x.end() && j != y.end());
	}

	template <class T, class Allocator>
	bool operator!=(const list<T,Allocator>& x, const list<T,Allocator>& y){
		return !(x == y);
	}

	template <class T, class Allocator>
	bool operator> (const list<T,Allocator>& x, const list<T,Allocator>& y){
		typename list<T,Allocator>::const_iterator i = x.begin();
		typename list<T,Allocator>::const_iterator j = y.begin();
		while(i != x.end() && j != y.end()){
			if( *i > *j){
				return true;
			}
			if( *j > *i){
				return false;
			}
			++i;
			++j;
		}
		return (i != x.end() && j == y.end());
	}

	template <class T, class Allocator>
	bool operator>=(const list<T,Allocator>& x, const list<T,Allocator>& y){
		typename list<T,Allocator>::const_iterator i = x.begin();
		typename list<T,Allocator>::const_iterator j = y.begin();
		while(i != x.end() && j != y.end()){
			if( *i >= *j){
				return true;
			}
			if(*j >= *i){
				return false;
			}
			++i;
			++j;
		}
		return (i != x.end() && j == y.end());
	}

	template <class T, class Allocator>
	bool operator<=(const list<T,Allocator>& x, const list<T,Allocator>& y){
		typename list<T,Allocator>::const_iterator i = x.begin();
		typename list<T,Allocator>::const_iterator j = y.begin();
		while(i != x.end() && j != y.end()){
			if( *i <= *j){
				return true;
			}
			if(*j <= *i){
				return false;
			}
			++i;
			++j;
		}
		return (i == x.end());
	}

	template <class T, class Allocator>
	void swap(list<T,Allocator>& x, list<T,Allocator>& y){
		x.swap(y);
	}

}

#pragma GCC visibility pop

#endif


