import { Color, Texture } from 'three';

import { serializable } from '../../engine/engine_serialization_decorator.js';
import { MaskableGraphic } from './Graphic.js';


class Sprite {
    @serializable(Texture)
    texture: Texture | null = null;

    rect?: { width: number, height: number };
}

/**
 * [Image](https://engine.needle.tools/docs/api/Image) displays a sprite (2D texture) in the UI. Can be used for icons,
 * backgrounds, or any visual element that needs a texture.  
 *
 * **Properties:**  
 * - `image` - Direct texture assignment (convenience property)
 * - `sprite` - Sprite object containing texture and rect info
 * - `color` - Tint color applied to the image (inherited from Graphic)
 *
 * **Usage with Button:**  
 * Image is commonly paired with {@link Button} to create clickable  
 * UI elements with visual feedback via color tinting.  
 *
 * @example Set an image texture
 * ```ts
 * const img = myIcon.getComponent(Image);
 * img.image = myTexture;
 * img.color = new RGBAColor(1, 0.5, 0.5, 1); // Red tint
 * ```
 *
 * @summary Display a 2D image in the UI
 * @category User Interface
 * @group Components
 * @see {@link Canvas} for the UI root
 * @see {@link Button} for clickable images
 * @see {@link RawImage} for non-UI image display
 */
export class Image extends MaskableGraphic {

    set image(img: Texture | null) {
        if (!this.sprite) this.sprite = new Sprite();
        this.sprite.texture = img;
        this.onAfterCreated();
    }
    get image(): Texture | null {
        if (this.sprite)
            return this.sprite.texture;
        return null;
    }

    @serializable(Sprite)
    get sprite(): Sprite | undefined {
        return this._sprite;
    }
    set sprite(sprite: Sprite | undefined) {
        if (this._sprite === sprite) return;
        this._sprite = sprite;
        this.onAfterCreated();
    }

    private _sprite?: Sprite;

    @serializable()
    private pixelsPerUnitMultiplier: number = 1;

    private isBuiltinSprite() {
        const sprite = this.sprite;
        switch (sprite?.texture?.name) {
            case "InputFieldBackground":
            case "UISprite":
            case "Background":
            case "Knob":
                return true;
        }
        // this is a hack/workaround for production builds where the name of the sprite is missing
        // need to remove this!!!!
        if (!sprite?.texture?.name?.length && sprite?.texture?.image?.width === 32 && sprite?.texture?.image?.height === 32)
            return true;
        return false;
    }

    protected onBeforeCreate(opts: any): void {
        super.onBeforeCreate(opts);
        if (this.isBuiltinSprite()) {
            opts.borderRadius = 5 / this.pixelsPerUnitMultiplier;
            if(this.sprite?.texture?.name === "Knob") {
                opts.borderRadius = 999;
            }
            // opts.borderColor = new Color(.4, .4, .4);
            // opts.borderOpacity = this.color.alpha;
            // opts.borderWidth = .3;
        }

    }

    protected onAfterCreated(): void {
        if (!this.__didAwake) return;
        super.onAfterCreated();
        // TODO: @swingingtom setting a built-in sprite at runtime doesnt update the image
        if (this.isBuiltinSprite()) return;
        this.setTexture(this.sprite?.texture);
    }
}

/**
 * @category User Interface
 * @group Components
 */
export class RawImage extends MaskableGraphic {
    @serializable(Texture)
    get mainTexture(): Texture | undefined {
        return this._mainTexture;
    }
    set mainTexture(texture: Texture | undefined) {
        if (this._mainTexture === texture) return;
        this._mainTexture = texture;
        this.onAfterCreated();
    }

    private _mainTexture?: Texture;

    protected onAfterCreated(): void {
        if(!this.__didAwake) return;
        super.onAfterCreated();
        // console.log(this);
        // if (this.mainTexture) {
        //     this.mainTexture.flipY = true;
        //     this.mainTexture.needsUpdate = true;
        // }
        this.setTexture(this.mainTexture);
    }
}
