local TargetZombie = {}

local function pMod(a, b)
    if a % b >= 0 then
        return a % b
    else
        return a % b + b
    end
end

local function vehicleDirection(x, y, z)
    if Math.abs(x) >= 45 and Math.abs(x) >= 45 then
        return 90 - y
    else
        return 270 + y
    end
end

local function HaveAmmo(vehicle)
    local id = vehicle:getId()
    if NoAmmoTurret[id] then
        NoAmmoTurret[id] = nil
    end
end
local function NoAmmo(vehicle)
    local id = vehicle:getId()
    local NoAmmoText = TextDrawObject.new()
    NoAmmoText:setDefaultColors(1, 1, 0.1, 0.7)
    NoAmmoText:setOutlineColors(0, 0, 0, 1)
    local AimBox = getText("IGUI_WQ_NoAmmo")
    NoAmmoText:ReadString(UIFont.Medium, AimBox, -1)
    local Targetsquare = vehicle:getSquare()
    local sx = IsoUtils.XToScreen(Targetsquare:getX(), Targetsquare:getY(), Targetsquare:getZ(), 0);
    local sy = IsoUtils.YToScreen(Targetsquare:getX(), Targetsquare:getY(), Targetsquare:getZ(), 0);
    sx = sx - IsoCamera.getOffX()
    sy = sy - IsoCamera.getOffY()
    -- sy = sy - 64
    sx = sx / getCore():getZoom(0)
    sy = sy / getCore():getZoom(0)
    -- sy = sy - NoAmmoText:getHeight()
    NoAmmoTurret[id] = {NoAmmoText, sx, sy}

end

local function SetAngleClose(modData, ShouldSetangle, TurretPart, vehicleUnit, TurretType, RotateTick)
    if modData.RotateTick == nil then
        modData.RotateTick = 0
    end
    local FpsNow = getAverageFPS()
    local MoreTick = FpsNow / 60
    if modData.RotateTick > 0 then
        modData.RotateTick = modData.RotateTick - 1
        TurretPart:setModelVisible(TurretType .. modData.OldAngle, true)
        return
    end
    modData.RotateTick = MoreTick + RotateTick
    if ShouldSetangle > modData.OldAngle then
        if ShouldSetangle - modData.OldAngle <= 180 then
            modData.OldAngle = pMod(modData.OldAngle + 5, 360)
        else
            modData.OldAngle = pMod(modData.OldAngle - 5, 360)
        end
    elseif ShouldSetangle < modData.OldAngle then
        if modData.OldAngle - ShouldSetangle <= 180 then
            modData.OldAngle = pMod(modData.OldAngle - 5, 360)
        else
            modData.OldAngle = pMod(modData.OldAngle + 5, 360)
        end
    else
    end
    TurretPart:setModelVisible(TurretType .. modData.OldAngle, true)
    sendClientCommand("TurretSendAll", "SetUpANewAngle", {
        vehicleId = vehicleUnit:getId(),
        Angle = modData.OldAngle,
        TurretType = TurretType
    })
end

local function SetAngle(Zombie, VechileSq, VechileUnit, ThisTurret)
    local modData = VechileUnit:getModData()
    if modData.OldAngle == nil then
        modData.OldAngle = 0
    end
    local ZombieX = Zombie:getX()
    local ZombieY = Zombie:getY()
    local VechileX = VechileSq:getX()
    local VechileY = VechileSq:getY()
    local ShouldSetangle = math.atan2(ZombieY - VechileY, ZombieX - VechileX)
    ShouldSetangle = math.floor((math.deg(ShouldSetangle) + 180) % 360)
    ShouldSetangle = math.floor(ShouldSetangle / 5) * 5
    local playerobj = getSpecificPlayer(0)
    local playerangle = playerobj:getForwardDirection():getDirection()
    local diff = 0
    ShouldSetangle = pMod(math.floor((360 - ShouldSetangle) -
                                         (360 -
                                             vehicleDirection(VechileUnit:getAngleX(), VechileUnit:getAngleY(),
                VechileUnit:getAngleZ())) + 0.5), 360)
    ShouldSetangle = math.floor(ShouldSetangle / 5) * 5
    local TurretPart = VechileUnit:getPartById(ThisTurret.TurretType)
    -- print("ShouldSetangle:" .. ShouldSetangle, "  OldAngle:" .. modData.OldAngle)
    if TurretPart and TurretPart:getInventoryItem() then
        TurretPart:setAllModelsVisible(false)
        if modData.OldAngle >= ShouldSetangle - 3 and modData.OldAngle <= ShouldSetangle + 3 then
            modData.AimReady = true
            TurretPart:setModelVisible(ThisTurret.TurretType .. modData.OldAngle, true)
        else
            modData.AimReady = false
            SetAngleClose(modData, ShouldSetangle, TurretPart, VechileUnit, ThisTurret.TurretType, ThisTurret.RotataTick)
        end
    end
