#ifndef GENERIC_LIST
#define GENERIC_LIST

/* 
Funciones para LISTAS: 

	void Delete()
	void Instance(List<T> &l)
	void Rewind(void)
	void Forward(void)
	void Next(void)
	void Prev(void)
	T *GetObj(void)
	LLink<T> *GetPos(void)
	bool EmptyP()
	bool EndP()
	bool LastP()
	bool BeginP()
	void Insert(T *o)
	void Add(T *o)
	void AddAfter(LLink<T> *pos,T *o)
	bool Iterate(T *&o)
	T *ExtractIni(void)
	T *Extract(void)
	bool MemberP(T *o)
	T *MemberGet(T *o)
	bool MemberRefP(T *o)
	int Length()
	void Copy(List l)
	bool DeleteElement(T *o)
	T *GetRandom(void)
	void SetNoOriginal(void)
	void SetOriginal(void)


	bool operator==(List<T> &l)
	T *operator[](int index)
*/ 

template <class T> class LLink {
public:
	LLink<T>(T *o,LLink<T> *n=NULL) {
		obj=o;next=n;
	};
	~LLink<T>() {delete obj;
				 if (next!=NULL) delete next;};
	inline LLink<T> *Getnext() {return next;};
	inline void Setnext(LLink<T> *n) {next=n;};
	inline T *GetObj() {return obj;};
	inline void Setobj(T *o) {obj=o;};

	void Anade(T *o) {
		if (next==NULL) {
			LLink<T> *node=new LLink<T>(o);
			next=node;
		} else {
			next->Anade(o);	
		}
		};

private:
	T *obj;
	LLink<T> *next;
};

