#ifndef __PLIST_IMPL__H__
#define __PLIST_IMPL__H__

#include "PList.h"

///constructeur par défaut de la PList
template <class T>
PList<T>::PList(){
	intialisationPList();
}

///constructeur de PList
/**	@param el : élement à ajouter à la PList
*/
template <class T>
PList<T>::PList(const T & el){
	intialisationPList();
	pushBack(el);
}

///constructeur de copie de PList
/**	@param liste : PList à copier
 * 	Pour que la copie des éléments de la liste se fasse il faut que l'opérateur = soit définit pour le type de la liste
*/
template <class T>
PList<T>::PList(const PList<T> & liste){
	intialisationPList();
	copyPList(liste);
}

///destructeur de PList
template <class T>
PList<T>::~PList(){
	clear();
}

///Définition de l'opérateur = de PList
/**	@param liste : PList à copier
 * 	@return PList copiée
 * 	Pour que la copie des éléments de la liste se fasse il faut que l'opérateur = soit définit pour le type de la liste
*/
template <class T>
typename PList<T>::PList & PList<T>::operator = (const PList<T> & liste){
	copyPList(liste);
	return *this;
}

///rédéfinition de l'opérateur += pour permettre l'ajout d'un élément de la PList
/**	@param el : élément à ajouter à la PList
 * 	@return PList avec l'élément el ajouter
*/
template <class T>
typename PList<T>::PList & PList<T>::operator += (const T & el){
	pushBack(el);
	return *this;
}

///rédéfinition de l'opérateur += pour permettre l'ajout des éléments d'une autre PList
/**	@param liste : PList d'élément à ajouter à la PList
 * 	@return PList avec l'élément el ajouter
*/
template <class T>
typename PList<T>::PList & PList<T>::operator += (const PList<T> & liste){
	pushBack(liste);
	return *this;
}

///fonction qui permet de normaliser les valeurs de la PList à scal (on divise tout les éléments de la PList par scal)
/**	@param scal : scalaire de normalisation (doit être non nul)
*/
template <class T>
void PList<T>::normalise(const T & scal){
	for(PIterator<T> it(begin()); it != end(); it++){
		*it /= scal;
	}
}


///fonction qui renvoie le premier élément de la PList
/**	@return premier élément de la PList
*/
template <class T>
T PList<T>::front() const{
	return p_handle.p_next->p_data;
}

///fonction qui renvoie le dernier élément de la PList
/**	@return dernier élément de la PList
*/
template <class T>
T PList<T>::back() const{
	return p_handle.p_prev->p_data;
}

///Fonction qui renvoie une copie d'un élément de la liste
/**	@param index : position de l'élément dans la liste
 * 	@return copie de élément de la liste
*/
template <class T>
T PList<T>::getAt(size_t index) const{
	PIterator<T> it(begin());
	it += index;
	return *it;
}

///fonction qui permet d'initialiser un élément de la liste à la position index
/**	@param value : valeur de l'élément
 * 	@param index : position de l'élément dans la liste
*/
template <class T>
void PList<T>::setAt(const T & value, size_t index){
	PIterator<T> it(begin());
	it += index;
	*it = value;
}

///définition de l'opérateur [] pour renvoyer une copie d'un élément de la liste
/**	@param index : position de l'élément dans la liste
 * 	@return copie de élément de la liste
*/
template <class T>
T PList<T>::operator [] (size_t index) const{
	return getAt(index);
}

///définition de l'opérateur [] pour renvoyer une référence d'un élément de la liste
/**	@param index : position de l'élément dans la liste
 * 	@return référence de élément de la liste
*/
template <class T>
T & PList<T>::operator [] (size_t index){
	PIterator<T> it(begin());
	it += index;
	return *it;
}

///fonction qui renvoie un PIterator pointant sur le premier élément de la PList
/**	@return PIterator pointant sur le premier élément de la PList
 * 	Utilisation
 * 	PList<int> liste;
 * 	...
 * 	for(PIterator<int> it = liste.begin(); it != liste.end(); it++){
 * 		...
 * 	}
*/
template <class T>
PIterator<T> PList<T>::begin() const{
	PIterator<T> it(p_handle.p_next);
	return it;
}

///fonction qui renvoie un PIterator pointant sur le dernier élément de la PList
/**	@return PIterator pointant sur le dernier élément de la PList
 * 	Utilisation
 * 	PList<int> liste;
 * 	...
 * 	for(PIterator<int> it = liste.begin(); it != liste.end(); it++){
 * 		...
 * 	}
*/
template <class T>
PIterator<T> PList<T>::end() const{
	PIterator<T> it( (PElement<T>*) &p_handle);
	return it;
}

