import { CanBeLanded, CanPlayAnticipation, Entity } from '../types/Entity';
import AnimationProgressHook from '../utils/hooks/AnimationProgressHook';
import HooksChain from '../utils/hooks/HooksChain';
import BaseSystem from './BaseSystem';

export type AnticipationSymbolsSystemCompatibleEntity = Entity & CanPlayAnticipation & CanBeLanded;

export default class AnticipationSymbolsSystem extends BaseSystem
{
    static override requiredFlags: string[] = [];
    static override requiredMaps: string[] = ['anticipatingReels', 'symbols'];
    static override requiredInputs: string[] = [];
    static override requiredMethods: string[] = [];
    static override requiredUtils: string[] = [];
    static override sharedSystemData: string[] = ['spinSystemState', 'currentStoppingReel', 'anticipatingSymbolsTests'];

    public state: 'active' | 'anticipating' | 'stopped' = 'stopped';
    private symbolsIDs: string[] = [];
    private hooks: HooksChain[] = [];

    override onEnter()
    {
        this.state = 'active';
        this.symbolsIDs = [];
    }

    override onLeave() {}

    update(dt: number): void
    {
        if (this.state === 'active')
        {
            if (this.isAnticipatingReel())
            {
                this.state = 'anticipating';
            }
        }
        if (this.state === 'anticipating')
        {
            this.animateSymbols();
        }
        if (this.isStopConditionMet())
        {
            this.state = 'stopped';
            this.hooks = [];
        }

        if (this.machine.sharedSystemData.spinSystemState === 'stopped')
        {
            this.state = 'stopped';
            this.hooks = [];
        }

        this.hooks = this.hooks.filter((hook) =>
        {
            hook.update(dt);

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

    private isStopConditionMet()
    {
        return this.state === 'anticipating'
      && this.machine.sharedSystemData.currentStoppingReel === this.machine.config.reels
      && !this.machine.maps.anticipatingReels[this.machine.config.reels - 1];
    }

    private animateSymbols()
    {
        this.machine.maps.symbols.forEach((id: string) =>
        {
            if (!id) return;
            if (this.symbolsIDs.includes(id)) return;
            const entity: AnticipationSymbolsSystemCompatibleEntity = this.machine.getChildByName(id);

            if (!entity) return;
            if (this.machine.sharedSystemData.anticipatingSymbolsTests.some((t: RegExp) => t.test(entity.key)))
            {
                const chain = new HooksChain()
                    .addHook(new AnimationProgressHook(() => entity.anticipate()))
                    .setOnComplete(() =>
                    {
                        this.symbolsIDs.splice(this.symbolsIDs.indexOf(id), 1);
                    });

                this.symbolsIDs.push(id);
                this.hooks.push(chain);
            }
        });
    }

    private isAnticipatingReel()
    {
        return this.machine.maps.anticipatingReels.some((v: boolean) => v);
    }
}
