
VLOCC = VLOCC or {}
VLOCC["NPCQuestCM"] = VLOCC["NPCQuestCM"] or {}
VLF.var.requestedNSZ = VLF.var.requestedNSZ or {}

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

local CalculateDistanceXY = VLF.CalculateDistanceXY
local GetZombieOnlineIDorUID = VLZB.GetZombieOnlineIDorUID
local GetZombieByOnlineIDOrUID = VLZB.GetZombieByOnlineIDOrUID
local GetPlayerGlobalModData = VLGMD.GetPlayerGlobalModData
local IsMultiplayerMode = VLF.IsMultiplayerMode
local GetQuestGlobalModData = VLGMD.GetQuestGlobalModData
local DelayFunction = VLF.DelayFunction
local SetZombieDefault = VLZB.SetZombieDefault
local GetAllnpcIDSpawning = VLZB.GetAllnpcIDSpawning
local GetZombieModDataQuest = VLZB.GetZombieModDataQuest
local NpcDoRifleShot = VLZB.NpcDoRifleShot
local GetItem = VLIT.GetItem
local IsCharExistAndAlive = VLZB.IsCharExistAndAlive
local RemoveZombiesInAreaExceptNPC = VLZB.RemoveZombiesInAreaExceptNPC
local RemoveZombie = VLZB.RemoveZombie
local IsNPC = VLZB.IsNPC
local DeactiveNPCSpawnedData = VLZB.DeactiveNPCSpawnedData
local ActiveNPCSpawnedData = VLZB.ActiveNPCSpawnedData
local SetZombieInactiveFakeNPC = VLZB.SetZombieInactiveFakeNPC
local KillNPCQuest = VLZB.KillNPCQuest

local function TransmitVLQuestModData()
    ModData.transmit("VL_Quest")
end

local function TransmitGlobalPlayerModData(pID)
    ModData.transmit("VL_Player_"..tostring(pID))
end

local function IsZombieExisting(uid)
    if not uid then return false end
    local zombies = getCell():getZombieList()
    for i = 0, zombies:size()-1 do
        local zombie = zombies:get(i)
        if zombie then
            local zUID = zombie:getUID()
             
            if zUID and zUID == uid then
                return true 
            end
        end
    end
    return false
end
                 
--Xóa moddata spawned của NPC khi zombie NPC không còn tồn tại trên bộ nhớ (tất cả client unload)
--24h ingame = 1h -> 1 minute ingame = 2.5s
local function EveryOneMinute_CheckNPCExist()
    --local questMD = ModData.getOrCreate("VL_Quest")
    local questMD = GetQuestGlobalModData(nil)
    for qStep, data in pairs(questMD.NPC) do
        if data.spawned == true then
            local uid = data.uid
            local zID = questMD.NPC[qStep]["zID"]
            if zID then
                --------------------SinglePlayer Only----------------------
                --Nếu chưa check qua các vị trí tồn tại của freeNPC thì bỏ qua việc CheckNPCExist các freeNPC này
                --Xóa nếu không cần freeNPC cập nhật lại mỗi khi player trong SinglePlayer relogin
                if not IsMultiplayerMode() then
                    if not VLF.var.freeNPCsUpdated then
                        if string.find(qStep, "freeNPC_") then
                            return
                        end
                    end
                end
                -----------------------------------------------------------
                   
                --Nếu vị trí NPC không giống lúc spawn +-5 ô thì --Xử lý trường hợp player teleport nên NPC chưa kịp xóa
                local x, y = questMD.NPC[qStep]["x"], questMD.NPC[qStep]["y"]
                local zombie = GetZombieByOnlineIDOrUID(zID)
                if zombie and x and y then
                    local _x, _y = zombie:getX(), zombie:getY()
                    --Nếu NPC nằm ngoài điểm spawn +- 5 ô 
                    --if (_x > (x + 5) or (_x < (x - 5))) and (_y > (y + 5) or (_y < (y - 5))) then
                    if math.abs(_x - x) > 5 or math.abs(_y - y) > 5 then
                        SetZombieDefault(zombie)
                        DeactiveNPCSpawnedData(qStep, zID)
                        --sendServerCommand("NPCQuestCM_C", "SetDefaultZombie_C", {zID = zID})
                        print("Server---EveryOneMinute_CheckNPCExist---Remove Data---NPC Khong Dung Toa Do= ", qStep, math.floor(_x), math.floor(_y), zombie:getZ())
                    end
                end
                
                --Nếu ID của NPC không tồn tại
                if not IsZombieExisting(uid) then
                    DeactiveNPCSpawnedData(qStep, zID)
                    print("Server---EveryOneMinute_CheckNPCExist---Remove Data---NPC Not Exist= ", qStep)
                end
            end
        end
    end
