#include "stdafx.h"
#include "acp_base.h"
#ifdef ACTIVE_EDITOR

#include "itf/CPAWData.hpp"
#pragma warning(disable:4355)
CPA_WalkDataMgr::CPA_WalkDataMgr():WalkData(this, NULL) {

	for (iVoid=0; iVoid<MAX_WALKDATA_ELEMENT; iVoid++) {
		aWD [iVoid].ItsData = NULL;
		aWD [iVoid].backnext = NO_ID;
		aWD [iVoid].backprev = NO_ID;
		aWD [iVoid].next = NO_ID;
		aWD [iVoid].prev = NO_ID;
	}
	iVoid = 0;
	iCount = 0;
}

CPA_WalkDataMgr::~CPA_WalkDataMgr() {
	int i;
	for (i=iCurrent; i!=NO_ID; i=aWD[i].backprev) {
		delete ((WalkData *)aWD[i].ItsData);
	}
}

void CPA_WalkDataMgr::InsertElement (CPA_DLLBase *pDLL, void *ItsData) {
	GoElement (pDLL);
	WalkData *pWDToInsertTo = (WalkData *)aWD[iCurrent].ItsData;
#ifdef CHECK	// check if occurence already exists.
	if (!pWDToInsertTo->CheckAndGoElement (ItsData)) {
		pWDToInsertTo->InsertVoidElement (ItsData);
		UpdateIVoid();
	}
#else 
	pWDToInsertTo->InsertVoidElement (ItsData);
	UpdateIVoid();
#endif
}

void CPA_WalkDataMgr::ActiveElement (CPA_DLLBase *pDLL) {
	GoElement (pDLL);
	ActiveCurrent();
}	

/******************** private *******************/

void CPA_WalkDataMgr::UpdateAll () {
	int i;
	for (i=iCurrent; i!=NO_ID; i=aWD[i].backprev) {
		((WalkData *)aWD[i].ItsData)->UpdateList();
	}
}

void CPA_WalkDataMgr::UpdateIVoid() {
	int i, j;
	WalkData *pWD;

	if (iCount!=MAX_WALKDATA_ELEMENT-1) {
		iVoid = ++iCount;
	}
	else {// take an element already used
		i=iLastUsed;
		do {
			pWD = (WalkData *)aWD[i].ItsData;
			i=aWD[i].next;
		}
		while (pWD->iCurrent==NO_ID); 
		j = pWD->iLastUsed;
		iVoid = j;
		
		// tell DLL that the data is no longer of use.
		pWD->MyDLL->fn_vEraseWalkDataStructure (aWD[j].ItsData);

		// update list
		if (j == pWD->iCurrent) // this WD had only one element
			pWD->iCurrent = NO_ID;
		else { // update the element linked to the one we took
			pWD->iLastUsed = aWD[j].next;
			aWD[pWD->iLastUsed].prev = NO_ID;
			aWD[pWD->iLastUsed].backprev = NO_ID;
		}
		// clean new Void element (for safety)
		aWD[j].next = aWD[j].prev = NO_ID;
		aWD[j].backnext = aWD[j].backprev = NO_ID;
	}
}
	
int CPA_WalkDataMgr::FindOrCreateWalkData (CPA_DLLBase *pDLL) {
	int i, iInsertNode;

	for (i=iCurrent; i!=NO_ID; i=aWD[i].backprev) {
		if (((WalkData *)aWD[i].ItsData)->MyDLL == pDLL) {
			iInsertNode = i;
			break;
		}
	}

	if (i==NO_ID) {	// New Node
		iInsertNode = iVoid;
		InsertVoidElement((void *)new WalkData (this, pDLL));
		UpdateIVoid();
		}
	return (iInsertNode);
}
	
void CPA_WalkDataMgr::GoElement (CPA_DLLBase *pDLL) {
	int iNew;
	UpdateAll ();
	iNew = FindOrCreateWalkData (pDLL);
	if (iNew != iCurrent) Go (iNew);
	UpdateList ();
}

inline void CPA_WalkDataMgr::ActiveCurrent() {
	((WalkData *)aWD[iCurrent].ItsData)->BackActive();
}

