local parent = require("tacz_manual_action_state_machine")

local STATIC_TRACK_LINE = parent.STATIC_TRACK_LINE
local MAIN_TRACK = parent.MAIN_TRACK
local BLENDING_TRACK_LINE = parent.BLENDING_TRACK_LINE
local MOVEMENT_TRACK = parent.MOVEMENT_TRACK
local movement_track_states = parent.movement_track_states
local RUN_END_HAND_TIME = 1300

local run_state = setmetatable({}, {__index = movement_track_states.run})
local walk_state = setmetatable({}, {__index = movement_track_states.walk})

local function isMainTrackStopped(context)
    return context:isStopped(context:getTrack(STATIC_TRACK_LINE, MAIN_TRACK))
end

function run_state.entry(this, context)
    local state = this.movement_track_states.run
    state.mode = -1
    state.time = context:getCurrentTimestamp()
    state.exit_reason = nil
    state.use_bipod = true
    context:runAnimation("run_start_bipod", context:getTrack(BLENDING_TRACK_LINE, MOVEMENT_TRACK), true, PLAY_ONCE_HOLD, 0.2)
end

function run_state.exit(this, context)
    local state = this.movement_track_states.run
    state.exit_time = context:getCurrentTimestamp()
    if (state.exit_reason == "aim") then
        state.use_bipod = false
        context:runAnimation("run_end", context:getTrack(BLENDING_TRACK_LINE, MOVEMENT_TRACK), true, PLAY_ONCE_HOLD, 0.3)
    else
        context:runAnimation("run_end_bipod", context:getTrack(BLENDING_TRACK_LINE, MOVEMENT_TRACK), true, PLAY_ONCE_HOLD, 0.3)
    end
end

function run_state.update(this, context)
    local track = context:getTrack(BLENDING_TRACK_LINE, MOVEMENT_TRACK)
    local state = this.movement_track_states.run

    if (context:isHolding(track)) then
        context:runAnimation("run_bipod", track, true, LOOP, 0.2)
        state.mode = 0
        context:anchorWalkDist()
    end

    if (state.mode ~= -1) then
        if (not context:isOnGround()) then
            if (state.mode ~= 1) then
                state.mode = 1
                context:runAnimation("run_hold_bipod", track, true, LOOP, 0.6)
            end
        else
            if (state.mode ~= 0) then
                state.mode = 0
                context:runAnimation("run_bipod", track, true, LOOP, 0.2)
            end
            context:setAnimationProgress(track, (context:getWalkDist() % 2.0) / 2.0, true)
        end
    end
end

function run_state.transition(this, context, input)
    local state = this.movement_track_states.run
    local aiming = context:isAiming() or context:getAimingProgress() > 0
    if (input == INPUT_IDLE) then
        if (aiming) then
            state.exit_reason = "aim"
        else
            state.exit_reason = "idle"
        end
        return this.movement_track_states.idle
    elseif (not isMainTrackStopped(context)) then
        state.exit_reason = "busy"
        return this.movement_track_states.walk
    elseif (input == INPUT_WALK) then
        if (aiming) then
            state.exit_reason = "aim"
        else
            state.exit_reason = "walk"
        end
        return this.movement_track_states.walk
    end
end

function walk_state.entry(this, context)
    movement_track_states.walk.entry(this, context)
    local run = this.movement_track_states.run
    local track = context:getTrack(BLENDING_TRACK_LINE, MOVEMENT_TRACK)
    local elapsed = context:getCurrentTimestamp() - (run.exit_time or 0)
    this.movement_track_states.walk.wait_run_end_bipod = (
        run.exit_time ~= nil
        and run.use_bipod
        and run.exit_reason ~= "busy"
        and elapsed < RUN_END_HAND_TIME
        and not context:isHolding(track)
        and not context:isStopped(track)
    )
end

function walk_state.update(this, context)
    local walk = this.movement_track_states.walk
    local run = this.movement_track_states.run
    if (walk.wait_run_end_bipod) then
        local elapsed = context:getCurrentTimestamp() - (run.exit_time or 0)
        local track = context:getTrack(BLENDING_TRACK_LINE, MOVEMENT_TRACK)
        if (elapsed < RUN_END_HAND_TIME and not context:isHolding(track) and not context:isStopped(track)) then
            return
        end
        walk.wait_run_end_bipod = false
    end
    movement_track_states.walk.update(this, context)
end

local M = setmetatable({
    movement_track_states = setmetatable({
        run = run_state,
        walk = walk_state
    }, {__index = movement_track_states})
}, {__index = parent})

return M