end

local function getDistanceBetween(x1, y1, x2, y2)
    local dx = x1 - x2
    local dy = y1 - y2
    return math.sqrt(dx * dx + dy * dy)
end

local function tableContains(tab, val)
    for i, v in ipairs(tab) do
        if v == val then
            return true, i
        end
    end
    return false, nil
end
local function TurretWorking(playerObj, vehicleUnit, TurretIndex, TurretCount)
    local currentSq = vehicleUnit:getSquare()
    local ThisTurret = AutoTurretList[vehicleUnit:getScriptName()]
    if not ThisTurret then
        return
    end
    local MainGunPart = vehicleUnit:getPartById(ThisTurret.TurretType)
    if not MainGunPart or not MainGunPart:getInventoryItem() then
        return
    end
    local cx = currentSq:getX()
    local cy = currentSq:getY()
    local cz = currentSq:getZ()
    local dx, dy = 0, 0
    local modData = vehicleUnit:getModData()
    if modData.ReadyToFire == nil then
        modData.ReadyToFire = false
        modData.TargetZombie = {}
        modData.FireTick = ThisTurret.FireTick

        modData.OldAngle = 0
    end
    -- 找到最近的僵尸-最大范围为20格
    local AmmoBox = vehicleUnit:getPartById("AmmoCan")
    if AmmoBox and AmmoBox:getInventoryItem() then
        local AmmoBoxInv = AmmoBox:getItemContainer()
        if AmmoBoxInv:getNumberOfItem(ThisTurret.AmmoType) == 0 then
            modData.ReadyToFire = false
            if SandboxVars.AutoTurret.InfintyAmmo then
                modData.ReadyToFire = true
            end
        else
            modData.ReadyToFire = true
        end
    else
        modData.ReadyToFire = false
    end
    if not modData.ReadyToFire then
        NoAmmo(vehicleUnit)
        if modData.OldAngle == nil then
            modData.OldAngle = 0
        end
        local TurretPart = vehicleUnit:getPartById(ThisTurret.TurretType)
        if TurretPart and TurretPart:getInventoryItem() then
            TurretPart:setAllModelsVisible(false)
            TurretPart:setModelVisible(ThisTurret.TurretType .. modData.OldAngle, true)
        end
        return
    end
    HaveAmmo(vehicleUnit)
    modData.TargetZombieList = {}
    for i = 0, getCell():getZombieList():size() - 1 do
        local zombie = getCell():getZombieList():get(i)
        local zx = zombie:getX()
        local zy = zombie:getY()
        local distance = getDistanceBetween(cx, cy, zx, zy)
        if distance <= ThisTurret.Range and zombie:getHealth() > 0 and not zombie:isDead() then
            -- print("Find Zombie")
            table.insert(modData.TargetZombieList, zombie)
        end

        if #modData.TargetZombieList >= TurretCount + 1 then
            break
        end
    end
    local GrapleTarget = playerObj:getGrapplingTarget()
    if GrapleTarget then
        local Find, index = tableContains(modData.TargetZombieList, GrapleTarget)
        if Find and #modData.TargetZombieList >= 1 and index then
            table.remove(modData.TargetZombieList, index)
        end
    end
    if #modData.TargetZombieList == 0 then
        if modData.OldAngle == nil then
            modData.OldAngle = 0
        end
        local TurretPart = vehicleUnit:getPartById(ThisTurret.TurretType)
        if TurretPart and TurretPart:getInventoryItem() then
            TurretPart:setAllModelsVisible(false)
            TurretPart:setModelVisible(ThisTurret.TurretType .. modData.OldAngle, true)
        end
        return
    end
    if not modData.TargetZombieUnit then
        if #modData.TargetZombieList > TurretIndex then
            modData.TargetZombieUnit = modData.TargetZombieList[TurretIndex]
        else
            modData.TargetZombieUnit = modData.TargetZombieList[1]
        end
    end
    if not modData.TargetZombieUnit then
        return
    end
    if modData.TargetZombieUnit:getHealth() <= 0 or modData.TargetZombieUnit:getHealth() == nil then
        modData.TargetZombieUnit = nil
        return
    end
    SetAngle(modData.TargetZombieUnit, currentSq, vehicleUnit, ThisTurret)

    -- FullAmmo(TurretIndex)
    if modData.ReadyToFire and modData.AimReady then
        local FpsNow = getAverageFPS()
        local MoreTick = FpsNow / 120

        if modData.FireTick > 0 then
            modData.FireTick = modData.FireTick - 1
        else
            modData.FireTick = ThisTurret.FireTick * MoreTick
            if modData.TargetZombieUnit ~= nil then
                Turretfire(playerObj, vehicleUnit, modData.TargetZombieUnit, ThisTurret)
                local AmmoBox = vehicleUnit:getPartById("AmmoCan")
                local AmmoBoxInv = AmmoBox:getItemContainer()
                AmmoBoxInv:RemoveOneOf(ThisTurret.AmmoType)
            end
        end
    end
