--------------------------------------------------------
-- obj.ownerUuid  		存在的话，只有指定人能看到
--------------------------------------------------------

local Util = require("common.Util")
local Timer = require("core.Timer")
local Msg = require("core.Msg")
local ObjHuman = require("core.ObjHuman")

TYPE_HUMAN = 1
TYPE_COLLECT = 2 

objs = objs or {} 				-- obj_id转换成对象
scenes = scenes or {}			-- 所有的场景 [sceneID][objId] = obj
scenes_fds = scenes_fds or {} 	-- 场景fd [sceneID][fd] = obj

local q_cap = 65536

local function q_push(id)
	if q_b + 1 == q_f or q_b == q_cap and q_f == 1 then
		assert()
	end
	q[q_b] = id
	q_b = q_b + 1
	if q_b == q_cap + 1 then
		q_b = 1
	end
end

function q_pop()
	if q_f + 1 == q_b or q_f == q_cap and q_b == 1 then
		assert()
	end
	local t = q[q_f]
	q_f = q_f + 1
	if q_f == q_cap + 1 then
		q_f = 1
	end
	return t
end

local function q_init()
	if q then
		return
	end
	q = {}
	q_b = q_cap
	q_f = 1
	for i = 1, q_cap do
		q[i] = 1000 + i -- 这里的objid从1001开始 0-1000留给客户端的剧情动画角色
	end
end
q_init()

obj_envs = {
	require("core.ObjHuman"),
}

function getObj(obj_id, obj_uid)
	local obj = objs[obj_id]
	if not obj then
		return
	end
	if obj_uid and obj_uid ~= obj.uid then
		return
	end
	return obj
end

function create(obj, obj_type)
	local id = q_pop()
	if objs[id] then
		assert()
	end
	objs[id] = obj
	obj.id = id
	obj.obj_type = obj_type
	obj.uid = Timer.now
	return obj
end

function destroy(obj)
	if objs[obj.id] ~= obj then
		assert()
	end
	objs[obj.id] = nil
	q_push(obj.id)
end

function destroyAllType(obj)
	if obj_envs[obj.obj_type].destroy then
		obj_envs[obj.obj_type].destroy(obj)
	else
		destroy(obj)
	end
end

function getBodyInfo(obj)
	return obj_envs[obj.obj_type].getBodyInfo(obj)
end

function getObjAdd(obj)
	return obj_envs[obj.obj_type].getObjAdd(obj)
end

local function getObjDel(obj)
	local mm = Msg.gc.GC_DEL_OBJ
	mm.obj_id = obj.id
	return mm
end

function getSpeed(obj)
	return obj_envs[obj.obj_type].getSpeed(obj)	
end

-- 取场景所有obj fd列表
local sceneFdObjs = {}
function getSceneObjFds(sceneID, exceptObj)
	for k in ipairs(sceneFdObjs) do
		sceneFdObjs[k] = nil
	end

	if not scenes_fds[sceneID] then
		return sceneFdObjs
	end

	local len = 0
	for _, obj in pairs(scenes_fds[sceneID]) do
		if obj ~= exceptObj then
			len = len + 1
			sceneFdObjs[len] = obj
		end
	end
	return sceneFdObjs
end

local sceneObjs = {}
function getSceneObjs(sceneID, obj_type, ownerUuid)
	for k in ipairs(sceneObjs) do
		sceneObjs[k] = nil
	end

	if not scenes[sceneID] then
		return sceneObjs
	end

	local len = 0
	for _, obj in pairs(scenes[sceneID]) do
		if (obj_type == nil or obj.obj_type == obj_type) and
			(ownerUuid == nil or obj.ownerUuid == ownerUuid) then
			len = len + 1
			sceneObjs[len] = obj
		end
	end
	return sceneObjs
end

-- 清除归属的对象
function clearOwnerObjs(sceneID, obj, noSend)
	if not sceneID then
		return
	end	
	local scene = scenes[sceneID]
	if not scene then
		return
	end
	if not obj.fd or not obj.db then
		return
	end

	for _, tobj in pairs(scene) do
		if not tobj.fd and tobj.ownerUuid and 
			tobj.ownerUuid == obj.db._id then
			scene[tobj.id] = nil
			destroyAllType(tobj)

			if not noSend then
				Msg.send(getObjDel(tobj), obj.fd)
			end
		end
	end
end

