import { BvhClient } from "../../../core/bvh2/bvh3/BvhClient.js";
import { AABB3 } from "../../../core/geom/3d/aabb/AABB3.js";
import { M4_IDENTITY } from "../../../core/geom/3d/mat4/M4_IDENTITY.js";
import {
    expand_aabb_by_transformed_three_object
} from "../../graphics/three/expand_aabb_by_transformed_three_object.js";
import { RenderableFlags } from "./RenderableFlags.js";

const DEFAULT_FLAGS = RenderableFlags.UpdateMatrix | RenderableFlags.BoundingBoxNeedsUpdate;


/**
 @deprecated use {@link ShadedGeometry} instead
 */
class Renderable {
    boundingBox = new AABB3(0, 0, 0, 0, 0, 0);

    bvh = new BvhClient();

    /**
     *
     * @type {number}
     */
    flags = DEFAULT_FLAGS;

    constructor(object) {
        this.object = object;

    }

    dispose() {
        const object = this.object;

        if (typeof object.dispose === "function") {
            object.dispose();
        }
    }

    configureFromObject() {
        this.__auto_detect_flag_cast_shadow_from_object(this.object);
    }

    /**
     *
     * @param {Object3D} object
     * @private
     */
    __auto_detect_flag_cast_shadow_from_object(object) {
        if (object.castShadow) {
            this.setFlag(RenderableFlags.CastShadow);
            return true;
        }

        const children = object.children;
        const child_count = children.length;
        for (let i = 0; i < child_count; i++) {
            const child = children[i];

            if (this.__auto_detect_flag_cast_shadow_from_object(child)) {
                return true;
            }
        }

        return false;
    }

    /**
     *
     * @param {boolean} v
     */
    set matrixAutoUpdate(v) {
        this.writeFlag(RenderableFlags.UpdateMatrix, v);
    }

    /**
     *
     * @returns {boolean}
     */
    get matrixAutoUpdate() {
        return this.getFlag(RenderableFlags.UpdateMatrix);
    }

    /**
     *
     * @param {boolean} v
     */
    set boundingBoxNeedsUpdate(v) {
        this.writeFlag(RenderableFlags.BoundingBoxNeedsUpdate, v);
    }

    /**
     *
     * @returns {boolean}
     */
    get boundingBoxNeedsUpdate() {
        return this.getFlag(RenderableFlags.BoundingBoxNeedsUpdate);
    }

    /**
     *
     * @param {number|RenderableFlags} flag
     * @returns {void}
     */
    setFlag(flag) {
        this.flags |= flag;
    }

    /**
     *
     * @param {number|RenderableFlags} flag
     * @returns {void}
     */
    clearFlag(flag) {
        this.flags &= ~flag;
    }

    /**
     *
     * @param {number|RenderableFlags} flag
     * @param {boolean} value
     */
    writeFlag(flag, value) {
        if (value) {
            this.setFlag(flag);
        } else {
            this.clearFlag(flag);
        }
    }

    /**
     *
     * @param {number|RenderableFlags} flag
     * @returns {boolean}
     */
    getFlag(flag) {
        return (this.flags & flag) === flag;
    }

    computeBoundsFromObject() {
        const object = this.object;

        object.updateMatrixWorld();

        const aabb = new Float32Array([
            Infinity, Infinity, Infinity,
            -Infinity, -Infinity, -Infinity
        ]);

        expand_aabb_by_transformed_three_object(aabb, object, M4_IDENTITY);

        // sanity checks to ensure valid bounds
        if (!Number.isFinite(aabb[0])) {
            aabb[0] = aabb[3] = 0;
        }

        if (!Number.isFinite(aabb[1])) {
            aabb[1] = aabb[4] = 0;
        }

        if (!Number.isFinite(aabb[2])) {
            aabb[2] = aabb[5] = 0;
        }


        this.boundingBox.readFromArray(aabb);

        this.clearFlag(RenderableFlags.BoundingBoxNeedsUpdate);
    }
}

Renderable.typeName = "Renderable";

Renderable.serializable = false;

export default Renderable;
