/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "binding_helpers.h"
		#include "core/render_data.h"
		#include "core/geometry.h"
		#include "core/iso_surface.h"
		#include "core/resource_factories.h"
		#include "core/graphic_resource_factory.h"
		#include "metafile/nml_object.h"
		#include "gpu/gpu_material.h"

		using namespace GS;
		using namespace GS::Render;
		using namespace GS::Script;
		
//------------------------------------------------------------------------------
SQInteger			GeometrySaveOnItself(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(g, Geometry, typetag_Geometry)		
	__SQ_GETSAFEPTR(f, Core::ResourceFactories, typetag_ResourceFactories)
	__SQ_GETEND

	// get the geo from core
	GS::Core::Geometry * core_geo = f->graphic->LoadGeometry(g->name);
	
	core_geo->flag = g->flag;
	core_geo->lod_distance = g->lod_distance;

	if(g->lod_proxy.IsValid())
		core_geo->lod_proxy = g->lod_proxy->name;
	else
		core_geo->lod_proxy = "";
	
	if(g->shadow_proxy.IsValid())
		core_geo->shadow_proxy = g->shadow_proxy->name;
	else
		core_geo->shadow_proxy = "";
		
	bool r = NML::SaveToFile(*core_geo, core_geo->name);

	__SQ_RETURN
}
//------------------------------------------------------------------------------
SQInteger			GeometryGetShadowProxyNull(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_RETURNBOOL(g->flag.IsSet(Core::Geometry::FlagNullShadowProxy))
}
//------------------------------------------------------------------------------
SQInteger			GeometrySetShadowProxyNull(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(g, Geometry, typetag_Geometry)		
	__SQ_GETBOOL(ShadowProxyNull)
	__SQ_GETEND
	g->flag.Raise(Core::Geometry::FlagNullShadowProxy, asbool(ShadowProxyNull));
	__SQ_RETURN
}
//------------------------------------------------------------------------------
SQInteger			GeometryGetLodNull(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_RETURNBOOL(g->flag.IsSet(Core::Geometry::FlagNullLodProxy))
}
//------------------------------------------------------------------------------
SQInteger			GeometrySetLodNull(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(g, Geometry, typetag_Geometry)		
	__SQ_GETBOOL(LodNull)
	__SQ_GETEND
	g->flag.Raise(Core::Geometry::FlagNullLodProxy, asbool(LodNull));
	__SQ_RETURN
}
//------------------------------------------------------------------------------
SQInteger			GeometryGetLodDistance(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_RETURNFLOAT(g->lod_distance)
}
//------------------------------------------------------------------------------
SQInteger			GeometrySetLodDistance(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(g, Geometry, typetag_Geometry)		
	__SQ_GETFLOAT(n)
	__SQ_GETEND
	g->lod_distance = n;
	__SQ_RETURN

}
//------------------------------------------------------------------------------
SQInteger			GeometryGetLod(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_RETURNSAFEPTR(g->lod_proxy.c_ptr(), typetag_Geometry)

}
//------------------------------------------------------------------------------
SQInteger			GeometrySetLod(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_GETSAFEPTRALLOWNULL(geo_Lod, GS::Render::Geometry, typetag_Geometry)
	__SQ_GETEND
	g->lod_proxy = geo_Lod ? geo_Lod : NULL;
	__SQ_RETURN

}
//------------------------------------------------------------------------------
SQInteger			GeometryGetShadowProxy(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_RETURNSAFEPTR(g->shadow_proxy.c_ptr(), typetag_Geometry)

}
//------------------------------------------------------------------------------
SQInteger			GeometrySetShadowProxy(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_GETSAFEPTRALLOWNULL(geo_shadow_proxy, GS::Render::Geometry, typetag_Geometry)
	__SQ_GETEND
	g->shadow_proxy = geo_shadow_proxy? geo_shadow_proxy : NULL;
	__SQ_RETURN
}

