--============= Active Event Survivalist_InHouse =============--
--Kiểm tra building hợp lệ thì gửi yêu cầu tới server Spawn FreeNPC_xxx
--FreeNPC có thể bị nhiễm hoặc không bị nhiễm - 5/5
--============================================================--
--SPAWN NPC TRONG EVENT Survivalist_InHouse
--Các bước để tạo và update FreeNPC
	--1, tạo ngẫu nhiên qStep, không có trong list qStep. qStep này chưa định nghĩa NPC nào cả
	--2, yêu cầu các client trỏ qStep này tới qStep của 1 trong 4 qStep dưới, tùy server đã chọn được qStep nào. sendClientCommand, qStep = sameQStep
	--3, các client đăng nhập sau thì: check tất cả qStep trong questMD.NPC, nếu có questMD.NPC[qStep].sameQStep thì cập nhật VLF.npcData[qStep] = VLF.npcData[sameQStep]
	--4, trường hợp thoát game và login chỗ có FreeNPC. nếu VLF.npcData[sameQStep] thì trỏ VLF.npcData[qStep] = VLF.npcData[sameQStep]
--============================================================--

assert(VLPL, "Stack trace: Missing dependency: VLPL - vl_event_survivalist_npc_c.lua")
assert(VLZB, "Stack trace: Missing dependency: VLZB - vl_event_survivalist_npc_c.lua")

local GetPlayerModDataQuest = VLPL.GetPlayerModDataQuest
local GetPlayerModData = VLPL.GetPlayerModData
local GetStaticCoordOfBuilding = VLF.GetStaticCoordOfBuilding
local GetQuestGlobalModData = VLGMD.GetQuestGlobalModData
local GetZombieOnlineIDorUID = VLZB.GetZombieOnlineIDorUID
local GetZombieModDataQuest = VLZB.GetZombieModDataQuest
local IsMultiplayerMode = VLF.IsMultiplayerMode
local SetPlayerGMD_Event = VLGMD.SetPlayerGMD_Event
local GetPlayerGlobalModData = VLGMD.GetPlayerGlobalModData

--Kiểm tra ModData của player về thời gian active lại event
local function CheckPlayerDataDelayEvent(player, args) --CheckPlayerDataForActiveEvent(player, {event = "Survivalist_InHouse", delayHour = 6})
	local playerGMD = GetPlayerGlobalModData(player)
	local event = args.event
	
	if event == "Survivalist_InHouse" then
		--print("Client--------CheckPlayerDataForActiveEvent= ")
		if not playerGMD.Event[event] then
			return true
			
		elseif playerGMD.Event[event] and playerGMD.Event[event].lastTime then
			local nowHour = getGameTime():getWorldAgeHours()
			local delayHour = args.delayHour or 6
			if (nowHour - playerGMD.Event[event].lastTime) >= delayHour then
				return true
			--else
				--print("Client--------TIME PASSED= ", nowHour, playerGMD.Event[event].lastTime, (nowHour - playerGMD.Event[event].lastTime))
			end
		end
	end
	return false
end

--Set ModData player về thời gian delay event mới
local function SetPlayerDataDelayEvent(player, args) --SetPlayerDataDelayEvent(player, {event = "Survivalist_InHouse"})
	--local pDataQ = GetPlayerModDataQuest(nil)
	local playerGMD = GetPlayerGlobalModData(player)
	local event = args.event
	
	if event == "Survivalist_InHouse" then
		if not playerGMD.Event[event] then playerGMD.Event[event] = {} end
		--print("Client--------SetPlayerDataDelayEvent= ")
		--playerGMD.Event[event].lastTime = getGameTime():getWorldAgeHours()
		SetPlayerGMD_Event(player, event, "lastTime", getGameTime():getWorldAgeHours())
	end
end