template <class T> class List {
public:
	List<T>() {list=NULL;act=NULL;top=NULL;original=true;};
	~List<T>() {
		if (original) {
			delete list;
		} /* if */ 
	};
	List<T>(List<T> &l) {list=l.list;act=list;top=l.top;original=false;};

	void Delete() {
		if (original) {
			delete list;
		} /* if */ 
		list=NULL;
		act=NULL;
		top=NULL;
	};

	void Instance(List<T> &l) {list=l.list;act=list;top=l.top;original=false;};
	void Rewind(void) {act=list;};
	void Forward(void) {act=top;};
	void Next(void) {
		if (act!=NULL) act=act->Getnext();
	};

	void Prev(void) {
		LLink<T> *tmp;

		if (act!=list) {
			tmp=list;
			while(tmp->Getnext()!=act) tmp=tmp->Getnext();
			act=tmp;
		} /* if */ 
	};

	T *GetObj(void) {return act->GetObj();};
	LLink<T> *GetPos(void) {return act;};
	bool EmptyP() {return list==NULL;};
	bool EndP() {return act==NULL;};
	bool LastP() {return act==top;};
	bool BeginP() {return act==list;};

	void Insert(T *o) {
		if (list==NULL) {
			list=new LLink<T>(o);
			top=list;
		} else {
			list=new LLink<T>(o,list);
		} /* if */ 
	};

	void Add(T *o) {
		if (list==NULL) {
			list=new LLink<T>(o);
			top=list;
		} else {
			top->Anade(o);
			top=top->Getnext();
		} /* if */ 
	};

	void AddAfter(LLink<T> *pos,T *o)
	{
		if (pos==NULL) {
			if (list==NULL) {
				list=new LLink<T>(o);
				top=list;
			} else {
				list=new LLink<T>(o,list);
			} /* if */ 
		} else {
			LLink<T> *nl=new LLink<T>(o);
		
			nl->Setnext(pos->Getnext());
			pos->Setnext(nl);
			if (nl->Getnext()==NULL) top=nl;
		} /* if */ 
	} /* AddAfter */ 

	T *operator[](int index) {
		LLink<T> *tmp=list;
		while(tmp!=NULL && index>0) {
			tmp=tmp->Getnext();
			index--;
		} /* while */ 
		if (tmp==NULL) throw;
		return tmp->GetObj();
	};

	bool Iterate(T *&o) {
		if (EndP()) return false;
		o=act->GetObj();
		act=act->Getnext();
		return true;
	} /* Iterate */ 

	T *ExtractIni(void) {
		LLink<T> *tmp;
		T *o;

		if (list==NULL) return NULL;
		o=list->GetObj();
		tmp=list;
		list=list->Getnext();
		tmp->Setnext(NULL);
		if (act==tmp) act=list;
		if (top==act) top=NULL;
		tmp->Setobj(NULL);
		delete tmp;
		return o;
	} /* ExtractIni */ 

	T *Extract(void) {
		LLink<T> *tmp,*tmp2=NULL;
		T *o;

		if (list==NULL) return NULL;
		tmp=list;
		while(tmp->Getnext()!=NULL) {
			tmp2=tmp;
			tmp=tmp->Getnext();
		} /* while */ 
		o=tmp->GetObj();
		if (tmp2==NULL) {
			list=NULL;
			top=NULL;
			act=NULL;
		} else {
			tmp2->Setnext(NULL);
			top=tmp2;
		} /* if */ 

		if (act==tmp) act=top;
		tmp->Setobj(NULL);
		delete tmp;
		return o;
	} /* Extract */ 

	bool MemberP(T *o) {
		LLink<T> *tmp;
		tmp=list;
		while(tmp!=NULL) {
			if (*(tmp->GetObj())==*o) return true;
			tmp=tmp->Getnext();
		} /* while */ 
		return false;
	} /* MemberP */ 

	T *MemberGet(T *o) {
		LLink<T> *tmp;
		tmp=list;
		while(tmp!=NULL) {
			if (*(tmp->GetObj())==*o) return tmp->GetObj();
			tmp=tmp->Getnext();
		} /* while */ 
		return NULL;
	} /* MemberGet */ 
	
	bool MemberRefP(T *o) {
		LLink<T> *tmp;
		tmp=list;
		while(tmp!=NULL) {
			if (tmp->GetObj()==o) return true;
			tmp=tmp->Getnext();
		} /* while */ 
		return false;
	} /* MemberRefP */ 

	int Length() {
		LLink<T> *tmp;
		int count=0;

		tmp=list;
		while(tmp!=NULL) {
			tmp=tmp->Getnext();
			count++;
		} /* while */ 
		return count;
	};

	void Copy(List l) {
		T *o;
		Delete();
		original=true;
		l.Rewind();
		while(l.Iterate(o)) {
			o=new T(*o);
			Add(o);
		} /* while */ 
	} /* Copy */ 

	bool DeleteElement(T *o)
	{
		LLink<T> *tmp1,*tmp2;

		tmp1=list;
		tmp2=NULL;
		while(tmp1!=NULL && tmp1->GetObj()!=o) {
			tmp2=tmp1;
			tmp1=tmp1->Getnext();
		} /* while */ 

		if (tmp1!=NULL) {
			if (tmp2==NULL) {
				/* Eliminar el primer elemento de la lista: */ 
				list=list->Getnext();
				tmp1->Setnext(NULL);
				if (act==tmp1) act=list;
				tmp1->Setobj(NULL);
				delete tmp1;
			} else {
				/* Eliminar un elemento intermedio: */ 
				tmp2->Setnext(tmp1->Getnext());
				if (act==tmp1) act=tmp1->Getnext();
				if (top==tmp1) top=tmp2;
				tmp1->Setnext(NULL);
				tmp1->Setobj(NULL);
				delete tmp1;
			} /* if */ 
			return true;
		} else {
			return false;
		} /* if */ 

	} /* DeleteOne */ 

	T *GetRandom(void) {
		int i,l=Length();
		i=((rand()*l)/RAND_MAX);
		if (i==l) i=l-1;

		return operator[](i);
	} /* GetRandom */ 

	bool operator==(List<T> &l) {
		LLink<T> *tmp1,*tmp2;

		tmp1=list;
		tmp2=l.list;
		while(tmp1!=NULL && tmp2!=NULL) {
			if (!((*(tmp1->GetObj()))==(*(tmp2->GetObj())))) return false;
			tmp1=tmp1->Getnext();
			tmp2=tmp2->Getnext();
		} /* while */ 
		return tmp1==tmp2;
	} /* == */ 

	void SetNoOriginal(void) {original=false;}
	void SetOriginal(void) {original=true;}

private:
	bool original;
	LLink<T> *list,*top;
	LLink<T> *act;
};



#endif