///fonction qui renvoie un PIterator pointant sur le dernier élément de la PList
/**	@return PIterator pointant sur le dernier élément de la PList
 * 	Utilisation
 * 	PList<int> liste;
 * 	...
 * 	for(PIterator<int> it = liste.rbegin(); it != liste.rend(); it--){
 * 		...
 * 	}
*/
template <class T>
PIterator<T> PList<T>::rbegin() const{
	PIterator<T> it(p_handle.p_prev);
	return it;
}

///fonction qui renvoie un PIterator pointant sur le premier élément de la PList
/**	@return PIterator pointant sur le premier élément de la PList
 * 	Utilisation
 * 	PList<int> liste;
 * 	...
 * 	for(PIterator<int> it = liste.rbegin(); it != liste.rend(); it--){
 * 		...
 * 	}
*/
template <class T>
PIterator<T> PList<T>::rend() const{
	PIterator<T> it((PElement<T>*)&p_handle);
	return it;
}

///focntion qui permet d'ajouter un élément à la fin de la  PList
/**	@param el : élément à ajouter à la fin de la PList
 * 	@return true si la fonction a réussie, false sinon
*/
template <class T>
bool PList<T>::pushBack(const T & el){
	p_size++;
	PElement<T>* element = NULL;
	if(p_handle.p_prev == NULL && p_handle.p_next == NULL){ //si c'est le premier élément c'est facile
		element = new PElement<T>(&p_handle, &p_handle, el);
		if(element == NULL) return false;
		p_handle.p_next = element;
		p_handle.p_prev = element;
	}else{
		element = new PElement<T>(p_handle.p_prev, &p_handle, el);
		if(element == NULL) return false;
		p_handle.p_prev->p_next = element;
		p_handle.p_prev = element;
	}
	return true;
}

///fonction qui permet d'ajouter une PList à la fin d'une autre PList
/**	@param liste : PListe que l'on veut ajouter à la liste courante
 * 	@return true si la fonction a réussie, false sinon
*/
template <class T>
bool PList<T>::pushBack(const PList<T> & liste){
	bool b(true);
	PIterator<T> it(liste.begin());
	while(it != liste.end() && b){
		b = pushBack(*it);
		++it;
	}
	return b;
}

///focntion qui permet d'ajouter un élément au début de la PList
/**	@param el : élément à ajouter au début de la PList
 * 	@return true si la fonction a réussie, false sinon
*/
template <class T>
bool PList<T>::pushFront(const T & el){
	p_size++;
	PElement<T>* element = NULL;
	if(p_handle.p_prev == NULL && p_handle.p_next == NULL){ //si c'est le premier élément c'est facile
		element = new PElement<T>(&p_handle, &p_handle, el);
		if(element == NULL) return false;
		p_handle.p_next = element;
		p_handle.p_prev = element;
	}else{
		element = new PElement<T>(&p_handle, p_handle.p_next, el);
		if(element == NULL) return false;
		p_handle.p_next->p_prev = element;
		p_handle.p_next = element;
	}
	return true;
}

///fonction qui permet d'ajouter une PList au début d'une autre PList
/**	@param liste : PListe que l'on veut ajouter au début courante
 * 	@return true si la fonction a réussie, false sinon
*/
template <class T>
bool PList<T>::pushFront(const PList<T> & liste){
	bool b(true);
	PIterator<T> it(liste.rbegin());
	while(it != liste.rend() && b){
		b = pushFront(*it);
		--it;
	}
	return b;
}

///fonction qui dégomme le dernier élément de la PList
/**	@return true si la fonction a réussie, false si il n'y a plus d'éléments à suprimer
*/
template <class T>
bool PList<T>::popBack(){
	if(p_size == 0) return false;
	if(p_size == 1){ // si on en est au dernier élément
		delete p_handle.p_prev;
		p_handle.p_next = NULL;
		p_handle.p_prev = NULL;
		p_size = 0;
		return false;
	}else{  //on comprend très bien tout ça avec un dessin
		PElement<T>* elADegommer = p_handle.p_prev;
		elADegommer->p_prev->p_next = &p_handle;
		p_handle.p_prev = elADegommer->p_prev;
		//maintenant qu'on a sorti l'élément à dégommer de la liste on peut le suprimer
		delete elADegommer;
		p_size--;
		return true;
	}
}

