// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


#include "nel/misc/types_nl.h"
#include "nel/misc/debug.h"
#include "nel/misc/path.h"
#include "nel/misc/common.h"
#include "nel/misc/file.h"
#include "nel/misc/time_nl.h"
#include "nel/misc/polygon.h"
#include "nel/misc/smart_ptr.h"

#include "nel/3d/scene_group.h"
#include "nel/3d/transform_shape.h"
#include "nel/3d/water_model.h"
#include "nel/3d/water_shape.h"
#include "nel/3d/quad_grid.h"

#include "build_rbank.h"
#include "build_surf.h"

#include "surface_splitter.h"

#include "nel/pacs/global_retriever.h"
#include "nel/pacs/retriever_bank.h"
#include "nel/pacs/surface_quad.h"
#include "nel/pacs/local_retriever.h"
#include "nel/pacs/retriever_instance.h"
#include "nel/pacs/chain.h"
#include "nel/pacs/collision_mesh_build.h"

#include <string>
#include <deque>
#include <map>

using namespace std;
using namespace NLMISC;
using namespace NL3D;


class CIGBox
{
public:
	CIGBox() {}
	CIGBox(const string &name, const CAABBox &bbox) : Name(name), BBox(bbox) {}
	string			Name;
	CAABBox			BBox;
	void			serial(NLMISC::IStream &f) { f.serial(Name, BBox); }
};

/*
string getZoneNameById(uint16 id)
{
	uint	x = id%256;
	uint	y = id/256;

	char ych[32];
	sprintf(ych,"%d_%c%c", y+1, 'A'+x/26, 'A'+x%26);
	return string(ych);
}
*/
string getZoneNameByCoord(float x, float y)
{
	const float zoneDim = 160.0f;

	float xcount = x/zoneDim;
	float ycount = -y/zoneDim + 1;

	char ych[32];
	sprintf(ych,"%d_%c%c",(sint)ycount, 'A'+(sint)xcount/26, 'A'+(sint)xcount%26);
	return string(ych);
}

CVector	getZoneCenterById(uint16 id)
{
	CAABBox		bbox;
	uint		x, y;
	const float	zdim = 160.0f;

	x = id%256;
	y = id/256;

	return CVector(zdim*((float)x+0.5f), -zdim*((float)y+0.5f), 0.0f);
}

uint32	getIdByCoord(uint x, uint y)
{
	return y*256+x;
}

string	changeExt(string name, const string &ext)
{
	string::iterator	it, last;
	last = name.end();

	for (it=name.begin(); it!=name.end(); ++it)
		if (*it == '.')
			last = it;

	name.erase(last, name.end());
	name.append(".");
	name.append(ext);
	return name;
}



void processAllPasses(string &zoneName)
{
	uint	/*i,*/ j;

	NLPACS::CZoneTessellation		tessellation;
	vector<NLPACS::COrderedChain3f>	fullChains;
	string							name;
	string							filename;

	try
	{
		uint16	zid = getZoneIdByName(zoneName);
		CAABBox	box = getZoneBBoxById(zid);

		CVector		translation = -box.getCenter();
		if (tessellation.setup(zid, 4, translation))
		{
			tessellation.build();

			CAABBox	tbox = tessellation.computeBBox();

			tessellation.compile();
			tessellation.generateBorders(1.0);

			NLPACS::CLocalRetriever	retriever;

			CAABBox	rbbox = tessellation.BestFittingBBox;
			CVector hs = rbbox.getHalfSize();
			hs.z = 10000.0f;
			rbbox.setHalfSize(hs);
			retriever.setBBox(rbbox);
			retriever.setType(NLPACS::CLocalRetriever::Landscape);

			for (j=0; j<tessellation.Surfaces.size(); ++j)
			{
				retriever.addSurface(0,
									 0,
									 0,
									 0,
									 0,
									 tessellation.Surfaces[j].IsUnderWater,
									 tessellation.Surfaces[j].WaterHeight,
									 tessellation.Surfaces[j].ClusterHint,
									 tessellation.Surfaces[j].Center,
									 tessellation.Surfaces[j].HeightQuad,
									 tessellation.Surfaces[j].QuantHeight);

				if (Verbose)
				{
					nlinfo("Added surface %d: water=%d", j, (tessellation.Surfaces[j].IsUnderWater ? 1 : 0));
				}
			}

			for (j=0; j<tessellation.Borders.size(); ++j)
			{
				if (tessellation.Borders[j].Right < -1)
				{
					retriever.addChain(tessellation.Borders[j].Vertices,
									   tessellation.Borders[j].Left,
									   NLPACS::CChain::getDummyBorderChainId());

				}
				else
				{
					retriever.addChain(tessellation.Borders[j].Vertices,
									   tessellation.Borders[j].Left,
									   tessellation.Borders[j].Right);
				}
			}

			retriever.computeLoopsAndTips();

			retriever.findBorderChains();
			retriever.updateChainIds();
			retriever.computeTopologies();

			retriever.computeCollisionChainQuad();

			retriever.setType(NLPACS::CLocalRetriever::Landscape);

			// and save it...

			if (!retriever.checkSurfacesIntegrity(translation))
			{
				nlwarning("retriever '%s' has a surface issue (self covering surface...)", zoneName.c_str());
			}

			COFile	outputRetriever;
			name = changeExt(zoneName, string("lr"));
			filename = OutputPath+name;
			if (Verbose)
				nlinfo("save file %s", filename.c_str());
			outputRetriever.open(filename);
			retriever.serial(outputRetriever);
		}
	}
	catch(const Exception &e)
	{
		printf("%s\n", e.what ());
	}
}


