#include "stdafx.h"
#include "CartoBaseTopologyManager.h"
#include <GeometryFunction.h>

CartoBaseTopologyManager::CartoBaseTopologyManager(void)
{
	tolerancia = 0.01;
	ncorrecciones = 0;
	terminado = true;
	nthread = 0;
}

CartoBaseTopologyManager::~CartoBaseTopologyManager(void)
{
}

bool CartoBaseTopologyManager::DivideLineVirtual(int ie, int ipun)
{
	CartoBaseEntity* entity = cad->_entities.get(ie);
	if (entity->type != CartoBaseTypes::Line)
		return false;
	CartoBaseLine* lin = cad->_lines.get(entity->id);
	CartoBaseLine nlin;
	CartoBaseEntity nent;

	nent = *entity;
	nlin.ini = lin->ini + ipun;
	nlin.n = lin->n - ipun;
	lin->n = ipun + 1;
	nent.id = cad->add(&nlin);
	
	if (nent.id < 0)
		return false;
	int nn = cad->add(&nent);
	if (nn >= 0)
	{
		referencias[cad->_entities.get(nn)->ref] = entity->ref;
		//cad->_entities.get(nn)->ref = entity->ref;//pisa la referencia (ojo peligorso)
	}
	return (nn >= 0);
}
bool CartoBaseTopologyManager::DivideLine(int ie, int ipun)
{
	CartoBaseEntity* rawEntity = cad->_entities.get(ie);
	CartoBaseInfoEntity entity = cad->getEntity(ie);
	if (!entity.line() )
		return false;
	CartoBaseLine* lin = cad->_lines.get(rawEntity->id);
	linebuff.set(&entity);
	linebuff.ia = rawEntity->ia;
	linebuff.pts.remove(0, ipun);

	lin->n = ipun + 1;
	return cad->add(linebuff);
}

bool CartoBaseTopologyManager::RevisaTopo(int ini, int fin)
{
	bool res = true;
	CartoBaseInfoEntity ent;
	for (int i = ini; i < fin; i++)
	{
		ent=cad->getEntity(i);
		if (!ent.line())
			continue;
		res=revisa(&ent);
		
	}
	if (!res)
	{
		lcorreccines.entro();
		conErrores = false;
		lcorreccines.salgo();

	}
	return res;
}

bool CartoBaseTopologyManager::revisa(CartoBaseInfoEntity* ent)
{
	CartoBaseInfoEntity enta=cad->get(0);
	double* pto1 = ent->getPto(0);
	double* pto2 = ent->getPto(ent->getNumberPtos()-1);
	double* aux;
	while (enta.isValid())
	{
		if (enta.line())
		{
			int n = enta.getNumberPtos()-1;
	
		    for (int ii = 1; ii < n; ii++)
			{
				aux= enta.getPto(ii);
				if (GeometryFunction::Dist2d(aux, pto1) < tolerancia || GeometryFunction::Dist2d(aux, pto2) < tolerancia)
				{
					if (!AddCorreccion(enta.i_entity, ii))
						return false;
				}
			}
		}
		enta = enta.next();
	}

	
	return true;
}


bool CartoBaseTopologyManager::AddCorreccion(int ie, int pto)
{
	lcorreccines.entro();
	ncorrecciones++;
	Cgarray_ord<int, int>* pts;
	if (correcciones.find(ie) == correcciones.end())
	{
		pts = new Cgarray_ord<int, int>();
		pts->f_ord = &comint;
		pts->fcomp = &comint;
	}
	else
	{
		pts = correcciones[ie];
	}
	bool res = (*pts) + pto;
	correcciones[ie] = pts;
	if (!res)
		strcpy(error, "Error al añadir corrección de topologia.");
	    
	lcorreccines.salgo();
	return res;
}
void CartoBaseTopologyManager::AplicaCorrecciones()
{
	if (ncorrecciones <= 0 || conErrores)
		return;
	std::map<int, Cgarray_ord<int, int>*>::iterator elem;
	int ult = -1;
	cad->_entities += ncorrecciones;//pide memoria extra que va a necesitar en total
	bool res = true;
	for (elem = correcciones.begin(); elem != correcciones.end(); ++elem) {
		ult = -1;
		for (int ii=0; ii<elem->second->ind.n; ii++)
		{
			CartoBaseEntity *e=cad->_entities.get(elem->first);
			int pt = *elem->second->get(elem->second->ind.n-1-ii);//divide de mayor a menor
			if (pt != ult)
			{
				res = res && DivideLineVirtual(elem->first, pt);
				ult = pt;
			}

		}
		Cgarray_ord<int, int>* pts = correcciones[elem->first];
		correcciones[elem->first] = NULL;
		delete pts;
	}
	if (!res)
	{
		conErrores = true;
		strcpy(error, "Error alaplicar correcciones de topologia.");
	}
}
bool CartoBaseTopologyManager::revisaTopologia(CartoBase* carto, int numthread)
{
	error[0] = 0;
	nthread = numthread;
	cad = carto;
	terminado = false;
	conErrores = false;
	referencias.clear();
	int ne = cad->getNumberEntities();
	int nt = (int)(ne * 1. / numthread);
	if (!(ne % numthread))
		nt = nt + 1;

	CthRevisaTopologia* th = new CthRevisaTopologia[numthread];
	int ini = 0;
	correcciones.clear();
	ncorrecciones = 0;

	for (int i = 0; i < numthread; i++)
	{
		th[i].ini = i * nt;
		th[i].fin = min(th[i].ini + nt, ne);
		th[i].topo = this;
		th[i].lanza();
	}
	while (nthread > 0)
		Sleep(100);
	while (!terminado)
	{
		Sleep(100);
	}
	//delete th;

	return !conErrores;

}