#include "stdafx.h"


WorldManager2D World2D;


void MTUnloadArea2D(Area2D &area, Ptr user, int thread){ area.Unload(); }
void MTLoadArea2D(Area2D &area, Ptr user, int thread){ area.Build();  }
void MTCreatePhysics2D(Area2D &area, Ptr user, int thread){ area.CreatePhysics(); }
void MTStitch2D(Area2D &area, WorldManager2D &w){ 
	area.Stitch(w.m_grid.get(area.pos + VecI2(1, 0))->data()->hm, w.m_grid.get(area.pos + VecI2(0, -1))->data()->hm);
}//Does not work correctly
WorldManager2D::WorldManager2D(){ changed = false; Active_areas.del(); Cached_areas.del(); Unloading_areas.del();  m_grid.del(); pw.del(); }
WorldManager2D::~WorldManager2D(){ thread_x.del(); Active_areas.del(); Cached_areas.del(); Unloading_areas.del();  m_grid.del(); pw.del(); }
void WorldManager2D::del(){ thread_x.del(); Active_areas.del(); Cached_areas.del(); Unloading_areas.del();  m_grid.del(); pw.del(); }
void WorldManager2D::Create(){
	gr.load("Data/Grass.gfx"); grNrm.load("Data/GrassNrm.gfx");
	dr.load("Data/Dirt.gfx"); drNrm.load("Data/DirtNrm.gfx");
	matA.base_0 = &gr; matA.base_1 = &grNrm; matA.cull = false;
	matB.color.set(BROWN.asVec(), 1); matB.cull = false;
	matC.base_0 = &dr; matC.base_1 = &drNrm; matC.cull = false;
	m_areasize = 64;
	ps.areaSize(AreaSize());
	pw.create(AreaSize());
	actRng = (D.viewRange()/2) / AreaSize();
}


void  WorldManager2D::ActiveRange(int _ActiveRange){
	actRng = _ActiveRange / AreaSize();
	CheckAreas();
}

void WorldManager2D::CheckAreas(){
	for (int x = CurPos.x - actRng - 1; x <= CurPos.x + actRng + 1; x++){
		for (int y = CurPos.y - actRng - 1; y <= CurPos.y + actRng + 1; y++){
			if (Area2D * area = m_grid.get(VecI2(x, y))->data()){
				if (Dist2(area->pos, CurPos) - 1 <= Sqr(actRng) + 5){
					if (area->state == AREA_UNLOADED){
						if (Cached_areas.include(area)){ changed= true; }
					}
				}
			}
		}
	}
}




/******************************************************************************
Thread functions must be of "bool Name(Thread &thread)" format.
They return true when want to continue thread processing, and false when want to stop them.
As input parameter thread functions receive reference to the 'Thread' which calls them.
/******************************************************************************/
bool ThreadFuncUpdateArea2D(Thread &thread_caller)
{
	if (thread_caller.wantToStop())return false;
	
	World2D.CheckAreas();
	if (World2D.changed){
		REPA(World2D.Active_areas){
			if (thread_caller.wantToStop())return false;
			C VecI2 pos = World2D.Active_areas[i]->pos;
			if (Dist2(pos, World2D.CurPos) - 1 > Sqr(World2D.ActiveRange())){
				World2D.Cached_areas.include(World2D.Active_areas[i]);
				World2D.Active_areas.exclude(World2D.Active_areas[i]);
			}
		}
		MultiThreadedCall<Area2D>(World2D.Cached_areas, MTLoadArea2D, NULL, Cpu.threads());
		//MultiThreadedCall<Area2D>(World2D.Cached_areas, MTLoadStitch2D, World2D, Cpu.cores()-1); //Does not work correctly
		FREPA(World2D.Cached_areas){
			if (thread_caller.wantToStop())return false;
			World2D.Cached_areas[i]->Stitch(World2D.m_grid.get(World2D.Cached_areas[i]->pos + VecI2(1, 0))->data()->hm, World2D.m_grid.get(World2D.Cached_areas[i]->pos + VecI2(0, -1))->data()->hm);
		}
		REPA(World2D.Cached_areas){
			if (thread_caller.wantToStop())return false;
			C VecI2 pos = World2D.Cached_areas[i]->pos;
			if (Dist2(pos, World2D.CurPos) - 1 <= Sqr(World2D.ActiveRange())){
				World2D.Active_areas.include(World2D.Cached_areas[i]);
				World2D.Cached_areas.exclude(World2D.Cached_areas[i]);

			}
			if (Dist2(pos, World2D.CurPos) - 1 > Sqr(World2D.ActiveRange()) + 5){
				World2D.Cached_areas[i]->RemovePhysics();
				World2D.Unloading_areas.include(World2D.Cached_areas[i]);
				World2D.Cached_areas.exclude(World2D.Cached_areas[i]);
			}
		}
		MultiThreadedCall<Area2D>(World2D.Unloading_areas, MTUnloadArea2D, NULL, Cpu.threads());
		REPA(World2D.Unloading_areas){
			if (thread_caller.wantToStop())return false;
			C VecI2 pos = World2D.Unloading_areas[i]->pos;
			World2D.Unloading_areas.exclude(World2D.Unloading_areas[i]);
			World2D.m_grid.del(World2D.m_grid.find(pos));
		}

		MultiThreadedCall<Area2D>(World2D.Active_areas, MTCreatePhysics2D, NULL, Cpu.threads() - 1);
		/*REPA(){
			if (thread_caller.wantToStop())return false;
			World2D.Active_areas[i]->CreatePhysics();
			//pw.set(&Active_areas[i]->pm, Active_areas[i]->_xz);
		}*/
		World2D.changed = false;
	}
	return false; // continue processing
}