//
//
//
//
//
//

/*
void tessellateAndMoulineZone(string &zoneName)
{
	uint	i, j;

	NLPACS::CZoneTessellation		tessellation;
	vector<NLPACS::COrderedChain3f>	fullChains;
	string							name;
	string							filename;

	try
	{
		uint16	zid = getZoneIdByName(zoneName);
		CAABBox	box = getZoneBBoxById(zid);

		CVector		translation = -box.getCenter();
		if (tessellation.setup(zid, 4, translation))
		{
			tessellation.build();

			CAABBox	tbox = tessellation.computeBBox();

			vector<CIGBox>				boxes;
			try
			{
				if (CFile::fileExists (IGBoxes))
				{
					CIFile		binput(IGBoxes);
					binput.serialCont(boxes);
				}
				else
				{
					nlinfo("WARNING: IG list no found");
				}
			}
			catch (const Exception &) { nlinfo("WARNING: IG list no found"); }

			for (i=0; i<boxes.size(); ++i)
			{
				if (tbox.intersect(boxes[i].BBox))
				{
					try
					{
						// load ig associated to the zone
						string	igname = boxes[i].Name;
						CIFile			monStream(CPath::lookup(igname));
						CInstanceGroup	ig;
						monStream.serial(ig);

						// search in group for water instance
						for (j=0; j<ig._InstancesInfos.size(); ++j)
						{
							string	shapeName = ig._InstancesInfos[j].Name;
							if (CFile::getExtension (shapeName) == "")
								shapeName += ".shape";

							string	shapeNameLookup = CPath::lookup (shapeName, false, false);
							if (!shapeNameLookup.empty())
							{
								CIFile			f;
								if (f.open (shapeNameLookup))
								{
									CShapeStream	shape;
									shape.serial(f);

									CWaterShape	*wshape = dynamic_cast<CWaterShape *>(shape.getShapePointer());
									if (wshape == NULL)
										continue;

									CMatrix	matrix;
									ig.getInstanceMatrix(j, matrix);

									CPolygon			wpoly;
									wshape->getShapeInWorldSpace(wpoly);

									uint	k;
									for (k=0; k<wpoly.Vertices.size(); ++k)
									{
										//wpoly.Vertices[k].z = 0.0f;
										wpoly.Vertices[k] = matrix * wpoly.Vertices[k];
									}

									tessellation.addWaterShape(wpoly);
								}
								else
								{
									nlwarning ("Can't load shape %s", shapeNameLookup.c_str());
								}
							}
						}
					}
					catch (const Exception &e)
					{
						nlwarning("%s", e.what());
					}
				}
			}

			tessellation.compile();
			tessellation.generateBorders(1.0);

			NLPACS::CLocalRetriever	retriever;

			CAABBox	rbbox = tessellation.BestFittingBBox;
			CVector hs = rbbox.getHalfSize();
			hs.z = 10000.0f;
			rbbox.setHalfSize(hs);
			retriever.setBBox(rbbox);
			retriever.setType(NLPACS::CLocalRetriever::Landscape);

			for (j=0; j<(sint)tessellation.Surfaces.size(); ++j)
			{
				retriever.addSurface(0,
									 0,
									 0,
									 0,
									 0,
									 tessellation.Surfaces[j].IsUnderWater,
									 tessellation.Surfaces[j].WaterHeight,
									 tessellation.Surfaces[j].ClusterHint,
									 tessellation.Surfaces[j].Center,
									 tessellation.Surfaces[j].HeightQuad,
									 tessellation.Surfaces[j].QuantHeight);
			}

			for (j=0; j<(sint)tessellation.Borders.size(); ++j)
			{
				if (tessellation.Borders[j].Right < -1)
				{
					retriever.addChain(tessellation.Borders[j].Vertices,
									   tessellation.Borders[j].Left,
									   NLPACS::CChain::getDummyBorderChainId());

				}
				else
				{
					retriever.addChain(tessellation.Borders[j].Vertices,
									   tessellation.Borders[j].Left,
									   tessellation.Borders[j].Right);
				}
			}

			fullChains = retriever.getFullOrderedChains();

			// save raw retriever
			COFile	outputRetriever;
			name = changeExt(zoneName, string("lr"));
			filename = OutputPath+PreprocessDirectory+name;
			if (Verbose)
				nlinfo("save file %s", filename.c_str());
			outputRetriever.open(filename);
			retriever.serial(outputRetriever);

			// save raw chains
			COFile	outputChains;
			name = changeExt(zoneName, string("ochain"));
			filename = OutputPath+name;
			if (Verbose)
				nlinfo("save file %s", filename.c_str());
			outputChains.open(filename);
			outputChains.serialCont(fullChains);
		}
	}
	catch(const Exception &e)
	{
		printf(e.what ());
	}
}




void processRetriever(string &zoneName)
{
	string							name;
	string							filename;

	try
	{
		uint16	zid = getZoneIdByName(zoneName);
		CAABBox	box = getZoneBBoxById(zid);

		NLPACS::CLocalRetriever	retriever;

		// load raw retriever
		CIFile	inputRetriever;
		name = changeExt(zoneName, string("lr"));
		filename = OutputPath+PreprocessDirectory+name;
		if (Verbose)
			nlinfo("load file %s", filename.c_str());

		if (CFile::fileExists(filename))
		{
			inputRetriever.open(filename);
			retriever.serial(inputRetriever);

			// compute the retriever

			retriever.computeLoopsAndTips();

			retriever.findBorderChains();
			retriever.updateChainIds();
			retriever.computeTopologies();

			retriever.computeCollisionChainQuad();

			retriever.setType(NLPACS::CLocalRetriever::Landscape);

			//
			CSurfaceSplitter	splitter;
			//splitter.build(retriever);

			// and save it...

			COFile	outputRetriever;
			name = changeExt(zoneName, string("lr"));
			filename = OutputPath+name;
			if (Verbose)
				nlinfo("save file %s", filename.c_str());
			outputRetriever.open(filename);
			retriever.serial(outputRetriever);
		}
	}
	catch(const Exception &e)
	{
		printf(e.what ());
	}
}
*/




