import { DisplayObject } from 'pixi.js';
import { MotionBlurFilter } from '@pixi/filter-motion-blur';
import BaseSystem from './BaseSystem';
import BaseMachine from '../slotMachines/BaseMachine';
import { CanBeBlurred } from '../types/Entity';

/** Describes motion blur configuration parameters. */
export interface BlurSymbolsSystemSettings
{
    velocity: [number, number];
    kernelSize: number;
    offset: number;
}

/** Coordinates blur enabling and disabling for symbol entities. */
export default class BlurSymbolsSystem extends BaseSystem<BlurSymbolsSystemSettings>
{
    /** Current lifecycle state for the blur system. */
    public state: 'running' | 'stopped' = 'stopped';
    /** Shared motion blur filter instance applied to eligible symbols. */
    private readonly blurFilter: MotionBlurFilter = new MotionBlurFilter([0, -50], 15, 7);

    /** Builds a blur system scoped to the provided machine. */
    constructor(machine: BaseMachine)
    {
        super(machine);
        this.settings = {
            velocity: [0, -50],
            kernelSize: 15,
            offset: 7,
        } satisfies BlurSymbolsSystemSettings;
        this.blurFilter.padding = 0;
        this.applyFilterSettings();
    }

    /** Updates blur configuration using the provided settings. */
    public override setSettings(newSettings: Partial<BlurSymbolsSystemSettings>): void
    {
        const mergedSettings: BlurSymbolsSystemSettings = {
            ...this.settings,
            ...newSettings,
        };

        if (newSettings.velocity)
        {
            mergedSettings.velocity = [...newSettings.velocity] as [number, number];
        }

        this.settings = mergedSettings;
        this.applyFilterSettings();
    }

    /** @inheritdoc */
    override onEnter(): void
    {
        this.state = 'stopped';
        this.refreshBlurState();
    }

    /** @inheritdoc */
    override onLeave(): void
    {
        this.state = 'stopped';
        this.clearBlurFromChildren();
    }

    /** @inheritdoc */
    override update(_dt: number): void
    {
        this.machine.children.forEach((child) =>
        {
            if (!this.isBlurCapableEntity(child)) return;

            if (child.isBlurred)
            {
                this.applyBlur(child);
                return;
            }

            this.removeBlur(child);
        });
    }

    /** Applies the blur effect when not already active. */
    private applyBlur(entity: CanBeBlurred): void
    {
        if (entity.blurEnabled) return;

        entity.filters = [this.blurFilter];
        entity.blurEnabled = true;
        entity.cacheAsBitmap = false;
    }

    /** Disables the blur effect when it is currently active. */
    private removeBlur(entity: CanBeBlurred): void
    {
        if (!entity.blurEnabled) return;

        entity.filters = null;
        entity.blurEnabled = false;
        entity.cacheAsBitmap = false;
    }

    /** Ensures a consistent blur state for all managed children. */
    private refreshBlurState(): void
    {
        this.machine.children.forEach((child) =>
        {
            if (!this.isBlurCapableEntity(child)) return;

            if (child.isBlurred)
            {
                this.applyBlur(child);
                return;
            }

            this.removeBlur(child);
        });
    }

    /** Clears blur from all tracked entities when the system stops. */
    private clearBlurFromChildren(): void
    {
        this.machine.children.forEach((child) =>
        {
            if (!this.isBlurCapableEntity(child)) return;
            this.removeBlur(child);
        });
    }

    /** Applies the configured filter options to the shared blur filter. */
    private applyFilterSettings(): void
    {
        const { velocity, kernelSize, offset } = this.settings;
        this.blurFilter.velocity.set(...velocity);
        this.blurFilter.kernelSize = kernelSize;
        this.blurFilter.offset = offset;
    }

    /** Type guard ensuring the display object supports blur toggling. */
    private isBlurCapableEntity(child: DisplayObject): child is DisplayObject & CanBeBlurred
    {
        const entity = child as CanBeBlurred;
        return typeof entity.isBlurred === 'boolean'
            && typeof entity.blurEnabled === 'boolean';
    }
}
