import { assert } from "../../../core/assert.js";
import { Transform } from "../transform/Transform.js";

export const TransformAttachmentFlags = {
    /**
     * Update will be performed when component is attached.
     * It's used to turn this flag off when you know that the child's transform is already correct to save some computations
     */
    Immediate: 1
};

const DEFAULT_FLAGS = TransformAttachmentFlags.Immediate;

/**
 * Controls {@link Transform} the entity it is attached to, enables transformation hierarchy.
 */
export class TransformAttachment {

    /**
     * Transform relative to the attachment target.
     * Think of this as "local transform".
     * @type {Transform}
     */
    transform = new Transform();

    /**
     * Which entity to attach to, this is what our transform is relative to.
     * @type {number}
     */
    parent = -1;

    /**
     * Do not modify directly, see {@link TransformAttachmentFlags}.
     * @type {number}
     */
    flags = DEFAULT_FLAGS;

    toString() {
        return JSON.stringify(this.toJSON());
    }

    toJSON() {
        return {
            transform: this.transform.toJSON(),
            parent: this.parent,
            flags: this.flags
        };
    }

    fromJSON({ transform, parent = -1, flags = DEFAULT_FLAGS }) {
        assert.defined(transform, 'transform');
        assert.isInteger(parent, 'parent');
        assert.isNonNegativeInteger(flags, 'flags');

        this.transform.fromJSON(transform);
        this.parent = parent;
        this.flags = flags;
    }

    get immediate() {
        return this.getFlag(TransformAttachmentFlags.Immediate);
    }

    /**
     *
     * @param {boolean} v
     */
    set immediate(v) {
        this.writeFlag(TransformAttachmentFlags.Immediate, v);
    }

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

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

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

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

}

/**
 * @readonly
 * @type {string}
 */
TransformAttachment.typeName = "TransformAttachment";

/**
 * @readonly
 * @type {boolean}
 */
TransformAttachment.prototype.isTransformAttachment = true;