//------------------------------------------------------------------------------
SQInteger			GeometryGetName(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_RETURNSTRING(g->name)
}
SQInteger			GeometryGetMinMax(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_RETURNMINMAX(g->minmax)
}
SQInteger			GeometrySetHidden(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_GETBOOL(hidden)
	__SQ_GETEND
	g->flag.Raise(Core::Geometry::FlagHidden, asbool(hidden));
	__SQ_RETURN
}
SQInteger			GeometryOptimize(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_RETURN
}
SQInteger			GeometryComputeIsoSurface(HSQUIRRELVM vm)
{
	sq_pop(vm, 4);
/*
	__SQ_GETSTART(4)
	__SQ_GETSAFEPTR(Item, nMItem, typetag_Item)
	__SQ_GETINT(nb_metaball)

	if	((nb_metaball > 0) && (Item->GetItemType() == Type_Object))
	{
		// get the geometry, or if it doesn't have it create one
		nSharedPtr <nGeometry> g;
		if(((nMObject *)Item)->GetGeometry().IsNull())
		{
			g = new nGeometry(Item->GetScene().GetEngine());
			((nMObject *)Item)->SetGeometry(g.c_ptr());
		}
		else
			g = ((nMObject *)Item)->GetGeometry();

		nVector* pos_metaball = new nVector[nb_metaball];

		// get the list of metaball
		sq_pushnull(vm);//null iterator
 		for(int i=0; i<nb_metaball; ++i)
		{
			sq_next(vm, __SQ_STACKPOS-1 );
			GetVector(vm, -1, pos_metaball[i]);
			sq_pop(vm,2);
		}
		sq_pop(vm,1); //pops the null iterator

		__SQ_GETUPDATESTACK

		float* value_metaball = new float[nb_metaball];
		sq_pushnull(vm);//null iterator
 		for(int i=0; i<nb_metaball; ++i)
 		{
			sq_next(vm, __SQ_STACKPOS-1 );
			sq_getfloat(vm, -1, &value_metaball[i]);
			sq_pop(vm, 2);
		}
		sq_pop(vm,1); //pops the null iterator

		__SQ_GETEND

		// compute min max from the metaball
		nVector MinGrid(10000000.0f,10000000.0f,10000000.0f);
		nVector MaxGrid(-10000000.0f,-10000000.0f,-10000000.0f);

		for(int i=0; i<nb_metaball; ++i)
		{
			if	(pos_metaball[i].x+value_metaball[i] > MaxGrid.x)	MaxGrid.x = pos_metaball[i].x+value_metaball[i];
			if	(pos_metaball[i].y+value_metaball[i] > MaxGrid.y)	MaxGrid.y = pos_metaball[i].y+value_metaball[i];
			if	(pos_metaball[i].z+value_metaball[i] > MaxGrid.z)	MaxGrid.z = pos_metaball[i].z+value_metaball[i];
			if	(pos_metaball[i].x-value_metaball[i] < MinGrid.x)	MinGrid.x = pos_metaball[i].x-value_metaball[i];
			if	(pos_metaball[i].y-value_metaball[i] < MinGrid.y)	MinGrid.y = pos_metaball[i].y-value_metaball[i];
			if	(pos_metaball[i].z-value_metaball[i] < MinGrid.z)	MinGrid.z = pos_metaball[i].z-value_metaball[i];
		}

 		nIsosurface isosurface(g->GetEngine());
		isosurface.Init(MinGrid, MaxGrid-MinGrid, nVector(90, 90, 90));
 
 		g->Free();
 		isosurface.Triangularize(g.c_ptr(), nb_metaball, pos_metaball, value_metaball);
 		g->material_table.Allocate(1);

 		nMaterial *m = g->material_table[0] = new nMaterial(g->GetEngine());
 		m->renderword |= nMaterial::Render_Smooth;
 		//	m->shader_map = m->AsShaderMap();
 		//g->ComputeVertexToPolygon();
 		// // Update ISO
 		g->render_data = g->GetEngine().GetRenderer().SetupGeometry(g.c_ptr());
 
		_safe_delete_array(value_metaball);
		_safe_delete_array(pos_metaball);
	}
*/
	__SQ_RETURN
}
SQInteger			GeometrySetup(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)
	__LOG_V__ << "GeometrySetup() STUB\n";
	__SQ_RETURN
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
SQInteger			GeometryGetMaterialList(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)
	sq_newarray(vm, 0);

	for (uint n = 0; n < g->material_table.GetCount(); ++n)
	{
		CObject::Push(vm, (void *)g->material_table[n].c_ptr(), typetag_Material);
		sq_arrayappend(vm, -2);
	}
	return 1;
}
SQInteger			GeometryGetMaterialCount(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_RETURNINT(g->material_table.GetCount())
}
SQInteger			GeometryGetMaterial(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_GETSTRING(name)

	Material *m = NULL;
	for	(uint n = 0; n < g->material_table.GetCount(); ++n)
		if	(g->material_table[n]->name == name)
		{
			m = g->material_table[n];
			break;
		}

	__SQ_GETEND
	__SQ_RETURNSAFEPTR(m, typetag_Material)
}
SQInteger			GeometryGetMaterialFromIndex(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_GETINT(index)
	__SQ_GETEND
	__SQ_RETURNSAFEPTR(g->material_table[(int)index].c_ptr(), typetag_Material)
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
SQInteger			GeometrySetMaterial(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(g, Geometry, typetag_Geometry)
	__SQ_GETINT(index)
	__SQ_GETSAFEPTR(m, Material, typetag_Material)
	__SQ_GETEND
	__SQ_RETURNBOOL(g->SetMaterial(uint(index), m))
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
SQInteger			GeometryCloneMaterials(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(g, Geometry, typetag_Geometry)

	__LOG_W__ << "[SQ] GeometryCloneMaterials: Cloning all materials for geometry '" << g->name << "'...\n";

	uint mat_count = g->material_table.GetCount();
	__LOG_W__ << "[SQ] GeometryCloneMaterials: Found " << mat_count << " materials.\n";

	for (uint i = 0; i < mat_count; ++i)
	{
		Material *original = g->material_table[i].c_ptr();
		if (!original)
		{
			__LOG_W__ << "[SQ] GeometryCloneMaterials: Material " << i << " is NULL, skipping.\n";
			continue;
		}

		__LOG_W__ << "[SQ] GeometryCloneMaterials: Cloning material " << i << " ('" << original->name << "')...\n";

		// Cast to GPU::Material
		GPU::Material *gpu_mat = dynamic_cast<GPU::Material*>(original);
		if (!gpu_mat)
		{
			__LOG_W__ << "[SQ] GeometryCloneMaterials: Material " << i << " is not GPU::Material, skipping.\n";
			continue;
		}

		// Clone manually (same as MaterialClone)
		try
		{
			GPU::Renderer &rend = gpu_mat->renderer;
			Material *cloned = new GPU::Material(rend);

			// Copy properties
			cloned->name = gpu_mat->name + "_clone";
			*((Core::BasicMaterial *)cloned) = *((Core::BasicMaterial *)gpu_mat);
			((GPU::Material*)cloned)->shader = gpu_mat->shader;

			// Copy texture table
			for (uint n = 0; n < Core::Material::max_texture_stage; ++n)
			{
				cloned->texture_table[n] = gpu_mat->texture_table[n];
			}

			// Assign cloned material back to geometry
			g->SetMaterial(i, cloned);

			__LOG_W__ << "[SQ] GeometryCloneMaterials: Material " << i << " cloned successfully.\n";
		}
		catch (...)
		{
			__LOG_E__ << "[SQ] GeometryCloneMaterials: Exception while cloning material " << i << "!\n";
		}
	}

	__LOG_W__ << "[SQ] GeometryCloneMaterials: Done!\n";
	__SQ_RETURN
}
//------------------------------------------------------------------------------

//-------------------------------------------------
void		RegisterGeometryBinding(HSQUIRRELVM vm)
//-------------------------------------------------
{
/*#
	Topic: Geometry
	Type: Geometry
#*/

/*#
	Section: GeometryGeneric
	Desc: Generic functions
#*/
	
	/*#
		Func: GeometryGetLod
		Proto: Geometry:Geometry geo
		Desc: Get geometry get geometry LOD.
	#*/
	sq_register(vm, GeometryGetLod, "GeometryGetLod", _SC(".x"));
	/*#
		Func: GeometrySetLod
		Proto: void:Geometry geo, Geometry geo_LOD
		Desc: Set geometry LOD.
	#*/
	sq_register(vm, GeometrySetLod, "GeometrySetLod", _SC(".xx"));
	
	/*#
		Func: GeometryGetShadowProxy
		Proto: Geometry:Geometry geo
		Desc: Get geometry get geometry Shadow proxy.
	#*/
	sq_register(vm, GeometryGetShadowProxy, "GeometryGetShadowProxy", _SC(".x"));	
	/*#
		Func: GeometrySetShadowProxy
		Proto: void:Geometry geo, Geometry show_proxy
		Desc: Set geometry shadow proxy
	#*/
	sq_register(vm, GeometrySetShadowProxy, "GeometrySetShadowProxy", _SC(".xx"));
	
	/*#
		Func: GeometryGetLodDistance
		Proto: float:Geometry geo
		Desc: Get geometry Lod distance.
	#*/
	sq_register(vm, GeometryGetLodDistance, "GeometryGetLodDistance", _SC(".x"));	
	/*#
		Func: GeometrySetLodDistance
		Proto: void:Geometry geo, float Lod distance
		Desc: Set geometry Lod distance
	#*/
	sq_register(vm, GeometrySetLodDistance, "GeometrySetLodDistance", _SC(".xn"));
	
	/*#
		Func: GeometryGetLodNull
		Proto: bool:Geometry geo
		Desc: Get geometry Lod Null.
	#*/
	sq_register(vm, GeometryGetLodNull, "GeometryGetLodNull", _SC(".x"));	
	/*#
		Func: GeometrySetLodNull
		Proto: void:Geometry geo, bool LodNull
		Desc: Set geometry Lod Null
	#*/
	sq_register(vm, GeometrySetLodNull, "GeometrySetLodNull", _SC(".xb"));
	/*#
		Func: GeometryGetShadowProxyNull
		Proto: bool:Geometry geo
		Desc: Get geometry ShadowProxy Null.
	#*/
	sq_register(vm, GeometryGetShadowProxyNull, "GeometryGetShadowProxyNull", _SC(".x"));	
	/*#
		Func: GeometrySetShadowProxyNull
		Proto: void:Geometry geo, bool ShadowProxyNull
		Desc: Set geometry ShadowProxy Null
	#*/
	sq_register(vm, GeometrySetShadowProxyNull, "GeometrySetShadowProxyNull", _SC(".xb"));

	/*#
		Func: GeometrySaveOnItself
		Proto: void:Geometry geo, g_factory
		Desc: Save the geometry on it's own nmg.
	#*/
	sq_register(vm, GeometrySaveOnItself, "GeometrySaveOnItself", _SC(".xx"));	
	
	
	/*#
		Func: GeometryGetName
		Proto: string:Geometry geo
		Desc: Get geometry name.
	#*/
	sq_register(vm, GeometryGetName, "GeometryGetName", _SC(".x"));
	/*#
		Func: GeometrySetHidden
		Proto: void:Geometry geo,bool hidden
		Desc: Hide or show geometry, items referring to this geometry will still be updated.
	#*/
	sq_register(vm, GeometrySetHidden, "GeometrySetHidden", _SC(".xb"));
	/*#
		Func: GeometryOptimize
		Proto: void:Geometry geo
		Desc: Optimize geometry for realtime rendering.
	#*/
	sq_register(vm, GeometryOptimize, "GeometryOptimize", _SC(".x"));
	/*#
	Func: GeometryComputeIsoSurface
	Proto: void:Item item, int nb_metaball,array pos_metaball,array value_metaball
	Desc: Create an iso surface to the geometry with the array of metaball.
	#*/
	sq_register(vm, GeometryComputeIsoSurface, "GeometryComputeIsoSurface", _SC(".xiaa"));
	/*#
		Func: GeometrySetup
		Proto: void:Geometry geo
		Desc: Setup geometry in the renderer.
	#*/
	sq_register(vm, GeometrySetup, "GeometrySetup", _SC(".x"));

/*#
	Section: GeometryTopology
	Desc: Topology functions
#*/

	/*#
		Func: GeometryGetMinMax
		Proto: MinMax:Geometry geo
		Desc: Get geometry minmax.
	#*/
	sq_register(vm, GeometryGetMinMax, "GeometryGetMinMax", _SC(".x"));

/*#
	Section: GeometryMaterial
	Desc: Material functions
#*/

	/*#
		Func: GeometryGetMaterialList
		Proto: array:Geometry geo
		Desc: Return a list of all materials in a geometry.
	#*/
	sq_register(vm, GeometryGetMaterialList, "GeometryGetMaterialList", _SC(".x"));
	/*#
		Func: GeometryGetMaterialCount
		Proto: int:Geometry geo
		Desc: Get geometry material count.
	#*/
	sq_register(vm, GeometryGetMaterialCount, "GeometryGetMaterialCount", _SC(".x"));
	/*#
		Func: GeometryGetMaterial
		Proto: Material:Geometry geo,string name
		Desc: Get material from name.
	#*/
	sq_register(vm, GeometryGetMaterial, "GeometryGetMaterial", _SC(".xs"));
	/*#
		Func: GeometrySetMaterial
		Proto: bool:Geometry geo,int index,Material material
		Desc: Replace a material in the geometry material table.
	#*/
	sq_register(vm, GeometrySetMaterial, "GeometrySetMaterial", _SC(".xix"));
	/*#
		Func: GeometryCloneMaterials
		Proto: void:Geometry geo
		Desc: Clone all materials in the geometry. Each material becomes an independent copy.
		Note: This allows you to modify materials for one geometry without affecting others that share the same loaded geometry file.
		Example: GeometryCloneMaterials(geo)
	#*/
	sq_register(vm, GeometryCloneMaterials, "GeometryCloneMaterials", _SC(".x"));
	/*#
		Func: GeometryGetMaterialFromIndex
		Proto: Material:Geometry geo,int index
		Desc: Get material from index.
	#*/
	sq_register(vm, GeometryGetMaterialFromIndex, "GeometryGetMaterialFromIndex", _SC(".xi"));
}
