import BaseMachine from '../slotMachines/BaseMachine';
import BaseSystem from './BaseSystem';
import { CanBeBlocked, CanPlayIdle, Entity, ValueAssignable } from '../types/Entity';

/**
 * Represents an entity compatible with the ReelsSpinSystem.
 * This entity must be able to be blocked, play idle animations, play bouncing animations, and be blurred.
 */
export type StickySymbolsSystemCompatibleEntity = Entity
    & CanBeBlocked
    & CanPlayIdle
    & ValueAssignable;

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

/** Represents a stick symbol system that manages the behavior of sticky symbols. */
export default class StickySymbolsSystem extends BaseSystem<StickySymbolsSystemSettings>
{

    static defaultSettings: StickySymbolsSystemSettings = {
        zIndex: 4,
    };
    /** Static list of flags that the machine should provide. Subclasses can override to specify their own requirements. */
    static override requiredFlags: string[] = ['isSpinning'];

    /** Static list of maps that the machine should provide. Subclasses can override to specify their own requirements. */
    static override requiredMaps: string[] = ['symbols', 'stickySymbols'];

    /** Static list of inputs that the machine should provide. Subclasses can override to specify their own requirements. */
    static override requiredInputs: string[] = [];

    /** Static list of methods that the machine should provide. Subclasses can override to specify their own requirements. */
    static override requiredMethods: string[] = ['getChildByName'];

    /** Static list of utils that the machine should provide. Subclasses can override to specify their own requirements. */
    static override requiredUtils: string[] = ['grid'];

    public state: 'watching' | 'stopped' = 'stopped';

    private stickySymbols: (StickySymbolsSystemCompatibleEntity | null)[] = [];

    constructor(machine: BaseMachine)
    {
        super(machine);
        this.setSettings(StickySymbolsSystem.defaultSettings);
    }

    onEnter()
    {
        this.state = 'watching';
    }

    onLeave()
    {
        this.stickySymbols.forEach((symbol, i) =>
        {
            if (symbol)
            {
                this._removeStickySymbol(i);
            }
        });
    }

    update(_dt: number): void
    {
        this.machine.maps.stickySymbols.forEach((isSticky: boolean, mapIndex: number) =>
        {
            if (isSticky && !this.stickySymbols[mapIndex])
            {
                this._addStickySymbol(mapIndex);
            } else if (!isSticky && this.stickySymbols[mapIndex])
            {
                this._removeStickySymbol(mapIndex);
            }
        });

        this._hideSymbolsBehindStickySymbols();
        this._updateZIndex();
        if (!this.machine.flags.isSpinning) this.state = 'stopped';
    }

    _addStickySymbol(i: number): void
    {
        const originalSymbol: StickySymbolsSystemCompatibleEntity = this.machine.getChildByName(this.machine.maps.symbols[i]);
        const stickySymbol: StickySymbolsSystemCompatibleEntity = this.machine.createSymbol(originalSymbol.key);
        stickySymbol.position.copyFrom(originalSymbol.position);
        stickySymbol.value = originalSymbol.value;
        this.machine.addChild(stickySymbol);
        this.stickySymbols[i] = stickySymbol;
    }


    _removeStickySymbol(i: number): void
    {
        this.stickySymbols[i].destroy();
        this.stickySymbols[i] = null;
        this.machine.getChildByName(this.machine.maps.symbols[i]).visible = true;
    }

    _hideSymbolsBehindStickySymbols(): void
    {
        this.stickySymbols.forEach((stickySymbol, i) =>
        {
            if (!stickySymbol) return;

            const symbol = this.machine.getChildByName(this.machine.maps.symbols[i]);
            if (!symbol) return;

            symbol.visible = false;
        });
    }

    _updateZIndex(): void
    {
        this.stickySymbols.forEach(symbol =>
        {
            if (!symbol) return;

            symbol.zIndex = this.settings.zIndex;
        });
    }
}