function enterScene(obj, sceneID, x, y)
	if obj.sceneID then
		print("ERROR:has enterScene")
		return
	end

	if obj.fd and obj.db then
	    -- 把周围的活动obj告诉这个玩家
		local sceneFdObjs = getSceneObjFds(sceneID, obj)
		for _, sceneObj in ipairs(sceneFdObjs) do
			if not sceneObj.ownerUuid or 
				sceneObj.ownerUuid == obj.db._id then
				Msg.send(getObjAdd(sceneObj), obj.fd)
				sendMove(sceneObj, obj)
			end
		end
	end

	resetPathParam(obj)
	obj.sceneID = sceneID
	obj.x = x
	obj.y = y
	if obj.fd then
		obj.way = 4
	end
	
	-- 把这个对象进入场景的信息告诉别的玩家
	if obj.ownerUuid then
		local ownerObj = ObjHuman.onlineUuid[obj.ownerUuid]
		if ownerObj and ownerObj.fd then
			Msg.send(getObjAdd(obj), ownerObj.fd)
		end
	else
		sendScene(getObjAdd(obj), sceneID)
	end

	scenes[sceneID] = scenes[sceneID] or {}
	scenes[sceneID][obj.id] = obj
	scenes_fds[sceneID] = scenes_fds[sceneID] or {}
	if obj.fd then
		scenes_fds[sceneID][obj.fd] = obj
	end
end

function leaveScene(obj)
	local sceneID = obj.sceneID
	if not sceneID then
		return
	end	
	local scene = scenes[sceneID]
	if not scene then
		return
	end

	obj.sceneID = nil
	if not scene[obj.id] then
		return
	end

	scene[obj.id] = nil
	if obj.fd then
		scenes_fds[sceneID][obj.fd] = nil
		clearOwnerObjs(sceneID, obj, true)
	end	
	
	-- 告诉附近玩家 这个对象离开场景了
	if obj.ownerUuid then
		local ownerObj = ObjHuman.onlineUuid[obj.ownerUuid]
		if ownerObj and ownerObj.fd then
			Msg.send(getObjDel(obj), ownerObj.fd)
		end
	else
		sendScene(getObjDel(obj), sceneID)
	end
	return true, sceneID
end

function sendScene(mm, sceneID, exceptObj)
	local objs = scenes[sceneID]
	if not objs then
		return
	end
	local list = Msg.list
	local len = 0
	for _, obj in pairs(objs) do
		if obj.fd and obj ~= exceptObj then
			len = len + 1
			list[len] = obj.fd
		end
	end
	list[0] = len
	Msg.sendMulti(mm, list)
end

function resetPathParam(obj)
	obj.pathLen = 0 						
	obj.pathPoints = nil 	
end

function walk(during)
	for sceneID, sceneObjs in pairs(scenes) do
		for objid, obj in pairs(sceneObjs) do
			if obj.pathLen > 0 then
				local speed = getSpeed(obj)
				local oldx = obj.x
				local oldy = obj.y
				local nearx = obj.pathPoints[obj.pathLen * 2 - 1]
				local neary = obj.pathPoints[obj.pathLen * 2]
				local dx = oldx - nearx
				local dy = oldy - neary

				local way = Util.getWay(oldx - nearx, oldy - neary)
				local moveLen = math.floor(speed / 1000 * during)

				if dx * dx + dy * dy <= moveLen * moveLen then
					obj.pathLen = obj.pathLen - 1
					oldx = nearx
					oldy = neary
				else
					oldx, oldy = Util.getTargetPoint(nearx, neary, oldx, oldy, moveLen)
				end

				obj.x = oldx 
				obj.y = oldy
				obj.way = way		
			end
		end
	end
end

function stop(obj)
	if obj.path_len == 0 then return end
	if not obj.sceneID then return end

	resetPathParam(obj)
	local msgRet = Msg.gc.GC_STOP_MOVE
	msgRet.way = obj.way or 4
	msgRet.obj_id = obj.id
	msgRet.x = obj.x
	msgRet.y = obj.y
	sendScene(msgRet, obj.sceneID)
end

function sendMove(obj, target)
	if not obj.sceneID then
		return 
	end

	if not obj.pathLen or obj.pathLen < 1 then
		return
	end

	local msgRet = Msg.gc.GC_MOVE
	msgRet.obj_id = obj.id
	msgRet.points[0] = obj.pathLen * 2
	for i = 1, obj.pathLen do
		local j = obj.pathLen + 1 - i
		msgRet.points[2 * i - 1] = obj.pathPoints[2 * j - 1]
		msgRet.points[2 * i] = obj.pathPoints[2 * j]
	end
	if target then
		Msg.send(msgRet, target.fd)
	else
		sendScene(msgRet, obj.sceneID, obj)
	end
end

