------------------------------------------------------------------------------------------------------------

local tinsert 	= table.insert

local tiny 		= require('engine.world.tiny-ecs')
local tinysrv	= require('engine.world.tiny-ecs-server')
local assetmgr 	= require("engine.world.asset-manager")
local utils 	= require('lua.utils')
local ffi 		= require("ffi")

------------------------------------------------------------------------------------------------------------

local worldmanager = {

	worlds = {},
	worlds_lookup = {},
	world_current = nil,

	systems = {},
	systems_lookup = {},
	entities = {},
	entities_lookup = {},
	cameras_lookup = {},

	assetmgr 	= assetmgr,
}

------------------------------------------------------------------------------------------------------------

worldmanager.addEntity = function( self, pos, rot, obj )
	
	if(obj.name == nil) then 
		print("[Error] Entity doesnt have a name.")
		return nil 
	end 

	local id = utils.tcount(self.entities)
	if(obj.go) then 
		local gostr = tostring(obj.go)
		id = utils.crc(gostr, string.len(gostr)) 
	end 

	obj.id = id
	obj.etype = obj.etype or "entity"
	obj.created = socket.gettime()
	obj.visible = obj.visible or 1
	obj.pos = tostring(obj.pos or pos)
	obj.rot = tostring(obj.rot or rot)
	obj.scale = { 1, 1, 1 }

	-- Keep some handles so we can easily remove
	tinsert(self.entities, obj)
	self.entities_lookup[obj.id] = utils.tcount(self.entities)
	
	return tiny.addEntity(self.current_world, obj)
end

------------------------------------------------------------------------------------------------------------

worldmanager.addGameObject = function( self, name, objurl )
	
	if(name == nil) then 
		print("[Error] Entity doesnt have a name.")
		return nil 
	end 
	local pos = go.get_position(objurl)
	local rot = go.get_rotation(objurl)

	local id = hash_to_hex(hash(objurl))
	local obj = {
		id = id,
		go = objurl,
		name = name, 
		etype = "gameobject",
		created = socket.gettime(),
		visible = 1,
		pos = { x=pos.x, y=pos.y, z=pos.z },
		rot = { x=rot.x, y=rot.y, z=rot.z, w=rot.w },
		scale = { 1, 1, 1 },
	}

	-- Keep some handles so we can easily remove
	tinsert(self.entities, obj)
	self.entities_lookup[obj.id] = utils.tcount(self.entities)
	return tiny.addEntity(self.current_world, obj)
end

------------------------------------------------------------------------------------------------------------

worldmanager.addCamera = function( self, name, objurl )

	if(name == nil) then 
		print("[Error] Entity doesnt have a name.")
		return nil 
	end 
	local pos = go.get_position(objurl)
	local rot = go.get_rotation(objurl)

	local aspect = go.get(objurl, "aspect_ratio") -- get aspect ratio
	local near = go.get(objurl, "near_z") -- get far z
	local far = go.get(objurl, "far_z") -- get far z
	local fov = go.get(objurl, "fov") -- get field of view

	local id = hash_to_hex(hash(objurl))
	local obj = {
		id = id,
		go = objurl,
		name = name, 
		etype = "camera",
		created = socket.gettime(),
		visible = 1,
		pos = { x=pos.x, y=pos.y, z=pos.z },
		rot = { x=rot.x, y=rot.y, z=rot.z, w=rot.w },
		scale = { 1, 1, 1 },

		fov = fov,
		aspect = aspect, 
		near = near, 
		far = far,
	}

	-- Keep some handles so we can easily remove
	tinsert(self.entities, obj)
	self.entities_lookup[obj.id] = utils.tcount(self.entities)
	self.cameras_lookup[obj.id] = utils.tcount(self.entities)
	return tiny.addEntity(self.current_world, obj)
end

------------------------------------------------------------------------------------------------------------

worldmanager.removeEntity = function( self, eid )

	if(eid == nil) then 
		print("[Error] removeEntity: Entity eid is nil?")
		return nil 
	end 
	local ent = self.entities[self.entities_lookup[eid]]
	if(ent == nil) then 
		print("[Error] removeEntity: Entity not found?")
		return nil 
	end
	if(eid) then go.delete(eid, true) end
	local oldent = tiny.removeEntity(self.current_world, ent)
    return oldent
end

------------------------------------------------------------------------------------------------------------

worldmanager.addSystem = function( self, systemname, filters, processFunc )

	if(self.systems_lookup[systemname]) then 
		print("[Error] System already exists: "..systemname)
		return nil
	else
		local new_system = tiny.processingSystem()
		new_system.filter = tiny.requireAll( unpack(filters) )
		new_system.process = processFunc
		local out_system = tiny.addSystem(self.current_world, new_system)
		self.current_world:update(0)

		local systeminfo = {
			name = systemname,
			filters = filters,
			index = out_system.index,
			active = out_system.active,
			modified = out_system.modified,
		}
		
		tinsert(self.systems, systeminfo)
		self.systems_lookup[systemname] = out_system.index
	end
end

------------------------------------------------------------------------------------------------------------
-- Add an asset to the world - to be used by objects within the world
--   This will be passed to the asset manager which determines the asset type and 
--   populates with asset obj with the correct data.
worldmanager.addAsset = function(self, assetFilename)


end

------------------------------------------------------------------------------------------------------------
-- Each world thats created we make a default proc to process for http server
worldmanager.addWorld = function(self, worldname)

	if(self.worlds_lookup[worldname]) then 
		print("[Error] World already exists: "..worldname)
		return nil
	else
		self.current_world = tiny.world()
		self.current_world.name = worldname 

		-- Add an updater for entities in the httpserver
		self:addSystem( worldname.."_Entities", { "name", "etype" }, tinysrv.entitySystemProc )

		tinsert(self.worlds, self.current_world)
		self.systems_lookup[worldname] = utils.tcount(self.worlds)
		return self.current_world, utils.tcount(self.worlds)
	end
end

------------------------------------------------------------------------------------------------------------

worldmanager.addmanager = function(mgrname, mgrobj)
	-- This adds the manager to be accessible by twig using the mgrname
    tinysrv[mgrname] = mgrobj
end

------------------------------------------------------------------------------------------------------------

worldmanager.processOptions = function(self, options) 
	if(options) then 
		if(options.noserver == true) then 
			tinysrv.init = function() end 
			tinysrv.final = tinysrv.init
			tinysrv.update = tinysrv.init
		end
		if(options.host) then 
			tinysrv.host = options.host
		end
		if(options.port) then 
			tinysrv.port = options.port
		end
	end
end

------------------------------------------------------------------------------------------------------------

worldmanager.init = function(self, options)

	self:processOptions(options)
	tinysrv.init()
	tinysrv.setWorlds(self.worlds)
	tinysrv.setEntities(self.entities, self.entities_lookup, self.cameras_lookup)
	tinysrv.setSystems(self.systems)
end

------------------------------------------------------------------------------------------------------------

worldmanager.final = function (self)

	tinysrv.final()
	if(self.current_world) then 
		tiny.clearEntities(self.current_world)
		tiny.clearSystems(self.current_world)
	end
end

------------------------------------------------------------------------------------------------------------

worldmanager.update = function(self, dt)
	-- Handle direct world swapping
	tinysrv.current_world = self.current_world
	for k,v in pairs(self.worlds) do
		v:update(dt)
	end
	tinysrv.update(dt)
end

------------------------------------------------------------------------------------------------------------

return worldmanager

------------------------------------------------------------------------------------------------------------