//
class CFaultyChain
{
public:
	uint		Chain;
	CVectorD	Start, End;
	sint		PreviousChain, NextChain;
};

class CReconstructed
{
public:
	CReconstructed() : FrontInstance(-1), FrontChain(-1) {}
	vector<uint>			Chains;
	CVectorD				Start, End;
	sint					FrontInstance, FrontChain;
};

class CFaultyInstance
{
public:
	uint					Instance;
	vector<CFaultyChain>	Chains;
	vector<CReconstructed>	Reconstructed;
};

class CChainRef
{
public:
	uint					Chain, Previous;
	uint					FrontChain;
	uint					BorderId;
	uint					From, To;
};

class CFullChain
{
public:
	uint					Instance;
	vector<CVector>			Vertices;
	vector<CChainRef>		Chains;
};


void	fixFaultyLinks(map<uint, CFaultyInstance> &faultyInstances, 
					   const vector<NLPACS::CRetrieverInstance> &instances,
					   const vector<NLPACS::CLocalRetriever> &retrievers)
{
	map<uint, CFaultyInstance>::iterator	ifi;
	uint									i, j, k, l;

	// first
	// rebuild full chains
	// -- join all chains that are missing a link
	for (ifi=faultyInstances.begin(); ifi!=faultyInstances.end(); ++ifi)
	{
		CFaultyInstance	&inst = (*ifi).second;

		// for each chain, find best matching ending chain
		for (k=0; k<inst.Chains.size(); ++k)
		{
			sint	best = -1;
			double	bestDist = 1.0e10;
			for (l=0; l<inst.Chains.size(); ++l)
			{
				if (l == k || (best != -1 && inst.Chains[best].PreviousChain != -1))
					continue;

				CVectorD	diff = inst.Chains[k].End - inst.Chains[l].Start;
				double		dist = diff.norm();

				if (dist < 0.1 && dist < bestDist)
				{
					best = l;
					bestDist = dist;
				}
			}

			if (best != -1)
			{
				inst.Chains[best].PreviousChain = k;
				inst.Chains[k].NextChain = best;
			}
		}

		//
		for (k=0; k<inst.Chains.size(); ++k)
		{
			if (inst.Chains[k].PreviousChain == -1)
			{
				l = k;
				inst.Reconstructed.push_back(CReconstructed());
				do
				{
					inst.Reconstructed.back().Chains.push_back(l);
				}
				while ((sint)(l=inst.Chains[l].NextChain) != -1);
				inst.Reconstructed.back().Start = inst.Chains[inst.Reconstructed.back().Chains.front()].Start;
				inst.Reconstructed.back().End   = inst.Chains[inst.Reconstructed.back().Chains.back()].End;
			}
		}
	}

	// second
	// match reconstructed chains
	// -- for each reconstructed chain in an instance, find best matching reconstructed chain in neighbour instances
	for (ifi=faultyInstances.begin(); ifi!=faultyInstances.end(); ++ifi)
	{
		CFaultyInstance	&inst = (*ifi).second;

		const NLPACS::CRetrieverInstance	&instance = instances[inst.Instance];
		const NLPACS::CLocalRetriever		&retriever = retrievers[instance.getRetrieverId()];
		vector<sint32>						neighbs = instance.getNeighbors();

		for (i=0; i<neighbs.size(); ++i)
		{
			map<uint, CFaultyInstance>::iterator	ifn = faultyInstances.find(neighbs[i]);
			if (ifn == faultyInstances.end())
				continue;

			CFaultyInstance	&neighb = (*ifn).second;

			for (j=0; j<inst.Reconstructed.size(); ++j)
			{
				if (inst.Reconstructed[j].FrontInstance != -1)
					continue;

				CVectorD		&astart = inst.Reconstructed[j].Start,
								&aend = inst.Reconstructed[j].End;

				const NLPACS::CRetrieverInstance	&ninstance = instances[neighb.Instance];
				const NLPACS::CLocalRetriever		&nretriever = retrievers[ninstance.getRetrieverId()];

				for (k=0; k<neighb.Reconstructed.size(); ++k)
				{
					if (neighb.Reconstructed[k].FrontInstance != -1)
						continue;

					CVectorD	&bstart = neighb.Reconstructed[j].Start,
								&bend = neighb.Reconstructed[j].End;

					if ((astart-bend).norm() < 0.1 && (aend-bstart).norm() < 0.1)
					{
						// ok, found missing match !
						inst.Reconstructed[j].FrontInstance = neighb.Instance;
						inst.Reconstructed[j].FrontChain = k;
						neighb.Reconstructed[k].FrontInstance = inst.Instance;
						neighb.Reconstructed[k].FrontChain = j;

						CFullChain		fci, fcn;
						uint			m;

						CVector			ori = instance.getOrigin(),
										orn = ninstance.getOrigin();

						fci.Instance = inst.Instance;
						fcn.Instance = neighb.Instance;

						// build full chains
						for (l=0; l<inst.Reconstructed[j].Chains.size(); ++l)
						{
							uint								chain = inst.Chains[inst.Reconstructed[j].Chains[l]].Chain;
							NLPACS::CLocalRetriever::CIterator	it(&retriever, chain);

							CChainRef	cr;
							cr.Chain = chain;
							cr.Previous = chain;
							cr.From = (uint)fci.Vertices.size();
							cr.BorderId = NLPACS::CChain::convertBorderChainId(retriever.getChain(chain).getRight());

							while (!it.end())
							{
								fci.Vertices.push_back(it.get3d()+ori);
								++it;
							}

							cr.To = (uint)fci.Vertices.size()-1;

							if (l < inst.Reconstructed[j].Chains.size()-1)
								fci.Vertices.pop_back();

							fci.Chains.push_back(cr);
						}

						for (l=0; l<neighb.Reconstructed[k].Chains.size(); ++l)
						{
							uint								chain = neighb.Chains[neighb.Reconstructed[k].Chains[l]].Chain;
							NLPACS::CLocalRetriever::CIterator	it(&nretriever, chain);

							CChainRef	cr;
							cr.Chain = chain;
							cr.Previous = chain;
							cr.From = (uint)fcn.Vertices.size();
							cr.BorderId = NLPACS::CChain::convertBorderChainId(nretriever.getChain(chain).getRight());

							while (!it.end())
							{
								fcn.Vertices.push_back(it.get3d()+orn);
								++it;
							}

							cr.To = (uint)fcn.Vertices.size()-1;

							if (l < neighb.Reconstructed[k].Chains.size()-1)
								fcn.Vertices.pop_back();

							fcn.Chains.push_back(cr);
						}

						if (fcn.Vertices.size() != fci.Vertices.size())
						{
							nlwarning("Couldn't reconstruct link between %d and %d, mismatching number of vertices", inst.Instance, neighb.Instance);
							break;
						}

						for (l=0; l<fci.Vertices.size(); ++l)
						{
							if ((fci.Vertices[l] - fcn.Vertices[fci.Vertices.size()-1-l]).norm() > 0.2f)
							{
								nlwarning("Couldn't reconstruct link between %d and %d, some vertices don't match", inst.Instance, neighb.Instance);
								break;
							}

							fci.Vertices[l] -= ori;
							fcn.Vertices[fci.Vertices.size()-1-l] -= orn;
						}

						if (l<fci.Vertices.size())
							break;

						uint	newChaini = (uint)retriever.getChains().size(),
								newChainn = (uint)nretriever.getChains().size();

						// save free border ids in order to renumerate them after splits
						vector<uint>	ifreeBorderIds, nfreeBorderIds;
						uint			inextBorderId, nnextBorderId;

						for (l=0; l<fci.Chains.size(); ++l)
							ifreeBorderIds.push_back(fci.Chains[l].BorderId);
						inextBorderId = (uint)retriever.getBorderChains().size();

						for (l=0; l<fcn.Chains.size(); ++l)
							nfreeBorderIds.push_back(fcn.Chains[l].BorderId);
						nnextBorderId = (uint)nretriever.getBorderChains().size();

						// generate splits from first chain on second chain
						for (l=0; l<fci.Chains.size()-1; ++l)
						{
							uint	splitAt = (uint)fci.Vertices.size()-1 - fci.Chains[l].To;

							for (m=(uint)fcn.Chains.size()-1; (sint)m>=0 && fcn.Chains[m].From>splitAt; --m)
								;

							// no split ?
							if ((sint)m < 0 || fcn.Chains[m].From == splitAt)
								continue;

							// insert split in second chain
							fcn.Chains.insert(fcn.Chains.begin()+m+1, fcn.Chains[m]);
							fcn.Chains[m].To = splitAt;
							fcn.Chains[m+1].From = splitAt;
							fcn.Chains[m+1].Chain = newChainn++;
						}

						// generate splits from second chain on first chain
						for (l=0; l<fcn.Chains.size()-1; ++l)
						{
							uint	splitAt = (uint)fcn.Vertices.size()-1 - fcn.Chains[l].To;

							for (m=(uint)fci.Chains.size()-1; (sint)m>=0 && fci.Chains[m].From>splitAt; --m)
								;

							// no split ?
							if ((sint)m < 0 || fci.Chains[m].From == splitAt)
								continue;

							// insert split in first chain
							fci.Chains.insert(fci.Chains.begin()+m+1, fci.Chains[m]);
							fci.Chains[m].To = splitAt;
							fci.Chains[m+1].From = splitAt;
							fci.Chains[m+1].Chain = newChaini++;
						}

						if (fci.Chains.size() != fcn.Chains.size())
						{
							nlwarning("Couldn't reconstruct link between %d and %d, chain splitting failed", inst.Instance, neighb.Instance);
							break;
						}

						// renumerate border ids after splits
						for (l=0; l<fci.Chains.size(); ++l)
						{
							if (!ifreeBorderIds.empty())
							{
								fci.Chains[l].BorderId = ifreeBorderIds.back();
								ifreeBorderIds.pop_back();
							}
							else
							{
								fci.Chains[l].BorderId = inextBorderId++;
							}

							(const_cast<NLPACS::CLocalRetriever&>(retriever)).forceBorderChainId(fci.Chains[l].Chain, fci.Chains[l].BorderId);
						}

						for (l=0; l<fcn.Chains.size(); ++l)
						{
							if (!nfreeBorderIds.empty())
							{
								fcn.Chains[l].BorderId = nfreeBorderIds.back();
								nfreeBorderIds.pop_back();
							}
							else
							{
								fcn.Chains[l].BorderId = nnextBorderId++;
							}

							(const_cast<NLPACS::CLocalRetriever&>(nretriever)).forceBorderChainId(fcn.Chains[l].Chain, fcn.Chains[l].BorderId);
						}

						// insert/replace new chains in instances

						vector<NLPACS::CLocalRetriever::CChainReplacement>	replacement;
						vector<uint>										newIds;

						l=0;
						while (l<fci.Chains.size())
						{
							sint	previous=-1;

							newIds.clear();
							replacement.clear();

							for (; l<fci.Chains.size(); ++l)
							{
								if (previous != -1 && previous != (sint)fci.Chains[l].Previous)
									break;

								previous = fci.Chains[l].Previous;

								NLPACS::CLocalRetriever::CChainReplacement	cr;

								cr.Chain = fci.Chains[l].Chain;
								cr.Left = retriever.getChain(previous).getLeft();
								cr.Right = NLPACS::CChain::convertBorderChainId(fci.Chains[l].BorderId);
								cr.Vertices.clear();
								cr.Vertices.insert(cr.Vertices.begin(),
												   fci.Vertices.begin()+fci.Chains[l].From, 
												   fci.Vertices.begin()+fci.Chains[l].To+1);

								replacement.push_back(cr);
								newIds.push_back(fci.Chains[l].BorderId);
							}

							if (replacement.size() >= 2)
							{
								(const_cast<NLPACS::CLocalRetriever&>(retriever)).replaceChain(previous, replacement);
								(const_cast<NLPACS::CRetrieverInstance&>(instance)).resetBorderChainLinks(newIds);
							}
						}

						l=0;
						while (l<fcn.Chains.size())
						{
							sint	previous=-1;

							newIds.clear();
							replacement.clear();

							for (; l<fcn.Chains.size(); ++l)
							{
								if (previous != -1 && previous != (sint)fcn.Chains[l].Previous)
									break;

								previous = fcn.Chains[l].Previous;

								NLPACS::CLocalRetriever::CChainReplacement	cr;

								cr.Chain = fcn.Chains[l].Chain;
								cr.Left = nretriever.getChain(previous).getLeft();
								cr.Right = NLPACS::CChain::convertBorderChainId(fcn.Chains[l].BorderId);
								cr.Vertices.clear();
								cr.Vertices.insert(cr.Vertices.begin(),
												   fcn.Vertices.begin()+fcn.Chains[l].From, 
												   fcn.Vertices.begin()+fcn.Chains[l].To+1);

								replacement.push_back(cr);
								newIds.push_back(fcn.Chains[l].BorderId);
							}

							if (replacement.size() >= 2)
							{
								(const_cast<NLPACS::CLocalRetriever&>(nretriever)).replaceChain(previous, replacement);
								(const_cast<NLPACS::CRetrieverInstance&>(ninstance)).resetBorderChainLinks(newIds);
							}
						}

						// force links between instances (border chain links)
						for (l=0; l<fci.Chains.size(); ++l)
						{
							m = (uint)fci.Chains.size()-1-l;
							(const_cast<NLPACS::CRetrieverInstance&>(instance)).forceBorderChainLink(fci.Chains[l].BorderId, 
																									 neighb.Instance,
																									 fcn.Chains[m].BorderId,
																									 fcn.Chains[m].Chain,
																									 nretriever.getChain(fcn.Chains[m].Chain).getLeft());
							(const_cast<NLPACS::CRetrieverInstance&>(ninstance)).forceBorderChainLink(fcn.Chains[m].BorderId, 
																									 inst.Instance,
																									 fci.Chains[l].BorderId,
																									 fci.Chains[l].Chain,
																									 retriever.getChain(fci.Chains[l].Chain).getLeft());

							if (Verbose)
								nlinfo("Fixed: link between %d/%d and %d/%d => %d/%d - %d/%d", fci.Instance, fci.Chains[l].Previous, fcn.Instance, fcn.Chains[m].Previous, fci.Instance, fci.Chains[l].Chain, fcn.Instance, fcn.Chains[m].Chain);
						}

						break;
					}
				}
			}
		}

		(const_cast<NLPACS::CLocalRetriever&>(retriever)).computeCollisionChainQuad();
	}
}