///fonction qui dégomme le premier élément de la PList
/**	@return true si la fonction a réussie, false si il n'y a plus d'éléments à suprimer
*/
template <class T>
bool PList<T>::popFront(){
	if(p_size == 0) return false;
	if(p_size == 1){ // si on en est au dernier élément
		delete p_handle.p_prev;
		p_handle.p_next = NULL;
		p_handle.p_prev = NULL;
		p_size = 0;
		return false;
	}else{  //on comprend très bien tout ça avec un dessin
		PElement<T>* elADegommer = p_handle.p_next;
		elADegommer->p_next->p_prev = &p_handle;
		p_handle.p_next = elADegommer->p_next;
		//maintenant qu'on a sorti l'élément à dégommer de la liste on peut le suprimer
		delete elADegommer;
		p_size--;
		return true;
	}
}

///fonction qui efface tout le contenu de la liste
/**	Attention : comme l'on dit ceux qui on fait la doc de la std, si le type T est un pointeur, c'est la responsabilité
 * 	de l'utilisateur de libérer sa mémoire, le PList n'a pas signée pour ça
*/
template <class T>
void PList<T>::clear(){
	//tant qu'on peut dégommer des éléments on le fait
	while(popBack());
}


///fonction qui eléve un élément de la PList
/**	@param it : PIterator dont on veut suprimer l'élément
 * 	@return true si il reste encore des élements dans la PList, false sinon
 * 	Une fois l'élément suprimé on replace le PIterator sur l'élement suivant l'élément suprimé
*/
template <class T>
bool PList<T>::erase(PIterator<T> & it){
	if(p_size == 0) return false;
	if(p_size == 1){ // si on en est au dernier élément
		delete p_handle.p_prev;
		p_handle.p_next = NULL;
		p_handle.p_prev = NULL;
		p_size = 0;
		return false;
	}else{  //on comprend très bien tout ça avec un dessin
		PElement<T>* elADegommer = it.p_element;
		elADegommer->p_prev->p_next = elADegommer->p_next;
		elADegommer->p_next->p_prev = elADegommer->p_prev;
		//on replace l'itérator sur l'élément suivant
		it.p_element = elADegommer->p_next;
		//maintenant qu'on a sorti l'élément à dégommer de la liste on peut le suprimer
		delete elADegommer;
		p_size--;
		return true;
	}
}


///fonction qui dégomme l'élément à l'index index
/**	@param index : index de l'élément à suprimer
 * 	@return true si la fonction a réussie, false si il ne reste plus d'élément dans la PList
*/
template <class T>
bool PList<T>::erase(int index){
	if(p_size == 0) return false;
	int i(0);
	PIterator<T> it(begin());
	PIterator<T> itEnd(end());
	while(i != index && it != itEnd){++it;i++;}
	//si i == index c'est que ça a due fonctionner
	if(i == index) return erase(it);
	else return false;
}

///fonction qui renvoie la taille de la PList
/**	@return taille de la PList
*/
template <class T>
size_t PList<T>::size() const{
	return p_size;
}

///Fonction qui converti une PListe en std::list
/**	@return PListe converti en std::list
*/
template <class T>
std::list<T> PList<T>::toStdList() const{
	std::list<T> liste;
	for(PIterator<T> it(begin()); it != end(); it++){
		liste.push_back(*it);
	}
	return liste;
}

///Fonction qui converti une PListe en std::vector
/**	@return PListe converti en std::vector
*/
template <class T>
std::vector<T> PList<T>::toStdVector() const{
	std::vector<T> liste;
	for(PIterator<T> it(begin()); it != end(); it++){
		liste.push_back(*it);
	}
	return liste;
}

///fonction qui initialise la PList
template <class T>
void PList<T>::intialisationPList(){
	p_handle.p_next = NULL;
	p_handle.p_prev = NULL;
	p_size = 0;
}

///fonction qui copie la PList
/**	@param liste : PList que l'on veut copier
 * 	Pour que la copie des éléments de la liste se fasse il faut que l'opérateur = soit définit pour le type de la liste
*/
template <class T>
void PList<T>::copyPList(const PList<T> & liste){
	clear();
	PIterator<T> it(liste.begin());
	PIterator<T> itEnd(liste.end());
	//on parcourt tout les éléments à copier de la liste
	while(it != itEnd){
		pushBack(*it);
		++it;
	}
}

#endif