--Client --Lấy buildings trong khoảng cách loading của player
local function GetBuildingsInRange(player, minDist, maxDist) --GetBuildingsInRange(player, 35, 60)
	local buildingList = getWorld():getMetaGrid():getBuildings()
	local bTab = {}
	for i=0, buildingList:size()-1 do
		local buildingDef = buildingList:get(i)
		if buildingDef then
			local x, y, z = GetStaticCoordOfBuilding(buildingDef)
			local disX = math.abs(x - player:getX())
			local disY = math.abs(y - player:getY())
			--Lấy building nằm trong khoảng min max
			if ((disX >= minDist) and (disX <= maxDist)) and ((disY >= minDist) and (disY <= maxDist)) then
				local bSquare = getCell():getGridSquare(x, y, z)
				if bSquare then
					--Lấy IsoBuilding để gọi hàm buildingDef không chạy được
					local building = bSquare:getBuilding()
					if building then
						table.insert(bTab, building)
						--print("--------buildingDef= ", buildingDef)
					end
				end
			end
		end
	end
	return bTab
end

--Client --Kiểm tra square modData của building 
local function CheckBuildingModDataForActiveEvent(building, args) --CheckBuildingModDataForActiveEvent(building, {event = event, hoursToNextActive = hoursToNextActive})
	local buildingDef = building:getDef()
	if buildingDef then
		local x, y, z = GetStaticCoordOfBuilding(buildingDef)
		local bSquare = getCell():getGridSquare(x, y, z)
		if bSquare then
			local sqData = bSquare:getModData()
			local event = args.event
			if event == "Survivalist_InHouse" then
				if not sqData.event_Survivalist_InHouse then
					--sendClientCommand(getPlayer(), "NPCQuestCM", "RequestEvents", {x = x, y = y, z = z, event = event, hoursToNextActive = args.hoursToNextActive})
					return true
				elseif sqData.event_Survivalist_InHouse and sqData.event_Survivalist_InHouse.lastActive then
					--print("Client----------CheckBuildingModDataForActiveEvent: 4444")
					local nowHour = getGameTime():getWorldAgeHours()
					if (nowHour - sqData.event_Survivalist_InHouse.lastActive) >= args.hoursToNextActive then
						return true
					end
				end
			end
		end
	end
	return false
end

local function ActiveEvent(building, args) ----ActiveEvent(building, {event = "Survivalist_InHouse", hoursToNextActive = 168})
	local player = getPlayer()
	if player and instanceof(player, "IsoPlayer") then
		local x, y, z = GetStaticCoordOfBuilding(building:getDef())
		sendClientCommand(player, "FreeNPCCM", "RequestEvents", {x = x, y = y, z = z, event = args.event, hoursToNextActive = args.hoursToNextActive})
		SetPlayerDataDelayEvent(player, {event = args.event})
		print("Client---ActiveEvent---Survivalist_InHouse---Request Event To Server At= ", x, y, z)
	end
end

--Client --Tính toán tỉ lệ xuất hiện Event Survivalist_InHouse
local function SpawnRate_Event_Survivalist_InHouse()
	local ageDay = getGameTime():getWorldAgeDaysSinceBegin()
	local rate
	if ageDay < 31 then rate = 100 --100/1000 = 10%
	elseif ageDay < 91 then rate = 70 --70/1000 = 7%
	else rate = 40 --40/1000 = 4%
	end
	return rate
end

--Lấy IsoRoom phòng bên ngoài bedroom: livingroom, hall, kitchen
local function GetOutsideRoomOfBedRoom(building)
    return building:getRandomRoom("livingroom") or building:getRandomRoom("hall") or building:getRandomRoom("kitchen")
end

