#include <iostream>
#include "LinkedListNode.cpp"
#include "LinkedListException.cpp"

using namespace std;

#ifndef LINKEDLIST_CLASS_INCLUDED
#define LINKEDLIST_CLASS_INCLUDED

/*
	Linked List Class
	LastModification : 2008. 5. 5
	Version : 1.0.1
	myllyj(myllyj at myllyj dot com) / http://program.myllyj.com

	Requirements:
		require LinkedListNode class.
		require LinkedListException classes.

	Comment:
		ÀÌ LinkedList´Â ÀÌÀü°ú ´ÙÀ½À» ÀÚÀ¯·ÎÀÌ Å½»öÇÒ ¼ö ÀÖ´Â
		DblLinkedListÀÓ
		¸ðµç Method´Â C# Naming StyleÀ» µû¸£°í ÀÖ´Ù.

*/

template <class T>
class LinkedList
{

private:
	LinkedListNode<T>* headerNode;
	LinkedListNode<T>* nowNode;
	void init();

public:

	LinkedList();

	void AddFirst(T data);
	void AddLast(T data);

	void Insert(T data);
	void Insert(int index, T data);

	int Count();

	void Remove();
	void Remove(int index);

	T Get();
	T Get(int index);

	void MovePrev();
	void MoveNext();
	bool Move(int index);
	void MoveFirst();
	void MoveLast();

	bool IsHeaderNode();

	void Clear();
	T* ToArray();

	bool Contains(T data);
	
};

template <class T>
LinkedList<T>::LinkedList()
{
	this->init();
}

template <class T>
void LinkedList<T>::init()
{
	this->headerNode = new LinkedListNode<T>();
	this->nowNode = this->headerNode;
	this->headerNode->next = this->headerNode;
	this->headerNode->prev = this->headerNode;
}

// °³Ã¼¸¦ ¸®½ºÆ®ÀÇ Ã¹ ºÎºÐ¿¡ Ãß°¡ÇÕ´Ï´Ù.
template <class T>
void LinkedList<T>::AddFirst(T data)
{
	nowNode = headerNode;
	this->Insert(data);
}

// °³Ã¼¸¦ ¸®½ºÆ®ÀÇ ³¡ ºÎºÐ¿¡ Ãß°¡ÇÕ´Ï´Ù.
template <class T>
void LinkedList<T>::AddLast(T data)
{
	nowNode = headerNode->prev;
	this->Insert(data);
}

// ¸®½ºÆ®¿¡ ½ÇÁ¦·Î Æ÷ÇÔµÈ ¿ä¼ÒÀÇ ¼ö¸¦ °¡Á®¿É´Ï´Ù.
template <class T>
int LinkedList<T>::Count()
{
	LinkedListNode<T>* tempNode;
	int count = 0;
	
	tempNode = headerNode->next;

	while (!tempNode->isEmpty)
	{
		tempNode = tempNode->next;
		count++;
	}
	return count;
}

// ¸®½ºÆ®ÀÇ ÇöÀç ÀÎµ¦½º¿¡ ¿ä¼Ò¸¦ »ðÀÔÇÕ´Ï´Ù.
template <class T>
void LinkedList<T>::Insert(T data)
{
	LinkedListNode<T>* newNode = new LinkedListNode<T>(data);

	newNode->next = nowNode->next;
	newNode->prev = nowNode;

	nowNode->next->prev = newNode;
	nowNode->next = newNode;

	// ÇöÀç³ëµå´Â »õ·Î Ãß°¡ÇÑ ³ëµå
	nowNode = newNode;
}

// ¸®½ºÆ®ÀÇ ÁöÁ¤µÈ ÀÎµ¦½º¿¡ ¿ä¼Ò¸¦ »ðÀÔÇÕ´Ï´Ù.
template <class T>
void LinkedList<T>::Insert(int index, T data)
{
	if (!this->move(index)) throw LinkedListIndexOutOfBoundsException();
	this->Insert(data);
}

// ¸®½ºÆ®ÀÇ ÇöÀç ÀÎµ¦½º¿¡¼­ ¿ä¼Ò¸¦ Á¦°ÅÇÕ´Ï´Ù.
template <class T>
void LinkedList<T>::Remove()
{
	// ÇØ´õ³ëµå´Â ¸øÁö¿ö
	if (nowNode==headerNode) throw LinkedListNoElementException();
	
	nowNode->prev->next = nowNode->next;
	nowNode->next->prev = nowNode->prev;

	// ÇöÀç³ëµå´Â Áö¿î³ð ´ÙÀ½À¸·Î ¼³Á¤
	LinkedListNode<T>* tempNode = nowNode;
	nowNode = nowNode->next;
	
	delete tempNode;
}

