import { type ISize, Text } from 'pixi.js';

type TextStyleWithWrap = Text['style'] & {
    defaultWordWrapWidth?: number;
};

type ResizableText = Text & {
    style: TextStyleWithWrap;
};

/** Base strategy for fitting PIXI text into a bounding box. */
export abstract class TextScaler
{
    protected constructor(protected readonly textObject: ResizableText) {}

    /** Shrinks or stretches the text to match the provided dimensions. */
    abstract scaleToFit(size: ISize): void;
}

/** Scales wrapped text while expanding the wrap width accordingly. */
export class WordWrapScaler extends TextScaler
{
    override scaleToFit(size: ISize): void
    {
        this.textObject.scale.set(1);

        if (!this.textObject.style.defaultWordWrapWidth)
        {
            this.textObject.style.defaultWordWrapWidth = this.textObject.style.wordWrapWidth;
        }

        this.textObject.style.wordWrapWidth = this.textObject.style.defaultWordWrapWidth;

        const scaleStep = 0.99;

        while (size.width / this.textObject.width < 1 || size.height / this.textObject.height < 1)
        {
            this.textObject.scale.set(
                this.textObject.scale.x * scaleStep,
                this.textObject.scale.y * scaleStep,
            );
            this.textObject.style.wordWrapWidth /= scaleStep;
        }
    }
}

/** Scales non-wrapped text uniformly to the largest fitting factor. */
export class NonWordWrapScaler extends TextScaler
{
    override scaleToFit(size: ISize): void
    {
        this.textObject.scale.set(1);

        const factor = Math.min(
            1,
            size.width / this.textObject.width,
            size.height / this.textObject.height,
        );

        this.textObject.scale.set(factor);
    }
}

