#include "../../config/events.lua"
#include "../../script/helpers/utils.lua"
#include "../../script/helpers/entity.lua"
#include "../../script/helpers/mathfr.lua"

joints = nil
barrel = nil
barrelBody = nil
shapes = nil
impulseStrength = nil
maxMass = nil
WATER_LEAK_TIMER = nil
WATER_LEAK_MAX = nil
snd = nil
isDamaged = nil
completed = nil
emitPos = nil
q = nil
vehiclesIndexes = nil
vehiclesHit = nil

function init()
    joints = FindJoints("joiner")
    barrel = FindShapes("barrel")
    barrelBody = GetShapeBody(barrel[1])
    shapes = FindShapes()

    impulseStrength = 8000
    maxMass = 10000

    WATER_LEAK_TIMER = 0
    WATER_LEAK_MAX = 2
	snd = LoadLoop("firehydrant-loop.ogg")
	RegisterListenerTo(EVENTS.GAMEMODE_INIT, "OnGamemodeInit")
end

function OnGamemodeInit()
	UnregisterListener(EVENTS.GAMEMODE_INIT, "OnGamemodeInit")
    vehiclesIndexes = GetVehiclePlayers()
end

function tick(dt)
    if completed then return end
    if isDamaged then
        if WATER_LEAK_TIMER < WATER_LEAK_MAX then
            PlayLoop(snd, emitPos, q)
        else
            completed = cEntity:EnqueueDeletion()
        end
    end
end

function GetVehiclePlayers()
    local t = {}
    local list = ListKeys("level.players")
    for i = 1, #list do
        t[GetInt("level.players." .. i)] = i
    end
    return t
end


function update(dt)
    if completed then return end
    if isDamaged then
        if WATER_LEAK_TIMER < WATER_LEAK_MAX then
			WATER_LEAK_TIMER = WATER_LEAK_TIMER + dt
            SpawnParticles()
        end
        if nextTicks then
            checkBarrelHit()
            nextTicks = nextTicks - 1
            if nextTicks < 0 then nextTicks = nil end
        end
        return
    end

    for i = 1, #barrel do
        if IsShapeBroken(barrel[i]) then
            for j = 1, #joints do
                Delete(joints[j])
            end
            isDamaged = true
            break
        end
    end

    if not isDamaged then return end

    for i = 1, #shapes do
        local body = GetShapeBody(shapes[i])

        if not HasTag(body, "barrel") then
            utils:LimitVelocity(body, 5, 5)
        end
    end

    ImpulseCenter()
    if vehiclesIndexes then
        checkBarrelHit()
        nextTicks = WATER_LEAK_MAX * 60
    end
end

local function rdm(min, max)
    return math.random(min or 0, max or 1)
end

local function VecRdm(min, max)
    return Vec(rdm(min, max),rdm(min, max),rdm(min, max))
end

function SpawnParticles()
    emitPos = TransformToParentPoint(GetBodyTransform(barrelBody), GetBodyCenterOfMass(barrelBody))

    q = 1.0 - WATER_LEAK_TIMER / WATER_LEAK_MAX
    local v = q * 30.0 + 2.0
    local r = q * 0.4 + 0.1
    local pc = math.ceil(q * 5) + 3
    ParticleType("plain")
    ParticleTile(1)
    ParticleColor(0.9, 0.95, 1.0)
    ParticleAlpha(1.0, 0.0)
    ParticleGravity(-15, -5, "easeout")
    ParticleStretch(q * 10, 0)
    ParticleCollide(0, 1, "constant", 0.2, 0.1)
    ParticleSticky(0.25)

    if not IsBodyDynamic(barrelBody) then
        local hit, cp = GetBodyClosestPoint(barrelBody, VecAdd(emitPos, Vec(0, 1.5, 0)))
        if hit then
            emitPos = cp
        end
    end
    for i = 1, pc do
        ParticleRadius(r, r * 3)
        local vel = VecScale(Vec(rdm(-0.25, 0.25), -1, rdm(-0.25, 0.25)), rdm(0.7, 1.0) * v)
        local pos = VecAdd(emitPos, VecRdm(-2.5, 2.5))
        SpawnParticle(pos, vel, q + rdm(2.0, 6.0))
    end
end

function ImpulseCenter()
    for i = 1, #barrel do
        local body = GetShapeBody(barrel[i])

        local bmi, bma = GetBodyBounds(body)
        local bc = VecLerp(bmi, bma, 0.5)
        local dir = Vec(0, 1, 0.1)

        local mass = GetBodyMass(body)
        local massScale = 1 - math.min(mass/maxMass, 1.0)
        local strength = impulseStrength * massScale
        local add = VecScale(dir, strength)

        ApplyBodyImpulse(body, bc, add)
    end
end

function checkBarrelHit()
    vehiclesHit = vehiclesHit or {}
    local wb = GetWorldBody()
    for i = 1, #barrel do
        local body = GetShapeBody(barrel[i])
        if body ~= wb then
            checkVehicleHit(body)
        end
    end
end

function checkVehicleHit(body)
    WatchBodyHit(body)
    if not IsBodyHitted(body) then return end

    for index = 0, GetBodyHitsCount(body) - 1 do
        local other_body, impulse, pos, normal, this_vel, other_vel = GetBodyHit(body, index)
        local veh = GetBodyVehicle(other_body)
        impulse = math.abs(impulse)
        if veh > 0 and HasTag(veh, "customhealth") and impulse >= 50 and not vehiclesHit[veh] and GetVehicleHealth(veh) > 0 then
            local dmg = math:MapRangeClamped(math.sqrt(impulse), 10, 50, 0, 0.1)
            if dmg > 0 then
                TriggerEvent(EVENTS.UPDATE_HEALTH, veh .. ";" .. dmg)
                vehiclesHit[veh] = true
            end
        end
    end
end
