local anim = require('openmw.animation')
local self = require('openmw.self')
local auxUtil = require('openmw_aux.util')

local playBlendedHandlers = {}
local function onPlayBlendedAnimation(groupname, options)
    auxUtil.callEventHandlers(playBlendedHandlers, groupname, options)
end

local animationEndedHandlers = {}
local function onAnimationEnded(groupname, startKey, stopKey, time, completion)
    auxUtil.callEventHandlers(animationEndedHandlers, groupname, {
        startKey = startKey,
        stopKey = stopKey,
        time = time,
        completion = completion,
    })
end

local function playBlendedAnimation(groupname, options)
    onPlayBlendedAnimation(groupname, options)
    if options.skip then
        return
    end
    anim.playBlended(self, groupname, options)
end

local textKeyHandlers = {}
local function onAnimationTextKey(groupname, key)
    auxUtil.callMultipleEventHandlers({ textKeyHandlers[groupname], textKeyHandlers[''] }, groupname, key)
end

local initialized = false

local function onUpdate(dt)
    if dt <= 0 then
        return
    end

    -- The script is loaded before the actor's CharacterController object is initialized, therefore
    -- we have to delay this initialization step or the call won't have any effect.
    if not initialized then
        self:_enableLuaAnimations(true)
        initialized = true
    end
end

return {
    engineHandlers = { 
        _onPlayAnimation = playBlendedAnimation,
        _onAnimationEnded = onAnimationEnded,
        _onAnimationTextKey = onAnimationTextKey,
        onUpdate = onUpdate,
    },
    
    interfaceName = 'AnimationController',
    --- 
    -- Animation controller interface
    -- @module AnimationController
    -- @context local
    -- @usage local anim = require('openmw.animation')
    -- local I = require('openmw.interfaces')
    --
    -- -- play spellcast animation 
    -- I.AnimationController.playBlendedAnimation('spellcast', { startkey = 'self start', stopkey = 'self stop', priority = {
    --      [anim.BONE_GROUP.RightArm] = anim.PRIORITY.Weapon,
    --      [anim.BONE_GROUP.LeftArm] = anim.PRIORITY.Weapon,
    --      [anim.BONE_GROUP.Torso] = anim.PRIORITY.Weapon,
    --      [anim.BONE_GROUP.LowerBody] = anim.PRIORITY.WeaponLowerBody
    --      } })
    -- 
    -- @usage -- react to the spellcast release textkey
    -- I.AnimationController.addTextKeyHandler('spellcast', function(groupname, key)
    --     -- Note, Lua is 1-indexed so have to subtract 1 less than the length of 'release'
    --     if key.sub(key, #key - 6) == 'release' then
    --         print('Abra kadabra!')
    --     end
    -- end)
    --
    -- @usage -- Add a text key handler that will react to all keys
    -- I.AnimationController.addTextKeyHandler('', function(groupname, key)
    --     if key.sub(key, #key - 2) == 'hit' and not key.sub(key, #key - 7) == ' min hit' then
    --         print('Hit!')
    --     end
    -- end)
    -- 
    -- @usage -- Make a handler that changes player attack speed based on current fatigue
    -- I.AnimationController.addPlayBlendedAnimationHandler(function (groupname, options)
    --     local stop = options.stopkey
    --     if #stop > 10 and stop.sub(stop, #stop - 10) == ' max attack' then
    --         -- This is an attack wind up animation, scale its speed by attack 
    --         local fatigue = Actor.stats.dynamic.fatigue(self)
    --         local factor = 1 - fatigue.current / fatigue.base
    --         speed = 1 - factor * 0.8
    --         options.speed = speed
    --     end
    -- end)
    -- 
    
    interface = {
        --- Interface version
        -- @field [parent=#AnimationController] #number version
        version = 1,
        
        --- AnimationController Package
        -- @type Package
        
        --- Make this actor play an animation. Makes a call to @{openmw.animation#playBlended}, after invoking handlers added through addPlayBlendedAnimationHandler
        -- @function [parent=#AnimationController] playBlendedAnimation
        -- @param #string groupname The animation group to be played
        -- @param #table options The table of play options that will be passed to @{openmw.animation#playBlended}
        playBlendedAnimation = playBlendedAnimation,

        --- Add a new playBlendedAnimation handler for this actor
        -- If `handler(groupname, options)` returns false, other handlers for
        -- the call will be skipped.
        -- @function [parent=#AnimationController] addPlayBlendedAnimationHandler
        -- @param #function handler The handler.
        addPlayBlendedAnimationHandler = function(handler)
            playBlendedHandlers[#playBlendedHandlers + 1] = handler
        end,

        --- Add a new animationEnded handler for this actor
        -- If `handler(groupname, info)` returns false, other handlers for
        -- the call will be skipped. info is a table that contains information related to
        -- the animation that ended and will contain the following fields:
        --
        --   * `time` - The absolute time in the animation when it was ended
        --   * `completion` - The relative time (0-1) in the animation when it was ended
        --   * `startKey` - The start key of the animation that ended
        --   * `stopKey` - The stop key of the animation that ended
        --
        -- @function [parent=#AnimationController] addAnimationEndedHandler
        -- @param #function handler The handler.
        addAnimationEndedHandler = function(handler)
            animationEndedHandlers[#animationEndedHandlers + 1] = handler
        end,

        --- Add a new text key handler for this actor
        -- While playing, some animations emit text key events. Register a handle to listen for all
        -- text key events associated with this actor's animations.
        -- If `handler(groupname, key)` returns false, other handlers for
        -- the call will be skipped.
        -- @function [parent=#AnimationController] addTextKeyHandler
        -- @param #string groupname Name of the animation group to listen to keys for. If it is an empty string or nil, all keys will be received
        -- @param #function handler The handler.
        addTextKeyHandler = function(groupname, handler)
            if not groupname then
                groupname = ""
            end
            local handlers = textKeyHandlers[groupname]
            if handlers == nil then
                handlers = {}
                textKeyHandlers[groupname] = handlers
            end
            handlers[#handlers + 1] = handler
        end,
    },

    eventHandlers = {
        AddVfx = function(data)
            anim.addVfx(self, data.model, data.options)
        end,
    }
}
