import { Plane, Vector3 } from '../loader/babylonjs-import';
import { intersectsPlaneAtToRef } from '../math';
export class Icon {
    id;
    iconId;
    styleId;
    size;
    visible;
    isPickable;
    /**
     * When any of these properties change the icon is marked as dirty in its {@link IconHandler}.
     */
    static _iconDirtyProperties = {
        iconId: true,
        styleId: true,
        size: true,
        visible: true,
        isPickable: true
    };
    /**
     * For internal use only. DO NOT MODIFY.
     * @hidden
     * @internal
     */
    _internal;
    _position;
    _color;
    /**
     * Creates a icon to use in 3D. The icon need to be attached to a {@link IconHandler} to be renderd.
     * @param id Your own string id.
     * @param iconId The icon index in the icon texture atlas. Fist icon has iconId 0.
     * @param styleId The styleId selects a icon style. First style has styleId 0.
     * @param size Set the scale size of the icon 0-N. If the size is 0.0 the icon is invisible. If it is 1, iconMaxSize of {@link IconHandler.setIconAtlas} is unscaled.
     * @param position Contains the 3D-coordinate where the icon is rendered.
     * @param color Custom color RGB, the A channel is used for icon size. If size is 0, the icon is not visible. If RGB is 1.0 only the color from icon texture pixels will be used for coloring. Other values are mixed in.
     * @param visible Defaults to `true`
     * @param isPickable Whether Icon can be found in a pick operation or not. Defaults to `true`.
     * @example
     * ```typescript
     * // A Icon needs to be attatched to a IconHandler to be renderd in the 3D-scene.
     * const iconHandler = new IconHandler(api);
     * // Add a icon atlas to use for all icons. Provide the babylonTextre, the total icon count (16), the amount of icons in a group (4) and if the texture use the alpha channel (true).
     * iconHandler.setIconAtlas(await getTexture('./icon.png'), 16, 4, true);
     * // Add a array of icons to be renderd.
     * iconHandler.attach([
     * new Icon(
     *    "someId_1",
     *    0, // Use the first icon form the 4X4 icon atlas (first row in icon atlas).
     *    2, // Use the third style index, in this case there is 4 icons in a group in the texture atlas.
     *    1, // size
     *    new Vector3(0, 0, 0), // Add a position for the icon. (at origin in this case).
     *    new Color4(Math.random(), Math.random(), Math.random(), 1), // Add a random color to the icon, and the max size to the icon. (1).
     *    true // This makes the icon pickable.
     * ),
     * new Icon(
     *    "someId_2",
     *    1, // Use the second icon row form the 4X4 icon atlas.
     *    0, // Use the first style of the second icon.
     *    1, // size
     *    new Vector3(0, 0, 0), // Add a position for the icon. (at origin in this case).
     *    new Color4(Math.random(), Math.random(), Math.random(), 1), // Add a random color to the icon, and the max size to the icon. (1).
     *    true // This makes the icon pickable.
     * )
     * ]);
     * ```
     * @example
     * ```typescript
     *   // A common use for icons is to enable a user to click on them and then perform some functionality.
     *   // Here is an example of how to implement this with the {@link onPointerObservable}.
     *   // For this to work the icons property isPickable needs to be set to true.
     *   api.onPointerObservable.add((eventData) => {
     *       const pointerButtonInfo = eventData.twinfinity.button(PredefinedPointerButtonId.Main);
     *       // Only continue on main click button release.
     *       if (pointerButtonInfo.event !== 'up') {
     *           return;
     *       }
     *       // Run a pick metod to check what the cursor is abowe.
     *       const pick: PickResult = eventData.twinfinity.pick(true);
     *       // If a Icon is picked.
     *       if (pick.type === PickResultType.Icon) {
     *           const pickedIcon = pick.object;
     *           console.log(pickedIcon);
     *       }
     *   });
     * ```
     */
    constructor(id, iconId, styleId, size, position, color, visible = true, isPickable = false) {
        this.id = id;
        this.iconId = iconId;
        this.styleId = styleId;
        this.size = size;
        this.visible = visible;
        this.isPickable = isPickable;
        this._position = position;
        this._color = color;
        this._position = new Proxy(position, {
            set: (position, p, v) => this.onPropertyChangeFlagIconAsDirtyInIconHandler(position, p, v)
        });
        this._color = new Proxy(color, {
            set: (color, p, v) => this.onPropertyChangeFlagIconAsDirtyInIconHandler(color, p, v)
        });
        return new Proxy(this, {
            set: (icon, p, v) => this.onPropertyChangeFlagIconAsDirtyInIconHandler(icon, p, v, Icon._iconDirtyProperties)
        });
    }
    /**
     * Position of icon in world space.
     */
    get position() {
        return this._position;
    }
    /**
     * Color of icon. If alpha is zero the icon will not be rendered.
     */
    get color() {
        return this._color;
    }
    /**
     * {@link IconHandler} this {@link Icon} is attached to.
     * See {@link IconHandler.attach} and {@link IconHandler.detach}.
     * @returns Parent {@link IconHandler}. `undefined` {@link Icon} as not yet been attached to a {@link IconHandler}.
     * */
    get parent() {
        return this._internal?.iconHandler;
    }
    /**
     * `true` if occluded, otherwise `false`. Only valid if
     *  {@link IconHandler.occlusionCullingInterval} `>= 0`.
     * Otherwise it will always be `false`.
     */
    get isOccluded() {
        return this._internal?.isOccluded ?? false;
    }
    /**
     * Detach icon from its  {@link IconHandler} if it is attached.
     * @return `true` if icon was detached. Otherwise `false`. Happens when icon has no {@link IconHandler}.
     */
    detach() {
        if (!this.parent) {
            return false;
        }
        this.parent.detach(this);
        return true;
    }
    /**
     * Perform intersection test against icon using a ray.
     * @param pO Ray to pick with
     * @returns Intersection or empty array if no intersection
     */
    intersect(pO) {
        const iconCurrentNormal = pO.ray.origin.subtract(this.position).normalize();
        // NOTE This can be optimized to do away with plane allocation by creating a static
        // temporary plane which we simply update when doing calcuation instead of allocating.
        const plane = Plane.FromPositionAndNormal(this.position, iconCurrentNormal);
        const dstIntersectionPoint = Vector3.Zero();
        const hitDistance = intersectsPlaneAtToRef(pO.ray, plane, dstIntersectionPoint);
        if (hitDistance < 0) {
            return [];
        }
        return [
            {
                position: dstIntersectionPoint,
                distance: hitDistance,
                normal: iconCurrentNormal
            }
        ];
    }
    onPropertyChangeFlagIconAsDirtyInIconHandler(target, property, value, markIconAsDirtyProperties) {
        const propVal = target[property];
        if (propVal !== value) {
            target[property] = value;
            if (!markIconAsDirtyProperties || markIconAsDirtyProperties[property]) {
                this.parent?._dirtyIcons.add(this);
            }
        }
        return true;
    }
}
//# sourceMappingURL=Icon.js.map