end
Events.EveryOneMinute.Remove(EveryOneMinute_CheckNPCExist)
Events.EveryOneMinute.Add(EveryOneMinute_CheckNPCExist)

--SPAWN ZOMBIE NPCQUEST
function VLF.SpawnZombieNPCQuest(args) --SpawnZombieNPCQuest({qStep = "q1s3", x = getPlayer():getX(), y = getPlayer():getY(), z = getPlayer():getZ(), num = 1, outfit = "Naked", femaleChance = 0, crawler = false, isFallOnFront = false, isFakeDead = false, knockedDown = false, isInvulnerable = true, isSitting = false, health = 1})
    local x, y , z = args.x, args.y, args.z
    local qStep = args.qStep
    --local pID = args.pID
    if not x or not y or not z or not qStep then return end
    local num = args.num or 1
    local outfit = args.outfit or ""
    local femaleChance = args.femaleChance or 0
    local crawler = args.crawler or false
    local isFallOnFront = args.isFallOnFront or false
    local isFakeDead = args.isFakeDead or false
    local knockedDown = args.knockedDown or false
    local isInvulnerable = args.isInvulnerable or false
    local isSitting = args.isSitting or false
    local health = args.health or 1
    
    --spawn zombie NPC
    local zombieList = addZombiesInOutfit(x, y, z, 1, outfit, femaleChance, crawler, isFallOnFront, isFakeDead, knockedDown, isInvulnerable, isSitting, health)
    local zombie = zombieList:get(0)
    zombie:setPosition(x + 0.5, y + 0.5, z) --di chuyển vào giữa ô
    local zID = GetZombieOnlineIDorUID(zombie)
    print("Server---SpawnZombieNPCQuest---NPC Spawned= ", qStep, x, y, z)
    
    local zData = GetZombieModDataQuest(zombie)
    zData.isNPCQuest = true --Cần send tới Client
    zData.spawnPoint = getCell():getGridSquare(math.floor(zombie:getX()), math.floor(zombie:getY()), math.floor(zombie:getZ())) --Cần send tới Client
    
    --sử dụng global moddata để tránh npc spawn trước mặt người chơi khác. Chỉ cần có 1 người có quest thì NPC spawn với tất cả ngươi chơi. Nhưng để active quest thì cần biến riêng
    local zUID = zombie:getUID()
    ActiveNPCSpawnedData(x, y, z, qStep, zID, zUID)

    --TransmitVLQuestModData()
    sendServerCommand("NPCQuestCM_C", "ActiveNPCSpawnedData_C", {x = x, y = y, z = z, qStep = qStep, zID = zID, zUID = zUID})

    zombie:setUseless(true)
    if not isServer() then
        SetZombieInactiveFakeNPC(zombie)
    end
    return zombie
end
local SpawnZombieNPCQuest = VLF.SpawnZombieNPCQuest

