require "vl_quest_utils"
require "vl_quest_zombie_npc_utils"
-- require "vl_quest_player_utils"

VLPL = VLPL or {}

assert(VLF,  "Stack trace: Missing dependency: VLF - vl_quest_player_utils.lua")
assert(VLZB,  "Stack trace: Missing dependency: VLZB - vl_quest_player_utils.lua")

local IsMultiplayerMode = VLF.IsMultiplayerMode
local GetNPCsByVarAndQStep = VLZB.GetNPCsByVarAndQStep

--lấy ID từ Player
function VLPL.GetPlayerID(player)
    if not player then return nil end
    local pID
    if IsMultiplayerMode() then
        pID = player:getOnlineID()
    else
        --pID = player:getPlayerNum()
        pID = 0
    end
    return pID
end
local GetPlayerID = VLPL.GetPlayerID

--lấy Player từ ID player
function VLPL.GetPlayerByID(pID)
    --if not pID then return nil end
    if IsMultiplayerMode() then
        return getPlayerByOnlineID(pID)
    else
        return getPlayer()
    end
    return nil
end

--Client only --Kiểm tra player đang ở trong khu vực xác định hay không
function VLPL.IsPlayerInArea(player, x, y, x2, y2, z)
	if not player then return false end
	if not x or not y or not x2 or not y2 then return false end
    if x > x2 or y > y2 then return false end
	local pX, pY, pZ = player:getX(), player:getY(), math.floor(player:getZ())
	if z then --Nếu có tọa độ z thì check phải cùng tầng z
		if (pX >= x and pX <= x2) and (pY >= y and pY <= y2) and pZ == z then
			return true
		end
	else
		if (pX >= x and pX <= x2) and (pY >= y and pY <= y2) then
			return true
		end
	end
	return false
end

--Client Only --Lấy local ModData của Player
function VLPL.GetPlayerModData(qStep)
    local player = getPlayer()
    if not player then return nil end
	local pData = player:getModData()
    if not pData.VL then pData.VL = {} end
	if not pData.VL.itemQuest then pData.VL.itemQuest = {} end
	if not pData.VL.time then pData.VL.time = {} end
    if not pData.VL.events then pData.VL.events = {} end
	
	if qStep then
		if not pData.VL.itemQuest[qStep] then pData.VL.itemQuest[qStep] = {} end
		if not pData.VL.time[qStep] then pData.VL.time[qStep] = {} end
		--if not pData.events[qStep] then pData.events[qStep] = {} end
	end
	return pData
end
local GetPlayerModData = VLPL.GetPlayerModData
 
--Client Only --Lấy local ModData Quest VL của Player
function VLPL.GetPlayerModDataQuest(qStep)
    local pDataQ = GetPlayerModData(qStep)
    return pDataQ.VL
end
  
--Kiểm tra player có đang nhìn thấy NPC không 	--nếu có zNPC thì không cần npcVariable, npcQStep
function VLPL.IsPlayerCanSeeNPC(player, zNPC, npcVariable, npcQStep)
	if not zNPC then
		local zNPCList = GetNPCsByVarAndQStep(npcVariable, npcQStep)
		zNPC = zNPCList and zNPCList[1]
	end
	if zNPC then
		if player:CanSee(zNPC) then
			return true
		end
	end
	return false
end

--Kiểm tra player có đang đeo Radio đang bật trên người không
local function PlayerHasAttachedActiveRadio(player)
    if not player then return nil end
	
	local function isRadio(item)
		if item and instanceof(item, "Radio") and item:getDeviceData() then
			if item:getDeviceData():getIsTurnedOn() then
				return true
			end
		end
		return false
	end
	
	local item = player:getPrimaryHandItem()
	if isRadio(item) == true then
		return item
	end
	
	item = player:getSecondaryHandItem()
	if isRadio(item) == true then
		return item
	end
	
--[[	
	--ERROR: các radio attach không bao giờ hết pin
	--FIXME: Xóa khi hoàn thiện
	local attachedItems = player:getAttachedItems()
	for i = 0, attachedItems:size() - 1 do
		local item = attachedItems:getItemByIndex(i)
		if isRadio(item) == true then
			return item
		end
	end
	]]
	
	--[[
	--ERROR khi cầm 2 radio 2 tay. Chỉ check được trên tay
	local radio = player:getEquipedRadio()
	if radio then
		if radio:getDeviceData():getIsTurnedOn() then
			return radio
		end
	end
	]]
	return nil
end