end

local TurretLightSquare = {}

local function CheckTurretLight(vehicleUnit)
    local TurretLight = vehicleUnit:getPartById("TurretLight")
    if TurretLight and TurretLight:getInventoryItem() then
        local currentSq = vehicleUnit:getSquare()
        local VehicleId = vehicleUnit:getId()
        if not TurretLightSquare[VehicleId] then
            TurretLightSquare[VehicleId] = {
                sq = currentSq,
                LightSource = nil
            }
        end
    end
end

local function TooDark(sq)
    return (forageSystem.getTimeOfDay() == "isNight");
end

local function TurretLightUpdate()
    for i, v in pairs(TurretLightSquare) do
        local sq = v.sq
        if not sq then
            if v.LightSource then
                getCell():removeLamppost(v.LightSource)
            end
            TurretLightSquare[i] = nil
        else
            if not v.LightSource then
                if TooDark(sq) then

                    local newlight = IsoLightSource.new(sq:getX(), sq:getY(), sq:getZ(), 0.4, 0.8, 0.4, 8)
                    getCell():addLamppost(newlight)
                    v.LightSource = newlight
                end
            else
                if not TooDark(sq) then
                    getCell():removeLamppost(v.LightSource)
                    v.LightSource = nil
                end
            end
        end
    end
end

Events.EveryOneMinute.Add(TurretLightUpdate)

local function UpdateLocalTurret()
    local playerObj = getSpecificPlayer(0)
    local VehicleList = playerObj:getCell():getVehicles()
    for i = 0, VehicleList:size() - 1 do
        local vehicleUnit = VehicleList:get(i)

        if string.find(vehicleUnit:getScriptName(), "AutoTurret") then
            local isClosest = false
            CheckTurretLight(vehicleUnit)
            if not isServer() then
                isClosest = true
            else
                local currentSq = vehicleUnit:getSquare()
                local cx = currentSq:getX()
                local cy = currentSq:getY()

                local PlayerListg = getOnlinePlayers()
                local myX = playerObj:getX()
                local myY = playerObj:getY()
                local myDis = getDistanceBetween(cx, cy, myX, myY)

                for j = 0, PlayerListg:size() - 1 do
                    local playerObjg = PlayerListg:get(j)

                    if playerObj:getUsername() ~= playerObjg:getUsername() then
                        local currentSqg = playerObjg:getCurrentSquare()
                        local zx = currentSqg:getX()
                        local zy = currentSqg:getY()
                        local distance = getDistanceBetween(cx, cy, zx, zy)

                        if distance <= myDis then
                            isClosest = true
                            break
                        end
                    end
                end
            end
            if isClosest then
                TurretWorking(playerObj, vehicleUnit, i + 1, VehicleList:size() - 1)
            end
        end
    end
end

Events.OnPlayerUpdate.Add(UpdateLocalTurret)