--Xóa zombie cách x ô vuông từ sq, ngoại trừ NPC. z == z
local function RemoveZombieFromSquares(sq, range) --RemoveZombieFromSquares(sq, 5)
    if not sq then return end
    --print("Server-----------RemoveZombieFromSquares= ") 
    if not range then range = 1 end
    local x, y, z = sq:getX(), sq:getY(), sq:getZ()
    local zombies = getCell():getZombieList()
    local zombieList = {}
    for i = 0, zombies:size()-1 do
        local zombie = zombies:get(i)
        if IsCharExistAndAlive(zombie) then
            local zX, zY, zZ = math.floor(zombie:getX()), math.floor(zombie:getY()), math.floor(zombie:getZ())
            if (zZ == z) and (zX >= x - range and zX <= x + range) and (zY >= y - range and zY <= y + range) then
                table.insert(zombieList, zombie)
            end
        end
    end
    
    --Nếu zombie không phải bất kì NPC nào thì xóa
    if #zombieList > 0 then
        for i = #zombieList, 1, -1 do
            local zombie = zombieList[i]
            if not IsNPC(zombie) then
                if IsCharExistAndAlive(zombie) then
                    RemoveZombie(zombieList[i])
                end
            end
        end
    end
end

--Xóa zNPC và xóa dữ liệu của zNPC
local function RemoveZombieAndSpawnedData(zID, qStep)
    if not zID or not qStep then return end
    local zombie = GetZombieByOnlineIDOrUID(zID)
    if zombie then
		--if not isServer() then 	--SP
			SetZombieDefault(zombie)
		--end
        RemoveZombie(zombie)
        DeactiveNPCSpawnedData(qStep, zID)
        --print("Server-----------RemoveZombieAndSpawnedData= ", qStep) 
    end
    
end

--Kiểm tra có player nào gần object hoặc gần tọa độ đã cho không
function VLF.IsAnyPlayerNearObjectByXY(zID, sqX, sqY, maxDist) --VLF.IsAnyPlayerNearObjectByXY(zID, nil, nil, 70)
    if IsMultiplayerMode() then
        local players = getOnlinePlayers()
        local x1, y1
        
        if zID then --nếu chỉ khai báo zID --zombie
            local zombie = GetZombieByOnlineIDOrUID(zID)
            x1, y1 = zombie:getX(), zombie:getY()
        elseif sqX and sqY then
            x1, y1 = sqX, sqY
        end

        if not maxDist then maxDist = 70 end
        if players and x1 and y1 then
            for i = 0, players:size() - 1 do
                local player = players:get(i)
                local x2, y2 = player:getX(), player:getY()
                if x2 and y2 then
                    local fDistance = CalculateDistanceXY(x1, y1, x2, y2)
                    if fDistance and fDistance < maxDist then
                        print("Server---IsAnyPlayerNearObjectByXY= ", player:getUsername(), x2, y2, player:getZ())
                        return player
                    end
                end
            end
        end
        return nil
    end
end
local IsAnyPlayerNearObjectByXY = VLF.IsAnyPlayerNearObjectByXY

--Thêm ô tô
function VLF.AddVehicle(args) --VLF.AddVehicle({vehicleName = vehicleName, x = x, y = y, z = z, angleX = 0, angleY = 90, angleZ = 0, damage = 100, crashFont = true, crashBack = true, colorH = 38, colorS = 59, colorV = 80})
	local x, y, z = args.x, args.y, args.z
	local square = getCell():getGridSquare(x, y, z)
	if not square or not square:isFree(true) or not square:isSolidFloor() then return end
	local vehicleName, crashBack = args.vehicleName, args.crashBack
	local vehicle = addVehicle(vehicleName, x, y, z)
    --print("Server-----------VLF.AddVehicle", qStep, vehicle) 
	if vehicle then
		local angleX, angleY, angleZ = args.angleX or 0, args.angleY or 0, args.angleZ or 0
		vehicle:setAngles(angleX, angleY, angleZ) --(thẳng, ngang, nghiêng)
		local crashFont = args.crashFont
		local damage = args.damage or 100.0
		if crashFont and crashFont == true then
			vehicle:crash(damage, true) --(float delta, boolean front) --damage 100 = vỡ cửa 50/50
		end
		if crashBack and crashBack == true then
			vehicle:crash(damage, false)
		end
		local colorH, colorS, colorV = args.colorH, args.colorS, args.colorV
		if colorH and colorS and colorV then
			vehicle:setColorHSV(colorH, colorS, colorV) --max: 100
		end
	end
