import { assert } from "../assert.js";
import Signal from "../events/signal/Signal.js";

export class BooleanVector3 {
    /**
     *
     * @param {boolean} x
     * @param {boolean} y
     * @param {boolean} z
     * @constructor
     */
    constructor(x = false, y = false, z = false) {
        /**
         *
         * @type {boolean}
         */
        this.x = x;
        /**
         *
         * @type {boolean}
         */
        this.y = y;
        /**
         *
         * @type {boolean}
         */
        this.z = z;

        this.onChanged = new Signal();
    }

    /**
     *
     * @param {boolean} x
     * @param {boolean} y
     * @param {boolean} z
     */
    set(x, y, z) {
        assert.equal(typeof x, 'boolean', `expected x to be a boolean, instead was '${typeof x}'`);
        assert.equal(typeof y, 'boolean', `expected y to be a boolean, instead was '${typeof y}'`);
        assert.equal(typeof z, 'boolean', `expected z to be a boolean, instead was '${typeof z}'`);

        const _x = this.x;
        const _y = this.y;
        const _z = this.z;

        if (x !== _x || y !== _y || z !== _z) {
            this.x = x;
            this.y = y;
            this.z = z;

            if (this.onChanged.hasHandlers()) {
                this.onChanged.send6(x, y, z, _x, _y, _z);
            }
        }
    }

    /**
     *
     * @param {boolean} v
     */
    setX(v) {
        this.set(v, this.y, this.z);
    }

    /**
     *
     * @param {boolean} v
     */
    setY(v) {
        this.set(this.x, v, this.z);
    }

    /**
     *
     * @param {boolean} v
     */
    setZ(v) {
        this.set(this.x, this.y, v);
    }

    /**
     *
     * @param {BooleanVector3} other
     */
    copy(other) {
        this.set(other.x, other.y, other.z);
    }

    toJSON() {
        return {
            x: this.x,
            y: this.y,
            z: this.z
        };
    }

    fromJSON(json) {
        this.set(json.x, json.y, json.z);
    }

    /**
     *
     * @param {BinaryBuffer} buffer
     */
    toBinaryBuffer(buffer) {
        const v = (this.x ? 1 : 0) | (this.y ? 2 : 0) | (this.z ? 4 : 0);

        buffer.writeUint8(v);
    }

    /**
     *
     * @param {BinaryBuffer} buffer
     */
    fromBinaryBuffer(buffer) {
        const value = buffer.readUint8();

        const x = (value & 1) !== 0;
        const y = (value & 2) !== 0;
        const z = (value & 4) !== 0;

        this.set(x, y, z);
    }
}

/**
 * @readonly
 * @type {BooleanVector3}
 */
BooleanVector3.true = Object.freeze(new BooleanVector3(true, true, true));
/**
 * @readonly
 * @type {BooleanVector3}
 */
BooleanVector3.false = Object.freeze(new BooleanVector3(false, false, false));
