--[[
#include script/Automatic.lua
#include script/LnL.lua
#include script/SHRED.lua
#include script/Helper.lua
]]

function init()
    Tool = LnLInitializeTool('scrap22', 'MOD/xml/tool.xml', 'Scrap-22', 3)

    Keyframes = {
        Deploy = Transform(Vec(0.45, -0.9, 0.3), QuatEuler(-60, 70, 30)),
        Default = Transform(Vec(0.24, -0.33, -0.75), QuatEuler(0, 0, 10)),
        Focused = Transform(Vec(0.13625, -0.2325, -0.675), QuatEuler(2.5, 0, 30)),
        Inspect = Transform(Vec(0.1, -0.1, -0.8), QuatEuler(40, 60, -2.5)),
        Inspect_2 = Transform(Vec(0.125, -0.235, -0.75), QuatEuler(30, -25, 70)),
        Prepare = Transform(Vec(0.3, 0.2, -0.4), QuatEuler(60, 5, -5)),
        Prepare_2 = Transform(Vec(-0.3, -0.2, -0.6), QuatEuler(75, 20, 45)),
        Prepare_3 = Transform(Vec(0.4, -0.3, -0.5), QuatEuler(-20, -15, 5)),
        Smack = Transform(Vec(0.0, -0.4, -1.5), QuatEuler(45, 15, -15)),
        Smack_2 = Transform(Vec(0.2, -0.05, -1.5), QuatEuler(0, 0, 80)),
        Smack_3 = Transform(Vec(-0.1, 0.15, -1.5), QuatEuler(0, 0, -10)),
    }
    
    SM_Statblock = {
        Default = {
            pos = { 'AutoSM_Define',     Vec(),  1.75,  0.475, 0.2 },
            rot = { 'AutoSM_DefineQuat', Quat(), 2.125, 0.5,   0.8 },
        },
        Focused = {
            pos = { 'AutoSM_Define',     Vec(),  1.8,   0.5,   0.5 },
            rot = { 'AutoSM_DefineQuat', Quat(), 2.2,   0.6,   0.6 },
        },
        Inspect = {
            pos = { 'AutoSM_Define',     Vec(),  2,     0.5,   0   },
            rot = { 'AutoSM_DefineQuat', Quat(), 2.2,   0.525, 0   },
        },
        Inspect_2 = {
            pos = { 'AutoSM_Define',     Vec(),  2,     0.5,   0   },
            rot = { 'AutoSM_DefineQuat', Quat(), 2.2,   0.525, 0   },
        },
        Prepare = {
            pos = { 'AutoSM_Define',     Vec(),  2,     0.5,   0   },
            rot = { 'AutoSM_DefineQuat', Quat(), 2.2,   0.525, 0   },
        },
        Prepare_2 = {
            pos = { 'AutoSM_Define',     Vec(),  2,     0.5,   0   },
            rot = { 'AutoSM_DefineQuat', Quat(), 2.2,   0.525, 0   },
        },
        Prepare_3 = {
            pos = { 'AutoSM_Define',     Vec(),  2,     0.5,   0   },
            rot = { 'AutoSM_DefineQuat', Quat(), 2.2,   0.525, 0   },
        },
        Smack = {
            pos = { 'AutoSM_Define',     Vec(),  2.5,   0.5,   1   },
            rot = { 'AutoSM_DefineQuat', Quat(), 2.8,   0.525, 1   },
        },
        Smack_2 = {
            pos = { 'AutoSM_Define',     Vec(),  2.5,   0.5,   1   },
            rot = { 'AutoSM_DefineQuat', Quat(), 2.8,   0.525, 1   },
        },
        Smack_3 = {
            pos = { 'AutoSM_Define',     Vec(),  2.5,   0.5,   1   },
            rot = { 'AutoSM_DefineQuat', Quat(), 2.8,   0.525, 1   },
        },
    }

    SM = false
    function RefreshSM(keyframe_id)
        stat = SM_Statblock[keyframe_id]

        if not SM then
            SM = {}

            for key, k in pairs(stat) do
                SM[key] = _G[k[1]](unpack(k, 2))
            end
        else
            for key, k in pairs(stat) do
                AutoSM_RecalculateK(SM[key], unpack(k, 3))
            end
        end
    end

    RefreshSM('Default')

    Keybinds = {
        Shoot = 'usetool',
        Focus = 'grab',
        Inspect = 'alt',
        Smack = 'mmb'
    }

    Mem = {
        Last_keyframe_id = 'Default',
        Time = {
            Shoot_L = -1 / 0,
            Trigger_L = -1 / 0,
            Inspect_L = -1 / 0,
            Focus_L = -1 / 0,
            Heat_L = -1 / 0,
            Release_Smack = -1 / 0,
            Throw_Gun_L = -1 / 0
        },
        Update_Info = {
            pcam_transform = GetPlayerCameraTransform(),
            sm_target_pos = GetPlayerCameraTransform().pos
        },
        Pulled_Trigger = false,
        Throwing_Gun = false,
        Extra_Trigger_Pulls = 0,
        Smack_Keyframe = 'Smack',
        Needs_To_Release_Smack = false,
        Movement = 0,
        Heat = 0,
        Last_Tool = false,
        Grab_Comp = 0,
        Thrown_Guns = {}
    }

    Stats = {
        Trigger_Pull_Time = 1 / 15,
        Shoot_Interval = 0.08,
        Overheat_Max = 15,
        Throw_Gun_Time = 0.03,
        Smack_Duration = 0.2
    }

    Sounds = {
        StartSmack = LoadSound('throw/m0.ogg', 5.5),
        Smack = LoadSound('bullet-hit0.ogg', 10),
        Folley = LoadSound('MOD/snd/folley0', 5.5),
        Roll = LoadSound('MOD/snd/roll0', 7.5),
        SlideSharp = LoadSound('MOD/snd/slide-sharp0', 7.5),
        SlideSoft = LoadSound('MOD/snd/slide-soft0', 7.5),
        Tink = LoadSound('MOD/snd/tink0.ogg', 10),
        DoubleTap = LoadSound('MOD/snd/doubletap0.ogg', 5),
        Shoot = LoadSound('MOD/snd/shoot0.ogg', 15),
        GasLarge = LoadSound('gas-l0.ogg', 7.5),
        GasSmall = LoadSound('gas-s0.ogg', 7.5),
    }
