import Reel from './Reel';
import { Graphics } from 'pixi.js';

/**
 * SingleSlotReel: per-cell reel anchored to a specific grid slot (row, column).
 */
import { ReelsSpinSystemCompatibleEntity } from '../ReelsSpinSystem';

export default class SingleSlotReel extends Reel {
    private maskGraphics?: Graphics;

    constructor(target: any, index: number) {
        super(target, index);

        this.createMask();
    }

    /** Returns the symbol to the pool and removes it from the stage. */
    private releaseSymbol(symbol: ReelsSpinSystemCompatibleEntity): void {
        symbol.isBlurred = false;
        symbol.filters = null;
        symbol.blurEnabled = false;
        symbol.cacheAsBitmap = false;
        symbol.filterArea = null;
        this.target.machine.removeChild(symbol);
        if (!this.target.pool.add(symbol.key, symbol)) {
            symbol.destroy();
        }
    }

    private createMask() {
        // Create a red rectangle graphics matching the slot size
        const slotWidth = this.target.machine.utils.grid.getSlotWidth();
        const slotHeight = this.target.machine.utils.grid.getSlotHeight();

        const gr = new Graphics();
        gr.beginFill(0xff0000, 1);
        // Centered rectangle relative to graphics position
        gr.drawRect(-slotWidth / 2, -slotHeight / 2, slotWidth, slotHeight);
        gr.endFill();

        // Position to match reel coordinates (column center, row center)
        const col = this.getColumnIndex();
        const halfSlotWidth = slotWidth * 0.5;
        gr.x = halfSlotWidth + (col * slotWidth);
        gr.y = this.getBaseY();

        // Mask should be hidden by default (visible only while spinning)
        gr.visible = false;

        // Add to machine container
        this.target.machine.addChild(gr);

        this.maskGraphics = gr;
    }

    /**
     * Apply or remove mask for a given symbol depending on current state.
     * Mask is enabled for all states except Bouncing and Stopped; in those states it is removed.
     * Also toggles the mask graphics visibility accordingly.
     */
    private updateMask(symbol: ReelsSpinSystemCompatibleEntity): void {
        // Mask should be enabled in all states except Bouncing and Stopped
        const stateName = this.currentState?.name;
        const shouldMask = stateName !== 'Bouncing' && stateName !== 'Stopped';

        // Toggle mask graphics visibility based on state
        if (this.maskGraphics) {
            this.maskGraphics.visible = shouldMask;
        }

        if (shouldMask && this.maskGraphics) {
            symbol.mask = this.maskGraphics;
            symbol.isMasked = true;
        } else {
            symbol.mask = null as any;
            symbol.isMasked = false;
        }
    }

    protected override getSlotsCount(): number {
        // Ensure SingleSlotReel manages exactly one visible slot
        return 1;
    }

    protected override getColumnIndex(): number {
        const reels = this.target.machine.config.reels;
        return this.index % reels;
    }

    protected override getBaseY(): number {
        const slotHeight = this.target.machine.utils.grid.getSlotHeight();
        const halfSlotHeight = slotHeight / 2;
        const reels = this.target.machine.config.reels;
        const row = Math.floor(this.index / reels);
        return halfSlotHeight + row * slotHeight;
    }

    // Override position switching to strictly manage a single symbol per reel
    // Using `any` to access base class private members without changing base visibility
    protected override _handleSwitchingPosition(): void {
        if (!this._isReelSpinning()) return;

        const slots = 1;
        const currentPos = Math.ceil(this.position);

        if (this._prevPosition === currentPos) return;

        for (let i = 0; i < slots; i++) {
            if (this.symbols.findIndex(([pos]) => pos === currentPos + i) >= 0) continue;
            this._addRandomSymbol(currentPos + i);
        }

        this._prevPosition = currentPos;
    }

    // Ensure random symbols also respect the mask immediately
    protected override _addRandomSymbol(position: number): void {
        const randomKey = this.target.getRandomSymbolKey();
        const symbol: ReelsSpinSystemCompatibleEntity = this.target.getSymbol(randomKey);
        this._setSymbolX(symbol);
        this.updateMask(symbol);
        this.symbols.push([position, symbol]);
        this.target.machine.addChild(symbol);
    }

    // Ensure mask is applied immediately when symbols are added via system
    public override addSymbols(symbols: ReelsSpinSystemCompatibleEntity[], position: number): void {
        const toAdd = symbols || [];
        toAdd.forEach((symbol, i) => {
            this._setSymbolX(symbol);
            this.updateMask(symbol);
            this.symbols.push([position + i, symbol]);
            this.target.machine.addChild(symbol);
        });
    }

    /**
     * Removes all scheduled symbols at or beyond the provided position to avoid duplicates before stopping.
     */
    public trimSymbolsFrom(position: number): void {
        this.symbols = this.symbols.filter(([symbolPosition, symbol]) => {
            if (symbolPosition < position) {
                return true;
            }
            this.releaseSymbol(symbol);
            return false;
        });
    }

    /** Keeps only the symbol that matches the provided stop position. */
    public retainOnlySymbolAt(position: number): void {
        this.symbols = this.symbols.filter(([symbolPosition, symbol]) => {
            const keep = symbolPosition === position;
            if (!keep) {
                this.releaseSymbol(symbol);
            }
            return keep;
        });
        this._prevPosition = position;
    }

    // Override symbols update to work with per-slot reel
    protected override _updateSymbols(): void {
        const slotHeight = this.target.machine.utils.grid.getSlotHeight();
        const baseY = this.getBaseY();

        this.symbols.forEach(([position, symbol]) => {
            const distance = this.position - position;
            symbol.y = (distance * slotHeight) + baseY;
            this._setSymbolX(symbol);
            // Apply or remove per-slot mask depending on current state
            this.updateMask(symbol);
        });

        this.symbols = this.symbols.filter(([position, symbol]) => {
            if (position <= this.position - 1) {
                this.target.machine.removeChild(symbol);
                if (!this.target.pool.add(symbol.key, symbol)) {
                    symbol.destroy();
                }
                return false;
            }
            return true;
        });
    }
}