// ¸®½ºÆ®ÀÇ ÁöÁ¤ÇÑ ÀÎµ¦½º¿¡¼­ ¿ä¼Ò¸¦ Á¦°ÅÇÕ´Ï´Ù.
template <class T>
void LinkedList<T>::Remove(int index)
{
	if(!this->Move(index)) throw LinkedListIndexOutOfBoundsException();
	this->Remove();
}

// ¸®½ºÆ®ÀÇ ÇöÀç ÀÎµ¦½º¿¡¼­ µ¥ÀÌÅÍ¸¦ °¡Á®¿É´Ï´Ù.
template <class T>
T LinkedList<T>::Get()
{
	return nowNode->data;
}

// ¸®½ºÆ®ÀÇ ÁöÁ¤ÇÑ ÀÎµ¦½º¿¡¼­ µ¥ÀÌÅÍ¸¦ °¡Á®¿É´Ï´Ù.
template <class T>
T LinkedList<T>::Get(int index)
{
	if(!this->Move(index)) throw LinkedListIndexOutOfBoundsException();
	return nowNode->data;
}

// ÀÌÀü ³ëµå·Î ÀÌµ¿ÇÕ´Ï´Ù.
template <class T>
void LinkedList<T>::MovePrev()
{
	nowNode = nowNode->prev;
}

// ´ÙÀ½ ³ëµå·Î ÀÌµ¿ÇÕ´Ï´Ù.
template <class T>
void LinkedList<T>::MoveNext()
{
	nowNode = nowNode->next;
}

// ¸®½ºÆ®ÀÇ ÁöÁ¤ÇÑ ÀÍµ¦½º·Î À§Ä¡¸¦ ÀÌµ¿ÇÕ´Ï´Ù.
template <class T>
bool LinkedList<T>::Move(int index)
{

	if (this->Count() < index) return false;
	if (index < 0) return false;

	nowNode = headerNode;
	int count = 0;

	while(count < index)
	{
		nowNode = nowNode->next;
		count++;
	}

	return true;
}

// ¸®½ºÆ®ÀÇ Ã¹ ³ëµå·Î ÀÌµ¿ÇÕ´Ï´Ù.
template <class T>
void LinkedList<T>::MoveFirst()
{
	nowNode = headerNode->next;
}

// ¸®½ºÆ®ÀÇ ¸¶Áö¸· ³ëµå·Î ÀÌµ¿ÇÕ´Ï´Ù.
template <class T>
void LinkedList<T>::MoveLast()
{
	nowNode = headerNode->prev;
}

// ¸®½ºÆ®ÀÇ ÇöÀç ÀÎµ¦½º°¡ ÇØ´õ³ëµåÀÇ ÀÎµ¦½ºÀÎÁö È®ÀÎÇÕ´Ï´Ù.
template <class T>
bool LinkedList<T>::IsHeaderNode()
{
	if (nowNode==headerNode) return true;
	return false;
}

// ¸®½ºÆ®¿¡¼­ ¿ä¼Ò¸¦ ¸ðµÎ Á¦°ÅÇÕ´Ï´Ù.
template <class T>
void LinkedList<T>::Clear()
{
	this->MoveFirst();
	while(nowNode!=headerNode) this->Remove();
}

// ¸®½ºÆ®ÀÇ ¿ä¼Ò¸¦ »õ ¹è¿­¿¡ º¹»çÇÕ´Ï´Ù.
template <class T>
T* LinkedList<T>::ToArray()
{
	
	if (this->Count()==0) throw LinkedListNoElementException();

	T* result = new T[this->Count()];
	LinkedListNode<T>* tmpNode = nowNode;
	int count = 0;

	this->MoveFirst();
	while(!nowNode->isEmpty)
	{
		result[count++] = nowNode->data;
		this->MoveNext();
	}

	// ³ëµå À§Ä¡ º¹±¸
	nowNode = tmpNode;

	return result;
}

// ¿ä¼Ò°¡ ¸®½ºÆ®¿¡ ÀÖ´ÂÁö ¿©ºÎ¸¦ È®ÀÎÇÕ´Ï´Ù.
template <class T>
bool LinkedList<T>::Contains(T data)
{
	// ÇöÀç À§Ä¡ ±×³É ±â·ÏÇØ ³õ°í.
	LinkedListNode<T> *tmpNode = nowNode;
	this->MoveFirst();

	while(!this->IsHeaderNode())
	{
		if (this->Get()==data)
		{
			// ³ëµåÀ§Ä¡ º¹±¸
			nowNode = tmpNode;
			return true;
		}
		this->MoveNext();
	}

	// ³ëµåÀ§Ä¡ º¹±¸
	nowNode = tmpNode;
	return false;

}

#endif