import { Box3, Color, type ColorRepresentation, LineSegments, Object3D, Vector3 } from "three";

import { CreateWireCube, Gizmos } from "../engine/engine_gizmos.js";
import { getBoundingBox, getWorldPosition, getWorldScale } from "../engine/engine_three_utils.js";
import { getParam } from "../engine/engine_utils.js";
import { Behaviour } from "./Component.js";

const gizmos = getParam("gizmos");
const debug = getParam("debugboxhelper");

/**
 * @category Helpers
 * @group Components
 */
export class BoxHelperComponent extends Behaviour {

    private box: Box3 | null = null;
    private static testBox: Box3 = new Box3();
    private _lastMatrixUpdateFrame: number = -1;
    private static _position: Vector3 = new Vector3();
    private static _size: Vector3 = new Vector3(.01, .01, .01);
    private static _emptyObjectSize: Vector3 = new Vector3(.01, .01, .01);

    public isInBox(obj: Object3D): boolean | undefined {
        if (!obj) return undefined;

        if (!this.box) {
            this.box = new Box3();
        }

        getBoundingBox([obj], undefined, undefined, BoxHelperComponent.testBox);

        if (BoxHelperComponent.testBox.isEmpty()) {
            const wp = getWorldPosition(obj, BoxHelperComponent._position);
            BoxHelperComponent.testBox.setFromCenterAndSize(wp, BoxHelperComponent._emptyObjectSize);
        }

        this.updateBox();
        const intersects = this.box?.intersectsBox(BoxHelperComponent.testBox);
        if (intersects) {
            if (debug) Gizmos.DrawWireBox3(BoxHelperComponent.testBox, 0xff0000, 5);

        }
        return intersects;
    }

    public intersects(box: Box3): boolean {
        if (!box) return false;
        return this.updateBox(false).intersectsBox(box);
    }

    public updateBox(force: boolean = false): Box3 {
        if (!this.box) {
            this.box = new Box3();
        }
        if (force || this.context.time.frameCount != this._lastMatrixUpdateFrame) {
            const firstUpdate = this._lastMatrixUpdateFrame < 0;
            this._lastMatrixUpdateFrame = this.context.time.frameCount;
            const updateParents: boolean = firstUpdate; // updating parents seems to cause falsely calculated positions sometimes?
            const wp = getWorldPosition(this.gameObject, BoxHelperComponent._position, updateParents);
            const size = getWorldScale(this.gameObject, BoxHelperComponent._size);
            this.box.setFromCenterAndSize(wp, size);
        }
        return this.box;
    }


    private _helper: LineSegments | null = null;
    private _color: Color | null = null;

    awake(): void {
        this._helper = null;
        this._color = null;
        this.box = null;
    }

    public showHelper(col: ColorRepresentation | null = null, force: boolean = false) {
        if (!gizmos && !force) return;
        if (this._helper) {
            if (col)
                this._color?.set(col);
            this.gameObject.add(this._helper);
            return;
        }
        this._helper = CreateWireCube(col);
        this.gameObject.add(this._helper);
    }

}