/* Copyright (c) <2003-2019> <Julio Jerez, Newton Game Dynamics>
* 
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* 
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 
* 3. This notice may not be removed or altered from any source distribution.
*/

#ifndef __dgPathFinder__
#define __dgPathFinder__

#include "dgStdafx.h"
#include "dgTree.h"
#include "dgHeap.h"

template<class NODEID, class COST> class dgPathFinder;


#define DG_MAX_PATH_ENUMERATION_CHILDREN	128

template<class NODEID, class COST>
class dgPathNode
{
	friend class dgPathFinder<NODEID, COST>;
	NODEID m_Id;
	COST m_NodeCostToGoal;
	COST m_NodeCostFromSource;
	dgPathNode* m_Next;

	public:
	dgPathNode();
	NODEID GetId() const;
	const dgPathNode* GetNext() const;
	const dgPathNode* GetParent() const;
};

template<class NODEID, class COST>
class dgPathCloseList: public dgTree<dgPathNode<NODEID, COST>, NODEID>
{
	protected:
	dgPathCloseList(): dgTree<dgPathNode<NODEID, COST>, NODEID>() {}
};


template<class NODEID, class COST>
class dgPathOpenHeap: public dgUpHeap<typename dgPathCloseList<NODEID, COST>::dgTreeNode*, COST>
{
	protected:
	dgPathOpenHeap(dgInt32 maxElements)
		:dgUpHeap<typename dgPathCloseList<NODEID, COST>::dgTreeNode*, COST>(maxElements)
	{
	}

	friend class dgPathFinder<NODEID, COST>;
};


template<class NODEID, class COST>
class dgPathFinder: public dgPathCloseList<NODEID, COST>
{
	dgInt32 maxChildren;

	public:
	dgPathFinder(dgInt32 maxElementsInOpenList, dgInt32 nodeMaxChildren);
	virtual ~dgPathFinder();

	virtual const dgPathNode<NODEID, COST>* CalCulatePath (NODEID source, NODEID goal);

	// this funtions must be overloaded by the user
	virtual COST GetCostFromParent(const dgPathNode<NODEID, COST>& node) const;
	virtual COST GetEstimatedCostToGoal(NODEID id) const;
	virtual dgInt32 EnumerateChildren(NODEID parent, NODEID array[]) const;

	dgPathOpenHeap<NODEID, COST> m_openList;
};


template<class NODEID, class COST>
dgPathNode<NODEID, COST>::dgPathNode ()
{
}

template<class NODEID, class COST>
NODEID dgPathNode<NODEID, COST>::GetId () const
{
	return m_Id;
}


template<class NODEID, class COST>
const dgPathNode<NODEID, COST>* dgPathNode<NODEID, COST>::GetNext() const
{
	return m_Next;
}


template<class NODEID, class COST>
const dgPathNode<NODEID, COST>* dgPathNode<NODEID, COST>::GetParent() const
{
	return m_Next;
}


template<class NODEID, class COST>
dgPathFinder<NODEID, COST>::dgPathFinder(
	dgInt32 maxElementsInOpenList, 
	dgInt32 nodeMaxChildren)
	:dgPathCloseList<NODEID, COST>(),
	 m_openList(maxElementsInOpenList)
{
	maxChildren	= nodeMaxChildren;
}


template<class NODEID, class COST>
dgPathFinder<NODEID, COST>::~dgPathFinder()
{
}

template<class NODEID, class COST>
const dgPathNode<NODEID, COST>* dgPathFinder<NODEID, COST>::CalCulatePath (NODEID source, NODEID goal)
{
	dgInt32 count;
	dgInt32 heapMaxCount;
	dgPathNode<NODEID, COST> cell;
	dgPathNode<NODEID, COST>* next;
	dgPathNode<NODEID, COST>* prev;
	dgPathNode<NODEID, COST>* link;
	typename dgPathCloseList<NODEID, COST>::dgTreeNode* node;
	NODEID idArray[DG_MAX_PATH_ENUMERATION_CHILDREN];

	dgPathCloseList<NODEID, COST>& close = *this;

	m_openList.Flush();
	close.RemoveAll();

	cell.m_Id = source;
	cell.m_Next = NULL;
	cell.m_NodeCostFromSource = COST (0);
	cell.m_NodeCostToGoal = GetEstimatedCostToGoal(cell.m_Id);
		  
	node = close.Insert (cell, source);
	
	heapMaxCount = m_openList.GetMaxCount();
	m_openList.Push (node, cell.m_NodeCostFromSource + cell.m_NodeCostToGoal);
	while (m_openList.GetCount()) {
		node = m_openList[0];
		dgPathNode<NODEID, COST>& parent = node->GetInfo();
		if (parent.m_Id == goal) {
			link = &parent;
			next = NULL;
			do {
				prev = link->m_Next;
				link->m_Next = next;
				next = link;
				link = prev;
			} while (link);
			return next;
		}

		m_openList.Pop();
		count = EnumerateChildren(node->GetKey(), idArray);
		cell.m_Next = &parent;
		for (int i = 0; i < count; i ++) {
			bool state;
			cell.m_Id = idArray[i];

			COST newCostFromSource (GetCostFromParent(cell) + parent.m_NodeCostFromSource);
			node = close.Insert (cell, cell.m_Id, state);
			dgPathNode<NODEID, COST>& newCell = node->GetInfo();
			if (state) {
				if (newCell.m_NodeCostFromSource <= newCostFromSource) {
					continue;
				}
				//newCell.m_Next = cell.m_Next;
			}

			newCell.m_NodeCostFromSource = newCostFromSource;
			newCell.m_NodeCostToGoal = GetEstimatedCostToGoal(newCell.m_Id);
			if (m_openList.GetCount() >= heapMaxCount) {
				m_openList.Remove (heapMaxCount);
			}
			m_openList.Push (node, newCell.m_NodeCostFromSource + newCell.m_NodeCostToGoal);
		}
	}
	return NULL;
}

template<class NODEID, class COST>
COST dgPathFinder<NODEID, COST>::GetCostFromParent(const dgPathNode<NODEID, COST>& node) const
{
	return COST (1);
}

template<class NODEID, class COST>
COST dgPathFinder<NODEID, COST>::GetEstimatedCostToGoal(NODEID id) const
{
	return COST (1);
}

template<class NODEID, class COST>
dgInt32 dgPathFinder<NODEID, COST>::EnumerateChildren(NODEID parent, NODEID array[]) const
{
	return 0;
}




#endif