void	processGlobalRetriever()
{
	NLPACS::CRetrieverBank		retrieverBank;
	NLPACS::CGlobalRetriever	globalRetriever;

	uint						ULid = getZoneIdByName(GlobalUL),
								DRid = getZoneIdByName(GlobalDR);
	
	CAABBox						ULbbox = getZoneBBoxById(ULid);
	CAABBox						DRbbox = getZoneBBoxById(DRid);
	CAABBox						bbox;

	CVector						vmin, vmax;

	vmin.minof(ULbbox.getMin(), DRbbox.getMin());
	vmax.maxof(ULbbox.getMax(), DRbbox.getMax());
	bbox.setMinMax(vmin, vmax);

	uint						x0 = ULid%256, 
								y0 = ULid/256,
								x1 = DRid%256, 
								y1 = DRid/256;
	
	globalRetriever.setRetrieverBank(&retrieverBank);
	globalRetriever.init();

	uint						x, y;

	if (Verbose)
		nlinfo("make all instances");

	for (y=y0; y<=y1; ++y)
	{
		for (x=x0; x<=x1; ++x)
		{
			try
			{
				string filename = OutputPath+getZoneNameById(x+y*256)+".lr";
				if (CFile::fileExists (filename))
				{
					uint	retrieverId = retrieverBank.addRetriever(filename);
					globalRetriever.makeInstance(retrieverId, 0, getZoneCenterById((uint16)getIdByCoord(x, y))); 
				}
			}
			catch (const Exception &e)
			{
				printf("%s\n", e.what ());
			}
		}
	}

	if (Verbose)
		nlinfo("make all links");
	globalRetriever.makeAllLinks();

//	if (Verbose)
//		nlinfo("clean retriever bank up");
//	retrieverBank.clean();

	map<uint, CFaultyInstance>				faultyInstances;

	const vector<NLPACS::CRetrieverInstance>	&instances = globalRetriever.getInstances();
	const vector<NLPACS::CLocalRetriever>		&retrievers = retrieverBank.getRetrievers();
	uint	i, j;
	uint	totalUnlinked = 0, totalLink = 0;
	for (i=0; i<instances.size(); ++i)
	{
		const vector<NLPACS::CRetrieverInstance::CLink>	&links = instances[i].getBorderChainLinks();
		CVector	pos = instances[i].getBBox().getCenter();
		string	unlinkstr = "instance "+toString(i)+":"+getZoneNameById(getZoneIdByPos(pos))+":";
		bool	unlinkerr = false;

		const NLPACS::CLocalRetriever			&retriever = retrievers[instances[i].getRetrieverId()];

		CFaultyInstance		fi;
		fi.Instance = i;

		for (j=0; j<links.size(); ++j)
		{
			++totalLink;
			if (links[j].Instance == 0xffff)
			{
				unlinkstr += (string(" ")+toString(j));
				++totalUnlinked;
				unlinkerr = true;

				CFaultyChain	fc;

				fc.Chain = retriever.getBorderChain(j);
				fc.Start = retriever.getStartVector(fc.Chain) + instances[i].getOrigin();
				fc.End   = retriever.getStopVector(fc.Chain) + instances[i].getOrigin();
				fc.PreviousChain = -1;
				fc.NextChain = -1;

				fi.Chains.push_back(fc);
			}
		}
		if (unlinkerr)
		{
			if (Verbose)
				nlinfo("unlink: %s", unlinkstr.c_str());
			faultyInstances.insert(std::pair<uint, CFaultyInstance>(i, fi));
		}
	}

	if (Verbose)
		nlinfo("%d are still unlinked (%d links total)", totalUnlinked, totalLink);

	// rebuild full chains
	if (totalUnlinked > 0)
	{
		if (Verbose)
			nlinfo("Fixing faulty links...");
		fixFaultyLinks(faultyInstances, instances, retrievers);

		// recheck

		const vector<NLPACS::CRetrieverInstance>	&instances = globalRetriever.getInstances();
		const vector<NLPACS::CLocalRetriever>		&retrievers = retrieverBank.getRetrievers();
		uint	i, j;
		uint	totalUnlinked = 0, totalLink = 0;
		for (i=0; i<instances.size(); ++i)
		{
			const vector<NLPACS::CRetrieverInstance::CLink>	&links = instances[i].getBorderChainLinks();
			CVector	pos = instances[i].getBBox().getCenter();
			string	unlinkstr = "instance "+toString(i)+":"+getZoneNameById(getZoneIdByPos(pos))+":";
			bool	unlinkerr = false;

			const NLPACS::CLocalRetriever			&retriever = retrievers[instances[i].getRetrieverId()];

			CFaultyInstance		fi;
			fi.Instance = i;

			for (j=0; j<links.size(); ++j)
			{
				++totalLink;
				if (links[j].Instance == 0xffff)
				{
					unlinkstr += (string(" ")+toString(j));
					++totalUnlinked;
					unlinkerr = true;

					CFaultyChain	fc;

					fc.Chain = retriever.getBorderChain(j);
					fc.Start = retriever.getStartVector(fc.Chain) + instances[i].getOrigin();
					fc.End   = retriever.getStopVector(fc.Chain) + instances[i].getOrigin();
					fc.PreviousChain = -1;
					fc.NextChain = -1;

					fi.Chains.push_back(fc);
				}
			}
			if (unlinkerr)
			{
				if (Verbose)
					nlinfo("after fix: unlink: %s", unlinkstr.c_str());
				faultyInstances.insert(std::pair<uint, CFaultyInstance>(i, fi));
			}
		}
	}

	if (Verbose)
		nlinfo("init the quad grid");
	globalRetriever.initQuadGrid();

	string	filename;

	COFile	outputRetriever;
	filename = OutputPath+GlobalRetriever;
	if (Verbose)
		nlinfo("save file %s", filename.c_str());
	outputRetriever.open(filename);
	globalRetriever.serial(outputRetriever);

	COFile	outputBank;
	filename = OutputPath+RetrieverBank;
	if (Verbose)
		nlinfo("save file %s", filename.c_str());
	outputBank.open(filename);
	retrieverBank.serial(outputBank);

	retrieverBank.saveRetrievers(OutputPath, CFile::getFilenameWithoutExtension(RetrieverBank));
}

///

void	updateRetrieverBank()
{
	NLPACS::CRetrieverBank		retrieverBank;

	string	filename;
	filename = OutputPath+RetrieverBank;

	CIFile	inputBank;
	if (Verbose)
		nlinfo("load file %s", filename.c_str());
	inputBank.open(filename);
	retrieverBank.serial(inputBank);
	inputBank.close();

	COFile	outputBank;
	if (Verbose)
		nlinfo("save file %s", filename.c_str());
	outputBank.open(filename);
	retrieverBank.serial(outputBank);
	outputBank.close();
}