--Tìm Radio đang TurnOn quanh player. Bán kình tùy loại Radio, max là 50.
--Tìm trong khoảng cách z tối đa +-2 tầng.
function VLPL.FindActiveRadiosAroundPlayer(player, radius)
    local radios = {}
    if not player then return radios end

    radius = radius or 30
    local cell = getCell()
    local px, py, pz = player:getX(), player:getY(), math.floor(player:getZ())

	for z = pz - 2, pz + 2 do
		for x = px - radius, px + radius do
			for y = py - radius, py + radius do
				local sq = cell:getGridSquare(x, y, z)
				if sq then
					--World objects (radio đặt sẵn bằng sprite)
					local objects = sq:getObjects()
					for i = 0, objects:size() - 1 do
						local obj = objects:get(i)

						if obj and instanceof(obj, "IsoRadio") and obj.getDeviceData then	--chỉ tìm Radio
						--if obj and obj.getDeviceData then
							local data = obj:getDeviceData()
							if data and data:getIsTurnedOn() then
								table.insert(radios, {
									item = obj,
									x = x,
									y = y,
									z = z
								})
							end
						end
					end
				
					--kiểm tra world item trên square
					local worldObjects = sq:getWorldObjects()
					if worldObjects then
						for i = 0, worldObjects:size() - 1 do
							local obj = worldObjects:get(i)
							if instanceof(obj, "IsoWorldInventoryObject") then
								local item = obj:getItem()
								
								if item and item.getDeviceData then
									local data = item:getDeviceData()
									if data and data:getIsTurnedOn() then
										table.insert(radios, {
											item = item,
											x = x,
											y = y,
											z = z
										})
									end
								end
							end
						end
					end
				end
			end
		end
	end
	
    return radios
end
local FindActiveRadiosAroundPlayer = VLPL.FindActiveRadiosAroundPlayer

--Kiểm tra player có đang nghe được Radio nào đang bật không
--Cách mỗi tầng thì +8 ô khoảng cách với Radio
function VLPL.PlayerCanHearAnyRadio(player)		--local isCanHear, radio = VLPL.PlayerCanHearAnyRadio(player)
	if not player or player:isAsleep() then return false, nil end
	
	--Radio gắn người		
	local radio = PlayerHasAttachedActiveRadio(player)
	if radio then
		--print("Client---PlayerCanHearAnyRadio---Player attached radio= ", radio)
		return true, radio
	end
	
	--Radio xung quanh
    local radios = FindActiveRadiosAroundPlayer(player, 50)
	--print("--------------#radios= ", #radios)
    if #radios == 0 then return false, nil end

    local px, py, pz = player:getX(), player:getY(), math.floor(player:getZ())
	local Z_PENALTY = 8  -- cách mỗi tầng +8 ô khoảng cách với Radio
	 
    for _, r in ipairs(radios) do
        local data = r.item:getDeviceData()
        if data then
            --local range = data:getTransmitRange() --khoảng cách nhận tín hiệu từ nguồn khác
			local range = data:getDeviceSoundVolumeRange()
            if range and range > 0 then
                local dx = px - r.x
                local dy = py - r.y
                local distXY = math.sqrt(dx*dx + dy*dy)
				
				local zDiff = math.abs(pz - r.z)
				local effectiveDist = distXY + (zDiff * Z_PENALTY)
				--print("Client---PlayerCanHearAnyRadio---Radio-Player dist: ", effectiveDist, " - Volume Range: ", range, " - zDiff: ", zDiff)
				
                if effectiveDist <= range then
                    return true, r.item
                end
            end
        end
    end

    return false, nil
end

--Server ONLY --Kiểm tra có bất kì player nào cách tọa độ x, y 1 khoảng = range ô không
function VLPL.IsAnyPlayerNearXY(x, y, range)
    local players = getOnlinePlayers()
    if not players then return false end

    for i = 0, players:size() - 1 do
        local player = players:get(i)
        if player then
            local px = player:getX()
            local py = player:getY()

            if math.abs(px - x) <= range and math.abs(py - y) <= range then
                return true
            end
        end
    end

    return false
end

--Hitreaction chung cho player
function VLPL.PlayerHitReaction(player)
	if not player then return end
	
	local handWeapon = HandWeapon.new("Base", "Temporary", "Weapon", "gun.png")
	local wielder = IsoPlayer.new(getCell())
	player:hitConsequences(handWeapon, wielder, true, 0, false)  --bRemote =  true -> Hit này đến từ network, đã xử lý rồi
	handWeapon = nil
	wielder = nil
end