end
local AddVehicle = VLF.AddVehicle

--Cập nhật data spawn cho NPC để không cần unload rồi load lại
local function UpdateNPC(qStep_Next, zID)
	local questMD = GetQuestGlobalModData(qStep_Next)
	local zombie = GetZombieByOnlineIDOrUID(zID)
    --if zombie and not zombie:isDead() and zombie:getHealth() > 0 then
    if IsCharExistAndAlive(zombie) then
        questMD.NPC[qStep_Next]["spawned"] = true
        questMD.NPC[qStep_Next]["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_Next]["zID"] = zID
        questMD.NPCqStep[zID] = qStep_Next
        --TransmitVLQuestModData()
        sendServerCommand("NPCQuestCM_C", "UpdateNPCCM_C", {qStep_Next = qStep_Next, zID = zID})
    else
        print("Server---UpdateNPC= no zombie zID")
    end
end

--Thay đổi trạng thái NPC trong danh sách chờ spawn QueueSpawnList  true/nil
local function ActiveNPCInQueueSpawn(player, qStep, active)
    if not qStep then return end
    local questMD = GetQuestGlobalModData(qStep)            
    if IsMultiplayerMode() then
        --kiểm tra nếu qStep của tất cả moddata player online đều false thì mới gán false, nếu có 1 true thì NPCsQueueToSpawn[qStep] luôn là true
        --để NPC luôn spawn khi có ít nhất 1 player là true
        if active == true then
            questMD.NPCsQueueToSpawn[qStep] = active
        else
            local players = getOnlinePlayers()
            for i = 0, players:size() - 1 do
                local _player = players:get(i)
                local playerGMD = GetPlayerGlobalModData(_player)
                if playerGMD.Quest then
                    for q, data in pairs(playerGMD.Quest) do
                        local active = data[qStep].active
                        --nếu bước nhiệm vụ đang kích hoạt
                        if active and active == true then
                            local isQStepNPC = data[qStep].isNPC
                            --nếu bước nhiệm vụ là loại có NPC
                            if isQStepNPC and isQStepNPC == true then
                                questMD.NPCsQueueToSpawn[qStep] = true
                                --TransmitVLQuestModData()
                                sendServerCommand("NPCQuestCM_C", "ActiveNPCInQueueSpawnCM_C", {qStep = qStep, active = true})
                                print("Server---NPC Active State In NPCsQueueToSpawn[qStep]= ", qStep, questMD.NPCsQueueToSpawn[qStep]) 
                                return
                            end
                        end
                    end
                end
            end
            --khi tất cả moddata player đều false/nil thì nil
            questMD.NPCsQueueToSpawn[qStep] = nil
            print("Server---NPC Active State In NPCsQueueToSpawn[qStep]= ", qStep, questMD.NPCsQueueToSpawn[qStep]) 
        end
    else    
        questMD.NPCsQueueToSpawn[qStep] = active
        print("Server---NPC Active State In NPCsQueueToSpawn[qStep]= ", qStep, active) 
    end
    --TransmitVLQuestModData()
    sendServerCommand("NPCQuestCM_C", "ActiveNPCInQueueSpawnCM_C", {qStep = qStep, active = active})
end


--================= ONCLIENTCOMMAND =================--
VLOCC["NPCQuestCM"]["RemoveNPC"] = function(player, args) 	--{zID = zID, qStep = qStep}
	--xóa zombie NPC
	RemoveZombieAndSpawnedData(args.zID, args.qStep)
end

VLOCC["NPCQuestCM"]["RemoveNPCWhenFarAwayPlayers"] = function(player, args)    --{zID = zID, qStep = qStep, maxDist = maxDist}
	--xóa zombie NPC nếu không có player nào trong phạm vi maxDist
	if IsMultiplayerMode() then
		local hasPlayerNearNPC = IsAnyPlayerNearObjectByXY(args.zID, nil, nil, args.maxDist)                                         
		if not hasPlayerNearNPC then
			RemoveZombieAndSpawnedData(args.zID, args.qStep)
		end
	else
		RemoveZombieAndSpawnedData(args.zID, args.qStep)
	end
end

VLOCC["NPCQuestCM"]["GetOrCreatePlayerGMD"] = function(player, args)    --{pID = nil}
	local playerGMD = GetPlayerGlobalModData(player)
	if playerGMD then
		sendServerCommand(player, "NPCQuestCM_C", "GetOrCreatePlayerGMD_C", playerGMD)
	end
end

VLOCC["NPCQuestCM"]["RequestSpawnNPC"] = function(player, args)    --{qStep = qStep, pID = pID, outfit = outfit, x = x, y = y, z = z, removeZRange = 5, femaleChance = femaleChance}
	local qStep = args.qStep
	local questMD = GetQuestGlobalModData(qStep)
	if not questMD.NPC[qStep]["spawned"] then
		local range = args.removeZRange or 5
		local femaleChance = args.femaleChance or 0
		local sq = getCell():getGridSquare(args.x, args.y, args.z)
		--print("Server-----------RequestSpawnNPC= ", sq) 
		if sq then
			RemoveZombieFromSquares(sq, range)
			DelayFunction(function()
				SpawnZombieNPCQuest({pID = args.pID, qStep = qStep, x = args.x, y = args.y, z = args.z, num = 1, outfit = args.outfit, femaleChance = femaleChance, crawler = false, isFallOnFront = false, isFakeDead = false, knockedDown = false, isInvulnerable = true, isSitting = false, health = 5})
			end, 15)
		end
	end
end
 
VLOCC["NPCQuestCM"]["ActiveNPCInQueueSpawnCM"] = function(player, args)    --{qStep = qStep, active = active}
	ActiveNPCInQueueSpawn(player, args.qStep, args.active)
end

VLOCC["NPCQuestCM"]["UpdateQuestModData"] = function(player, args)     --{qStep = qStep, modDataKey = modDataKey, val = val}
	local qStep = args.qStep
	local questMD = GetQuestGlobalModData(qStep)
	local modDataKey = args.modDataKey
	local val = args.val
	if modDataKey == "turnToZombie" then
		if questMD.NPC[qStep][modDataKey] ~= val then
			questMD.NPC[qStep][modDataKey] = val
		end
	elseif modDataKey == "AddedEventObjects" or modDataKey == "AddedBuilding" then
		if not questMD.Object[qStep] then questMD.Object[qStep] = {} end
		if questMD.Object[qStep][modDataKey] ~= val then
			questMD.Object[qStep][modDataKey] = val
		end
		
	end
	sendServerCommand(player, "NPCQuestCM_C", "UpdateQuestModData_C", args)
end

VLOCC["NPCQuestCM"]["RemoveZombiesInAreaExceptNPC"] = function(player, args)   --{x1 = 12553, y1 = 1628, x2 = 12561, y2 = 1635, z = 6}
	RemoveZombiesInAreaExceptNPC(args.x1, args.y1, args.x2, args.y2, args.z)
end

VLOCC["NPCQuestCM"]["UpdateNPCToNextQStep"] = function(player, args)   --{qStep_Next = qStep_Next, zID = zID}
	UpdateNPC(args.qStep_Next, args.zID)
end

VLOCC["NPCQuestCM"]["SetDefaultZombie"] = function(player, args)   --{zID = zID}
    local zID = args.zID
    local zombie = GetZombieByOnlineIDOrUID(zID)
    if zombie then
        SetZombieDefault(zombie) 
        sendServerCommand("NPCQuestCM_C", "SetDefaultZombie_C", {zID = zID})
    end
end

VLOCC["NPCQuestCM"]["KillNPC"] = function(player, args)   --{qStep = qStep, zID = zID}
	KillNPCQuest(args.qStep, args.zID)
end