void WorldManager2D::update(C Vec2 &at){
	CurPos.set(at.x / AreaSize(), at.y / AreaSize());
	if (CurPos.x<0)CurPos.x -= 1;
	if (CurPos.y<0)CurPos.y -= 1;
	Physics.startSimulation();
	Physics.stopSimulation();

	World2D.CheckAreas();
	if (World2D.changed){
		REPA(World2D.Active_areas){
			C VecI2 pos = World2D.Active_areas[i]->pos;
			if (Dist2(pos, World2D.CurPos) - 1 > Sqr(World2D.ActiveRange())){
				World2D.Cached_areas.include(World2D.Active_areas[i]);
				World2D.Active_areas.exclude(World2D.Active_areas[i]);
			}
		}
		MultiThreadedCall<Area2D>(World2D.Cached_areas, MTLoadArea2D, NULL, Cpu.threads()-1);
		FREPA(World2D.Cached_areas){
			World2D.Cached_areas[i]->Stitch(World2D.m_grid.get(World2D.Cached_areas[i]->pos + VecI2(1, 0))->data()->hm, World2D.m_grid.get(World2D.Cached_areas[i]->pos + VecI2(0, -1))->data()->hm);
		}
		REPA(World2D.Cached_areas){
			C VecI2 pos = World2D.Cached_areas[i]->pos;
			if (Dist2(pos, World2D.CurPos) - 1 <= Sqr(World2D.ActiveRange())){
				World2D.Active_areas.include(World2D.Cached_areas[i]);
				World2D.Cached_areas.exclude(World2D.Cached_areas[i]);

			}
			if (Dist2(pos, World2D.CurPos) - 1 > Sqr(World2D.ActiveRange()) + 5){
				World2D.Cached_areas[i]->RemovePhysics();
				World2D.Unloading_areas.include(World2D.Cached_areas[i]);
				World2D.Cached_areas.exclude(World2D.Cached_areas[i]);
			}
		}
		MultiThreadedCall<Area2D>(World2D.Unloading_areas, MTUnloadArea2D, NULL, Cpu.threads()-1);
		REPA(World2D.Unloading_areas){
			C VecI2 pos = World2D.Unloading_areas[i]->pos;
			World2D.Unloading_areas.exclude(World2D.Unloading_areas[i]);
			World2D.m_grid.del(World2D.m_grid.find(pos));
		}

		MultiThreadedCall<Area2D>(World2D.Active_areas, MTCreatePhysics2D, NULL, Cpu.threads() - 1);
		//REPA(){
		//if (thread_caller.wantToStop())return false;
		//pw.set(&Active_areas[i]->pm, Active_areas[i]->_xz);
		//}
		World2D.changed = false;
	}

	/*if (!thread_x.active()){
		thread_x.create(ThreadFuncUpdateArea2D, 0, 0, 100); // thread_x  will use 'ThreadFunc' 
	}*/
	if (Time.appTime() > m_last_update){
		m_last_update = Time.appTime() + 0.33;
		REPAO(Active_areas)->update();
	}
}



Area2D *WorldManager2D::GetArea(C VecI &v){ if (Cell<Area2D> *area = m_grid.find(v.xz())){ return area->data(); }return nullptr; }


void WorldManager2D::Draw(){
	Flt lod_s = D.lodFactorShadow(); D.lodFactorShadow(1);
	REPAO(Active_areas)->Draw(); // draw mesh
	REPAO(Cached_areas)->Draw(); // draw mesh
	D.lodFactorShadow(lod_s);

	D.text(0, 0.8, S + "Active Areas: " + Active_areas.elms());
	D.text(0, 0.7, S + "Cached Areas: " + Cached_areas.elms());
	D.text(0, 0.6, S + "Unloading Areas: " + Unloading_areas.elms());
}


void WorldManager2D::Load(C Str &world){}