import BaseMachine from '../slotMachines/BaseMachine';
import AnimationProgressHook from '../utils/hooks/AnimationProgressHook';
import HooksChain from '../utils/hooks/HooksChain';
import BaseSystem from './BaseSystem';

/** Configuration settings for the reel spinning behavior in the ReelsSpinSystem. */
export interface CelebrateSystemSettings
{
    celebrateAnimationName: string,
    zIndex: number,
}

/** Enum representing different states of the celebrate process. */
const STATES = {
    CELEBRATING: 'celebrating',
    STOPPED: 'stopped',
};

/**
 * @class AnticipationSystem
 * @extends BaseSystem<CelebrateSystemSettings>
 * @description Handles the spinning behavior of ExpandedSymbol entities in a slot machine game,
 * including the initialization, updating, and stopping of expanded symbols.
 */
export default class CelebrateSystem extends BaseSystem<CelebrateSystemSettings>
{
    /** Required maps used in this system */
    static override requiredMaps: string[] = ['tinting', 'symbols'];

    private state: string;

    /** Stores hooks for tracking animation progress */
    private hooks: HooksChain[] = [];

    /**
     * Default configuration settings for reel spin behavior.
     * @type {ReelsSpinSystemSettings}
     */
    static defaultSettings: CelebrateSystemSettings = {
        celebrateAnimationName: 'win',
        zIndex: 1000,
    };

    /**
     * Initializes the reel spin system with specified machine settings and prepares reels.
     * @class
     * @param {BaseMachine} machine - The slot machine instance that the spin system controls.
     */
    constructor(machine: BaseMachine)
    {
        super(machine);
        this.setSettings(CelebrateSystem.defaultSettings);
        this.state = STATES.STOPPED;
    }

    /**
     * Handles the actions to be performed when entering the spin state.
     * Initializes the state to STARTING, and resets the starting and stopping reels, as well as the elapsed time.
     */
    override onEnter(): void
    {
        this.state = STATES.CELEBRATING;
        this.hooks = [];
        this.playCelebrateAnimations();
    }

    /**
     * Updates the state of the spin system based on the elapsed time and current state.
     * @param dt - The delta time since the last update, in milliseconds.
     */
    update(dt: number): void
    {
        if (this.state === STATES.CELEBRATING)
        {
            this.processHooks(dt);
            if (this.hooks.length === 0)
            {
                this.machine.maps.tinting = this.machine.maps.symbols.map(() => false);
                this.state = STATES.STOPPED;
            }
        }
    }

    playCelebrateAnimations(): void
    {
        if (!this.machine.inputs.celebrateSymbolIndexes) return;
        this.machine.maps.tinting = this.machine.maps.symbols.map(() => true);
        this.machine.inputs.celebrateSymbolIndexes.forEach((index: number) =>
        {
            const entity = this.machine.getChildByName(this.machine.maps.symbols[index]);
            const prevZIndex = entity.zIndex;

            entity.zIndex = this.settings.zIndex;

            this.machine.maps.tinting[index] = false;

            const symbolChain = new HooksChain()
                .addHook(new AnimationProgressHook(() => (entity as any)[this.settings.celebrateAnimationName]()))
                .setOnComplete(() =>
                {
                    entity.zIndex = prevZIndex;
                });

            this.hooks.push(symbolChain);
        });
    }

    /**
     * Processes the hooks by updating them with the given delta time and filtering out the completed ones.
     * @param {number} dt - The delta time to update the hooks.
     * @returns {void}
     */
    processHooks(dt: number): void
    {
        this.hooks = this.hooks.filter((hook) =>
        {
            hook.update(dt);

            return !hook.isComplete();
        });
    }

    /**
     * Handles the actions to be performed when leaving the current state.
     * @returns {void}
     */
    onLeave(): void
    {
    }
}