void CPA_WalkDataMgr::DBackward() {
	if (iCurrent!=NO_ID)
		if (((WalkData *)aWD[iCurrent].ItsData)->Backward())
			ActiveCurrent();
}

void CPA_WalkDataMgr::DForward() {
	if (iCurrent!=NO_ID)
		if (((WalkData *)aWD[iCurrent].ItsData)->Forward())
			ActiveCurrent();
}

void CPA_WalkDataMgr::EBackward() {
	if (Backward ())
		ActiveCurrent();
}
	
void CPA_WalkDataMgr::EForward() {
	if (Forward ())
		ActiveCurrent();
}

/*************************   WalkData   ************************/

WalkData::WalkData(CPA_WalkDataMgr *aWDM, CPA_DLLBase *pDLL) {
	MyWDM = aWDM;
	aWD = aWDM->aWD;	// ne changera pas
	iCurrent = NO_ID;
	iFirst = NO_ID;
	iLast = NO_ID;
	iLastUsed = NO_ID;
	MyDLL = pDLL;
}

inline void WalkData::ABLink (int a, int b) {aWD[a].next = b; aWD[b].prev = a;BLink(a,b);}
inline void WalkData::BLink (int a, int b) {aWD[a].backnext = b; aWD[b].backprev = a;}

void WalkData::UpdateList(){
	int i;
	if ((iCurrent==NO_ID) || (iLast==iCurrent)) return;
	
	for (i=iLast; i!=NO_ID; i=aWD[i].backnext) {	// updatelist
		aWD[i].prev = aWD[i].backprev;
		aWD[i].next = aWD[i].backnext;
		};
	iLast = iCurrent;
	}

int WalkData::CheckAndGoElement (void *ItsData) {
	int i;
	for (i=iCurrent; i!=NO_ID; i=aWD[i].prev) {
		if ((aWD[i].ItsData == ItsData) && (aWD[i].ItsData == ItsData)) {
			if (i!=iCurrent) Go(i);
			UpdateList();
			return (TRUE);
		}
	}
	return (FALSE);
}

void WalkData::InsertVoidElement (void *ItsData) {
	UpdateList();

	iLast = iFirst = MyWDM->iVoid;

	if (iCurrent!=NO_ID)
		ABLink (iCurrent, iFirst);
	else {
		iLastUsed = iFirst;
		aWD[iFirst].prev = NO_ID;
		aWD[iFirst].backprev = NO_ID;
	}
	aWD[iFirst].aWalkData = this;
	aWD[iFirst].ItsData = ItsData;
	aWD[iFirst].next = NO_ID;
	aWD[iFirst].backnext = NO_ID;

	iCurrent = iFirst;

}

int WalkData::Backward() {
	int iNew;
	
	if (iCurrent==NO_ID) return (FALSE);

	iNew = aWD[iCurrent].prev;
	if (iNew == NO_ID) return (FALSE);
	
	Go (iNew);	
	return (TRUE);
}
	
int WalkData::Forward() {
	int iNew;

	if (iCurrent==NO_ID) return (FALSE);

	iNew = aWD[iCurrent].next;
	if (iNew == NO_ID) return (FALSE);

	Go (iNew);	
	return (TRUE);
}

void WalkData::Go(int iNew) {	// iNew must not be iCurrent nor NO_ID
	int i, j, k;

	j = aWD[iNew].backprev;
	k = aWD[iNew].backnext;
	if (j!=NO_ID)
		BLink (j, k);
	else {
		aWD[k].backprev = NO_ID;
		if (iLast==iNew) iLast = k;
		else j=k;	// to update iLast correctly
	}
	if (iLastUsed==iNew) iLastUsed = k;

	BLink (iCurrent, iNew);
	aWD[iNew].backnext = NO_ID;
	iCurrent = iNew;

	// update iLast if needed
	for (i=j; (i!=iLast) && (i!=NO_ID); i=aWD[i].backnext);
	if (i==iLast) iLast = j;
}

void WalkData::BackActive () {
	if (iCurrent==NO_ID)
		MyDLL->fn_vBackActiveMe (NULL);
	else
		MyDLL->fn_vBackActiveMe (aWD[iCurrent].ItsData);
}

#endif	
	