--Client --check every hour
function VLF.Event_Survivalist_InHouse()
	local rate = SpawnRate_Event_Survivalist_InHouse()
	--local rate = 500
	local active = false
	local r = ZombRand(1000) --FIXME: ZombRand(1000)
	--print("Client-----Event_Survivalist_InHouse= random - ", r)
	if r < rate then
		active = true
	end
	if active == true then
		--kiểm tra player ModData đã reset cooldown chưa --thời gian tối thiểu cho cooldown event
		active = CheckPlayerDataDelayEvent(getPlayer(), {event = "Survivalist_InHouse", delayHour = 3}) --FIXME: 3
	end
	
	if active == true then
		local player = getPlayer()

		for i = 1, 10 do
			--chọn ngẫu nhiên building
			local buildingList = GetBuildingsInRange(player, 27, 60) --FIXME: 27, 60
			if #buildingList == 0 then return end
			local index = ZombRand(1, #buildingList + 1)
			local building = buildingList[index]
			
			--kiểm tra building ModData đã reset cooldown chưa
			if building and building:isResidential() and not building:hasRoom("office") and not building:hasRoom("elevator") then
				local outsideRoom = GetOutsideRoomOfBedRoom(building)
				if outsideRoom then
					local event, hoursToNextActive = "Survivalist_InHouse", 336 --FIXME: 336	--14 ngày mới xuất hiện tiếp trong ngôi nhà này
					local active = CheckBuildingModDataForActiveEvent(building, {event = event, hoursToNextActive = hoursToNextActive})
					if active then
						ActiveEvent(building, {event = event, hoursToNextActive = hoursToNextActive})
						return
					end
				end
			else
				table.remove(buildingList, index) --nếu không load buildingList lại thì remove từ lần 2 sẽ sai index
			end
		end
	end
end
--Event_Survivalist_InHouse = VLF.Event_Survivalist_InHouse
Events.EveryHours.Remove(VLF.Event_Survivalist_InHouse)
Events.EveryHours.Add(VLF.Event_Survivalist_InHouse)

--Trỏ qStep ngẫu nhiên của Free NPC tới qStep cố định
local function FreeNPCToSameQStep(qStep, sameQStep)
    local questMD = GetQuestGlobalModData(qStep)
    questMD.NPC[qStep]["sameQStep"] = sameQStep
    VLF.npcData[qStep] = VLF.npcData[sameQStep]
end

--Cập nhật qStep của FreeNPC khi vào game
local function UpdateFreeNPCOnStart(playerNum, player)
    local questMD = GetQuestGlobalModData(nil)
    for qStep, data in pairs(questMD.NPC) do
        if data["sameQStep"] then
            FreeNPCToSameQStep(qStep, data["sameQStep"])
        end
    end            
end
Events.OnCreatePlayer.Remove(UpdateFreeNPCOnStart)
Events.OnCreatePlayer.Add(UpdateFreeNPCOnStart)

--===================SinglePlayer Only===================--

--Tìm các freeNPC theo tọa độ spawn để cập nhật lại zID mới
local function GetAndRequestUpdateFreeNPCs()
	local questMD = GetQuestGlobalModData(nil)
	local cell = getCell()
	
    for qStep, data in pairs(questMD.NPC) do
		local breakTo = false
        --if data["sameQStep"] then --Check cái này có thể sẽ trùng vào NPC event khác sau này
		if string.find(qStep, "freeNPC_") then
			local x, y, z = data["x"], data["y"], data["z"]
			if x and y and z then
				--print("Client---FreeNPC Data= ", qStep, x, y, z)
				--Kiểm tra sq để xác định Client có đang load vị trí cần check không --Client không cần check các NPC ở ngoài vùng load
				local sq = cell:getGridSquare(x, y, z)
				if sq then
					--Vòng lặp theo bán kính từ bé đến lớn: 0 -> +-2
					local range = {0, 1, -1, 2, -2} 
					for _, pX in ipairs(range) do
						for _, pY in ipairs(range) do
							local _x, _y = x + pX, y + pY
							local _sq = cell:getGridSquare(_x, _y, z)
							if _sq then
								--print("Client---FreeNPC---Find Zombie At= ", _x, _y, z)
								local mObjs = _sq:getMovingObjects()
								for i = 0, mObjs:size() - 1 do
									local mObj = mObjs:get(i)
									if mObj and instanceof(mObj, "IsoZombie") then
										--Kiểm tra outfit của Zombie có phải thuộc loại Survivalist
										local zOutfit = mObj:getOutfitName()
										if zOutfit then
											if string.find(zOutfit, "Survivalist", 1, true) then
												local zID = GetZombieOnlineIDorUID(mObj)
												print("Client---FreeNPC---FOUND---Zombie Outfit Survivalist At= ", _x, _y, z)
												sendClientCommand(getPlayer(), "FreeNPCCM", "UpdateFreeNPCs", {qStep = qStep, zID = zID})
												breakTo = true
												break
											end
										end
									end
								end
								if breakTo == true then break end
							end
						end
						if breakTo == true then break end
					end
				end
			end
		end
	end
end

--SinglePlayer Only
--Sử dụng OnZombieUpdate để đảm bảo đã có zombie để check
--Chỉ chạy 1 lần duy nhất rồi xóa Event
local function OnZombieUpdate_CheckFreeNPCs(zombie)
	local zData = GetZombieModDataQuest(zombie)
    if not zData.UID then zData.UID = zombie:getUID() return end --để đảm bảo zombie không dùng lại moddata của z cũ.
	--đã qua 1 tick
	
	--Tìm freeNPC và yêu cầu update ID mới
	GetAndRequestUpdateFreeNPCs()
	
	--Yêu cầu server thay đổi flag để bắt đầu check tồn tại của freeNPC để xóa (Trước đó flag = false mỗi khi khởi động lại game để tự động bỏ qua freeNPC khi check tồn tại)
	sendClientCommand(getPlayer(), "FreeNPCCM", "CheckedAllFreeNPCs", nil)
	
	Events.OnZombieUpdate.Remove(OnZombieUpdate_CheckFreeNPCs)
end
if not IsMultiplayerMode() then
	Events.OnZombieUpdate.Remove(OnZombieUpdate_CheckFreeNPCs)
	Events.OnZombieUpdate.Add(OnZombieUpdate_CheckFreeNPCs)
end

--SinglePlayer Only
--Update lại dữ liệu của FreeNPC khi zombie bị reset theo yêu cầu của Server
local function UpdateFreeNPCCM_C(qStep, zID)
	local questMD = GetQuestGlobalModData(qStep)
	local zombie = GetZombieByOnlineIDOrUID(zID)
	if zombie and not zombie:isDead() and zombie:getHealth() > 0 then
		--questMD.NPC[qStep]["uid"] = zombie:getUID() --dùng cho server kiểm tra zombie nếu không tồn tại thì xóa spawned, chỉ dùng UID vì tính duy nhất
		questMD.NPC[qStep]["zID"] = zID
		--Xóa questMD.NPCqStep cũ
		for _zID, _qStep in pairs(questMD.NPCqStep) do
			if _qStep == qStep then
				questMD.NPCqStep[_zID] = nil
				break
			end
		end
		--Thêm questMD.NPCqStep mới
		questMD.NPCqStep[zID] = qStep
	else
		print("Client---UpdateFreeNPCCM_C= no zombie")
	end
end

--========================================================--
--[[
--OnServerCommand KHÔNG CHẠY TRONG SINGLE PLAYER
local function OnServerCommand_FreeNPC(module, command, args)
    if module == "FreeNPCCM_C" then
        if command == "FreeNPCSameQStep_C" then 	--{qStep = qStep, sameQStep = sameQStep}
            FreeNPCToSameQStep(args.qStep, args.sameQStep)
		
		elseif command == "UpdateFreeNPCCM_C" then	--{qStep = qStep, zID = zID}
			UpdateFreeNPCCM_C(args.qStep, args.zID)

        end
    end
end
Events.OnServerCommand.Remove(OnServerCommand_FreeNPC)
Events.OnServerCommand.Add(OnServerCommand_FreeNPC)
]]

--================= ONSERVERCOMMAND =================--
VLOSC["FreeNPCCM_C"] = VLOSC["FreeNPCCM_C"] or {}

VLOSC["FreeNPCCM_C"]["FreeNPCSameQStep_C"] = function(args)	--{qStep = qStep, sameQStep = sameQStep}
	FreeNPCToSameQStep(args.qStep, args.sameQStep)
end

VLOSC["FreeNPCCM_C"]["UpdateFreeNPCCM_C"] = function(args)	--{qStep = qStep, zID = zID}
	UpdateFreeNPCCM_C(args.qStep, args.zID)
end