end

function handleCommand(cmd)
    if cmd == 'quickload' then init() end
end

function tick(dt)
    local Time = GetTime()

    local camera = Transform(Mem.Update_Info.pcam_transform.pos, GetPlayerCameraTransform().rot)
    local camera_fwd = AutoTransformFwd(camera)
    
    Mem.Heat = Mem.Heat * (1 - 3 * dt)

    local selected, canusetool, current_tool = LnLSelected(Tool)
    if selected then
        ------------------------------------------------ INITIAL SETUP ------------------------------------------------
        local p_vel = GetPlayerVelocity()
        local p_speed = VecLength(p_vel)
        local p_speed_01 = math.min(p_speed, 7.5) / 7.5
        local p_compensation = VecScale(p_vel, 0.067)

        local nozzle = LnLGetBoneWorldTransform(Tool, 'hull.nozzle')
        local nozzle_fwd = AutoTransformFwd(nozzle)
        
        local aimassist = LnLGetBoneWorldTransform(Tool, 'hull.aimassist')
        local overheat = LnLGetBoneWorldTransform(Tool, 'hull.overheat')

        local keyframe_id = 'Default'
        
        if InputDown(Keybinds.Smack) and not Mem.Needs_To_Release_Smack then
            if GetPlayerGrabBody() == 0 then
                if InputDown(Keybinds.Inspect) then
                    keyframe_id = 'Prepare_3'
                    Mem.Smack_Keyframe = 'Smack_3'
                elseif InputDown(Keybinds.Focus) then
                    keyframe_id = 'Prepare_2'
                Mem.Smack_Keyframe = 'Smack_2'
                else
                    keyframe_id = 'Prepare'
                    Mem.Smack_Keyframe = 'Smack'
                end
            else
                if InputDown(Keybinds.Inspect) then
                    keyframe_id = 'Prepare_2'
                    Mem.Smack_Keyframe = 'Smack_2'
                else
                    keyframe_id = 'Prepare'
                    Mem.Smack_Keyframe = 'Smack'
                end
            end
            
            if InputPressed(Keybinds.Shoot) then
                AutoSM_AddVelocity(SM.pos, QuatRotateVec(camera.rot, Vec(0, 6, -35)))

                Mem.Throwing_Gun = true
                Mem.Time.Throw_Gun_L = Time
                Mem.Needs_To_Release_Smack = true
            end
        elseif InputReleased(Keybinds.Smack) and not Mem.Needs_To_Release_Smack then
            Mem.Time.Release_Smack = Time

            if Mem.Smack_Keyframe == 'Smack_3' then
                AutoSM_AddVelocity(SM.rot, QuatEuler(-30, 10, -10))
            elseif Mem.Smack_Keyframe == 'Smack_2' then
                AutoSM_AddVelocity(SM.pos, QuatRotateVec(camera.rot, Vec(-10, 4, -5)))
                AutoSM_AddVelocity(SM.rot, QuatEuler(-40))
            else
                AutoSM_AddVelocity(SM.rot, QuatEuler(-30, -10, -10))
            end

            PlaySound(Sounds.StartSmack, overheat.pos, 1.25)
        elseif Time - Mem.Time.Release_Smack <= Stats.Smack_Duration then
            keyframe_id = Mem.Smack_Keyframe

        elseif InputDown(Keybinds.Inspect) then
            if Mem.Last_keyframe_id:sub(1, 7) ~= 'Inspect' then
                Mem.Time.Inspect_L = Time
                PlaySound(Sounds.Folley, overheat.pos, 0.5)
            end

            if InputDown(Keybinds.Focus) then
                keyframe_id = 'Inspect_2'
            else
                keyframe_id = 'Inspect'
            end
    
            SetBool('hud.aimdot', false)
        else
            if InputDown(Keybinds.Focus) then
                if Mem.Last_keyframe_id ~= 'Focused' then
                    Mem.Time.Focus_L = Time
                end
    
                keyframe_id = 'Focused'
    
                SetBool('hud.aimdot', false)
            end
        end

        if InputReleased(Keybinds.Smack) then
            Mem.Needs_To_Release_Smack = false
        end

        local keyframe = LnLFOVToolTransformOffset(Keyframes[keyframe_id], -1)
        keyframe.rot = QuatRotateQuat(keyframe.rot, QuatEuler(
            AutoSigmoid(Time - Mem.Time.Inspect_L, 360, 12, 0) * 2,
            0,
            AutoSigmoid(Time - Mem.Time.Inspect_L, -60, 13, 0) * 2
        ))

        ------------------------------------------------ Stepping ------------------------------------------------
        Mem.Movement = Mem.Movement + p_speed * 3 * dt
        while Mem.Movement >= math.pi * 2 do
            Mem.Movement = Mem.Movement - math.pi * 2
        end

        ------------------------------------------------ Small Jiggles -- *nuzzles you with overdone animation* ------------------------------------------------
        do
            -- Camera Sway
            local p_delta = Vec(InputValue("camerax"), InputValue("cameray")) -- Position
            local r_delta = Vec(InputValue("cameray"), InputValue("camerax")) -- Rotation
            local compensated = QuatRotateVec(AutoQuatInverse(keyframe.rot), r_delta)
            AutoSM_AddVelocity(SM.pos, VecScale(p_delta, -100 * dt))
            AutoSM_AddVelocity(SM.rot, QuatEuler(unpack(VecScale(compensated, 1000 * dt)))) -- The unpack is just to convert it to a quaternion, nothing special

            -- Movement Bob
            local bob_vel = Vec(0, math.sin(Mem.Movement) * p_speed_01 * 4 * dt) -- Just makes the weapon bob up and down on a wave, scaled by the player velocity
            local bob_rot = QuatRotateVec(AutoQuatInverse(keyframe.rot),
                Vec(math.cos(Mem.Movement) * p_speed_01 * 16 * dt))              -- Just makes the weapon rotate up and down on a wave, scaled by the player velocity
            AutoSM_AddVelocity(SM.pos, bob_vel)
            AutoSM_AddVelocity(SM.rot, QuatEuler(unpack(bob_rot)))

            -- Movement Sway
            local p_vel_rel = AutoSwizzle(TransformToLocalVec(camera, p_vel), 'yx') -- Removes the z axis and swaps the x and y
            local modded = QuatRotateVec(AutoQuatInverse(keyframe.rot),
                AutoVecMulti(VecScale(p_vel_rel, 10 * dt), Vec(-1, 1, 1)))
            AutoSM_AddVelocity(SM.rot, QuatEuler(modded[1], modded[2], modded[3]))

            -- Aiming Transition
            if (Mem.Last_keyframe_id == 'Default' and keyframe_id == 'Focused') or (Mem.Last_keyframe_id == 'Focused' and keyframe_id == 'Default') then
                AutoSM_AddVelocity(SM.pos, Vec(0, 1))
                AutoSM_AddVelocity(SM.rot, QuatEuler(5, 0, 3))
            end

            -- Aim Jiggles
            AutoSM_AddVelocity(SM.rot, AutoRandomQuat(AutoSigmoid(Time - Mem.Time.Focus_L, 2, 10, 0) * 2))

            -- Heat Jiggles
            AutoSM_AddVelocity(SM.rot, AutoRandomQuat(Mem.Heat / 10))
        end

        ------------------------------------------------ DAKAKAKA ------------------------------------------------
        if InputPressed(Keybinds.Shoot) and not Mem.Throwing_Gun then
            PlaySound(Sounds.Folley)

            if Time - Mem.Time.Trigger_L >= Stats.Trigger_Pull_Time and Time - Mem.Time.Shoot_L >= Stats.Shoot_Interval then
                Mem.Pulled_Trigger = true
                Mem.Time.Trigger_L = Time
            else
                Mem.Extra_Trigger_Pulls = Mem.Extra_Trigger_Pulls + 1
            end
        end

        if Mem.Extra_Trigger_Pulls > 0 and not Mem.Pulled_Trigger then
            Mem.Extra_Trigger_Pulls = Mem.Extra_Trigger_Pulls - 1
            Mem.Pulled_Trigger = true
            Mem.Time.Trigger_L = Time
        end

        local shoot =
            Mem.Pulled_Trigger and
            Time - Mem.Time.Trigger_L >= Stats.Trigger_Pull_Time and
            Time - Mem.Time.Shoot_L > Stats.Shoot_Interval

        if shoot then
            local origin = VecCopy(nozzle.pos)
            local dir
            if AutoTableContains({ 'Inspect', 'Prepare', 'Prepare_2', 'Prepare_3', 'Smack', 'Smack_2', 'Smack_3' }, keyframe_id) or Time - Mem.Time.Inspect_L < 0.3 then
                dir = nozzle_fwd
            elseif keyframe_id == 'Focused' then
                dir = VecNormalize(VecSub(aimassist.pos, camera.pos))
            else
                QueryRejectBody(GetToolBody())
                QueryRequire('physical')
                local ray = AutoRaycast(camera.pos, camera_fwd, 128)

                local to_nozzle = Time - Mem.Time.Shoot_L < 0.225 and 0.5 or 0.1
                to_nozzle = math.min(to_nozzle + 1 / ray.dist, 1)

                dir = VecNormalize(VecLerp(VecNormalize(VecSub(ray.intersection, nozzle.pos)), nozzle_fwd, to_nozzle))
            end

            MakeHole(origin, 0.25, 0.2, 0, true)
            
            for i = 1, 3 do
                local offsetdir = i == 1 and dir or QuatRotateVec(AutoRandomQuat(3), dir)

                local proj = Proj(origin, offsetdir, SHRAPNEL_STATS.BaseSpeed, SHRAPNEL_STATS.BaseShrapnel)
                SHRAPNEL_TABLE[#SHRAPNEL_TABLE + 1] = proj
            end

            AutoSM_AddVelocity(SM.pos, QuatRotateVec(camera.rot, Vec(0, 1.1, 2)))
            AutoSM_AddVelocity(SM.rot, QuatEuler(6, -2.5))

            PlaySound(Sounds.Shoot, nozzle.pos, 0.55)
            PlaySound(Sounds.GasSmall, overheat.pos, Mem.Heat / Stats.Overheat_Max)
            Sparks(nozzle.pos, nozzle_fwd, 12, 20, 7, AutoColors.amber_light)
            ShakeCamera(0.325)

            ReleasePlayerGrab()

            Mem.Pulled_Trigger = false
            Mem.Time.Shoot_L = Time
            Mem.Heat = Mem.Heat + 5

            if Mem.Heat > Stats.Overheat_Max then
                LnLDropTool(false, true, 15)
                SetString('game.player.tool', 'none')

                Explosion(overheat.pos, 0.6)
                PlaySound(Sounds.GasLarge, overheat.pos, 1.5)
                Mem.Heat = 0
            end
        end

        ------------------------------------------------ Fun ------------------------------------------------
        if Mem.Heat > 1 then
            if Time - Mem.Time.Heat_L > 0.067 then
                OverheatParticles(overheat.pos, AutoVecRnd(Vec(0, 2), 1), math.min(Mem.Heat * 0.1, 1))
                Mem.Time.Heat_L = Time
            end
        end

        do
            local glow_strength = (Mem.Heat / Stats.Overheat_Max) ^ 5 + AutoSigmoid(Time - Mem.Time.Shoot_L, 8, 50, 0) * 2
            PointLight(overheat.pos, AutoColors.amber_light[1], AutoColors.amber_light[2], AutoColors.amber_light[3],
                glow_strength)

            DrawBodyHighlight(GetToolBody(), AutoSigmoid(Mem.Heat, 10, -2.5, 13))

            -- PlayLoop(Sounds.Gas, overheat.pos, 1)
        end

        -- if InputDown('mmb') then SetTimeScale(0.15) dt = dt * (1 - 0.15 + 1) end

        ------------------------------------------------ SECONDARY MOTION SYSTEM UPDATING ------------------------------------------------
        if Mem.Last_keyframe_id ~= keyframe_id then
            RefreshSM(keyframe_id)
            Mem.Last_keyframe_id = keyframe_id
        end

        local world_keyframe = TransformToParentTransform(camera, keyframe)
        world_keyframe.pos = VecAdd(world_keyframe.pos, p_compensation)
        Mem.Update_Info.sm_target_pos = VecCopy(world_keyframe.pos)

        AutoSM_Update(SM.rot, world_keyframe.rot, dt)

        ------------------------------------------------ APPLYING TRANSFORMATION ------------------------------------------------
        local sm_transform = Transform(AutoSM_Get(SM.pos), AutoSM_Get(SM.rot))
        local sm_local_transform = TransformToLocalTransform(camera, sm_transform)

        local trigger_t = Mem.Pulled_Trigger and (Time - Mem.Time.Trigger_L) / Stats.Trigger_Pull_Time or 0
        LnLSetTransformation(Tool, 'hull.trigger', Transform(Vec(), QuatEuler(0, 0, trigger_t * 15)))

        LnLApplyRig(Tool)

        Mem.Grab_Comp = AutoLerp(Mem.Grab_Comp, GetPlayerGrabBody() == 0 and 0 or 1, dt * 8)
        sm_local_transform.pos = VecAdd(sm_local_transform.pos, Vec(0, Mem.Grab_Comp ^ 2))

        local applied_tool_transform = LnLFOVToolTransformOffset(sm_local_transform)
        SetToolTransform(applied_tool_transform, 0)

        ------------------------------------------------ Throw ------------------------------------------------
        if Mem.Throwing_Gun and Time - Mem.Time.Throw_Gun_L >= Stats.Throw_Gun_Time then
            local body = LnLDropTool(false, true, 18)
            
            SetBodyVelocity(body, AutoSM_GetVelocity(SM.pos))
            SetBodyAngularVelocity(body, AutoSM_GetVelocity(SM.rot))
            -- SetBodyVelocity(body, VecAdd(AutoSM_GetVelocity(SM.pos), QuatRotateVec(camera.rot, Vec(0, 1, -8))))
            -- SetBodyAngularVelocity(body, AutoVecRnd(AutoSM_GetVelocity(SM.rot), 5))
            SetTag(body, Tool.id .. 'Thrown')
            Mem.Thrown_Guns[#Mem.Thrown_Guns+1] = body
            
            PlaySound(Sounds.StartSmack, overheat.pos, 1.25)
            
            SetString('game.player.tool', Tool.id)
            Rest()

            return
        end

        ------------------------------------------------ Smack ------------------------------------------------
        if AutoTableContains({ 'Smack', 'Smack_2' ,'Smack_3' }, keyframe_id) then
            QueryRejectBody(GetToolBody())
            QueryRequire('physical')
            local q = AutoQueryClosest(sm_transform.pos, 0.3)
    
            local vel = AutoSM_GetVelocity(SM.pos)
            local vel_normalized = VecNormalize(vel)
            if q.hit then
                local mat = GetShapeMaterialAtPosition(q.shape, q.point)

                MakeHole(q.point, 0.225, 0.175, 0, true)
                ConstrainVelocity(q.body, 0, q.point, vel_normalized, VecLength(vel) / GetBodyMass(q.body) * 50)

                if HasTag(GetShapeBody(q.shape), Tool.id .. 'Thrown') then
                    Explosion(q.point, 0.8)
                    Delete(GetShapeBody(q.shape))
                end
                
                Sparks(q.point, q.normal, 10, 5, 5, AutoColors.amber_light)
                PlaySound(MaterialSoundBank[mat].medium, q.point, 0.8)
                PlaySound(Sounds.Smack, q.point, 0.8)
                ShakeCamera(0.55)

                AutoSM_SetVelocity(SM.pos, VecScale(q.normal, 5))
                Mem.Time.Release_Smack = -1 / 0
            end
        end
    else
        Rest()
    end
    
    Mem.Last_Tool = current_tool
end

function update(dt)
    Mem.Update_Info.pcam_transform = GetPlayerCameraTransform()

    AutoSM_Update(SM.pos, Mem.Update_Info.sm_target_pos, dt)
    
    Sim(dt)

    for _, proj in pairs(SHRAPNEL_TABLE) do
        ProjectileTrail(proj.pos, proj.fwd, AutoColors.amber_light)
        OverheatParticles(proj.pos, AutoVecRnd(VecScale(proj.fwd, -2), 1), 0.5)
    end

    -- for i, b in pairs(Mem.Thrown_Guns) do
    --     QueryRejectBody(b)
    --     QueryRequire('physical large')
    --     local center = AutoBodyCenter(b)
    --     local q = AutoQueryClosest(center, 0.35)
    --     if q.hit then
    --         Explosion(center, 0.6)
    --         Delete(b)
            
    --         Mem.Thrown_Guns[b] = nil
    --     end
    -- end
end

function draw(dt)
    if LnLSelected(Tool) then
        local t = GetTime() - Mem.Time.Shoot_L
        if t < 0.03125 then
            
            UiBlur(0.1)
            -- UiBlur(AutoSigmoid(Time - Mem.Time.Shoot_L, 1, 1000))
        end
    end
    
    -- UiAlign('center middle')
	-- for _, proj in pairs(SHRAPNEL_TABLE) do
	-- 	local ui_pos = { UiWorldToPixel(proj.pos) }
	-- 	UiPush()
	-- 		UiTranslate(ui_pos[1], ui_pos[2])
	-- 		AutoUiCircle(16 / ui_pos[3], 2, 8)
	-- 	UiPop()
	-- 	-- AutoUIArrowInWorld(proj.pos, AutoVecMove(proj.pos, proj.fwd, 2), 1, 0.5)
	-- end
end