Source: configurator/connection.js

import { Reporter } from '../reporter/reporter.js';
import { Connector } from '../package/connector/connector.js';
import { ConnectorType } from '../package/connector/connector_type.js';
import { ConnectionType } from '../package/connector/connection_type.js';
import { ComponentTree } from '../component/component_tree.js';
import { Quaternion } from '../../node_modules/three/build/three.module.js';
import { Connectable } from './connectable.js';


/** Connection between two connector instances */

class Connection extends Connectable {

    /**
     * @param {Reporter} reporter
     * @param {Object} settings
     * @param {UUID} [settings.id]
     * @param {string} [settings.name]
     * @param {Object} settings.from
     * @param {Connectable} settings.from.connectable
     * @param {Connector} settings.from.connector
     * @param {Object} settings.to
     * @param {Connectable} settings.to.connectable
     * @param {Connector} settings.to.connector
     * @param {Quaternion} [settings.quaternion]
     */
    
    constructor ( reporter, settings )
    {

        if ( settings.blockInstances ) {
            delete settings.blockInstances;
        }

        super(
            reporter,
            settings
        );

        // old fashioned sanity checking

        for ( let side of [ 'from', 'to']) {

            if ( ! settings[ side ] ) {
                throw new Error( `Missing "${side}" object`);
            }

            if (! ( settings[ side ].connectable instanceof Connectable)) {
                if ( ! settings[ side ].connectable ) {
                    throw new Error( `Missing a ${side}.connectable.`);
                }
                else {
                    throw new Error( `${side}.connectable is not a Connectable, but a ${settings[ side ].connectable.constructor.name}.`);
                }
            }

            if (! ( settings[ side ].connector instanceof Connector)) {
                if ( ! settings[ side ].connector ) {
                    throw new Error( `Missing ${side}.connector.`);
                }
                else {
                    throw new Error( `${side}.connector is not a Connector, but a ${settings[ side ].connector.constructor.name}.`);
                }
            }

            if ( ! settings[ side ].connectable.block.dependsDirectlyOn( settings[ side ].connector )) {
                throw new Error( `Entry ${side} connector is not a dependency of block` );
            }

            if ( settings.quaternion && ! settings.quaternion instanceof Quaternion ) {
                throw new Error( `.quaternion is not a Quaternion but a ${settings.quaternion.constructor.name}`)
            }
        }

        if ( ! settings.quaternion ) {
            this.quaternion = Connection.defaultQuaternion;
        }

        this.inverseQuaternion = this.quaternion.clone().invert()

        if ( settings.from.connectable === settings.to.connectable) {
            throw new Error( `Connections to and from blocks are the same.`);
        }

        this.connectors = [ settings.from.connector, settings.to.connector ];

        this.connectorTypes = [ settings.from.connector.type, settings.to.connector.type ];

        this.connectables = [settings.from.connectable, settings.to.connectable ];

        // mf typescript
        if ( ! this.from ) {
            this.from  = settings.from;
        }
        if ( ! this.to ) {
            this.to  = settings.to;
        }
    }

    
    static _exportName = {
        singular: 'connection',
        plural: 'connections'
    };


    /** @type {ExportLevel} */
    
    static _exportLevel = 'inline';


    static connectorTemplateSetting = 'connectionType';

    // Quaternion [x, y, z, w]
    // [ 0, 0, 0, 1 ]  - Identity quaternion, no rotation,
    // [ 1, 0, 0, 0 ]  - 180° turn around X axis,
    // [ 0, 1, 0, 0 ]  - 180° turn around Y axis,
    // [ 0, 0, 0, 1 ]  - 180° turn around Z axis,

    static baseQuaternion = new Quaternion(0, 1, 0, 0);

    static defaultQuaternion = new Quaternion(0,0,0,1);

    static treeLock = false;


    /**
     * @type {Array<Connectable>}
     */

    connectables;


    /**
     * @type {Array<Connector>}
     */
    
    connectors;


    /**
     * @type {Array<ConnectorType>}
     */
    
    connectorTypes;


    /** @type {ComponentTree} */

    _tree;


    /** @type {ConnectionType} */

    type;


    _onTreeSet() {

        for ( let side of [ 'from', 'to']) {

            const connectableExportName = this._settings[ side ].connectable.exportName;

            if ( ( ! this._tree[ connectableExportName ] ) || ( ! this._tree[ connectableExportName ].includes( this._settings[ side ].connectable))) {
                throw new Error( `${side} ${connectableExportName} is not specified in the tree (${this._settings[ side ].connectable.label}).` );
            }
        }

        

        // console.log( this._tree.inclusive.connectionTypes)
        if ( ! this._tree.inclusive.connectionTypes ) {
            throw new Error( `No connection types defined in tree` );
        }

        const connectionType = this._tree.inclusive.connectionTypes.find( connectionType =>
            ( connectionType.connectorTypes[ 0 ] === this.connectorTypes[ 0 ] && connectionType.connectorTypes[ 1 ] === this.connectorTypes[ 1 ] )
            ||
            ( connectionType.connectorTypes[ 0 ] === this.connectorTypes[ 1 ] && connectionType.connectorTypes[ 1 ] === this.connectorTypes[ 0 ] )
        );

        if ( ! connectionType ) {
            console.error( this._tree.inclusive.connectionTypes);
            throw new Error( `Missing this connection's type in tree.pkg [${this.connectorTypes[0].label} <-> ${this.connectorTypes[1].label}]` );
        }

        this.type = connectionType;

        if ( this.type.parent) {
            if (this.connectorTypes[ 0 ] === this.connectorTypes[ 1 ]) {
                throw new Error(`${this.type.label} defines a parent/child relationship, but both connectors have the same type in ${this.label}`);
            }
        }

        this.connectorTemplate = connectionType;

        super._onTreeSet();
    }


    _onTreeUnset() {
        super._onTreeUnset();
        this.type = undefined;
    }


    /**
     * Checks whether a connector or blockInstance is part of this connection
     * @param {Component} component 
     * @returns {Boolean}
     */

    connects(component) {
        if ( component instanceof Connectable ) {
            return this._settings.from.connectable === component || this._settings.to.connectable === component;
        }
        else if ( component instanceof Connector ) {
            return this._settings.from.connector === component || this._settings.to.connector === component;
        }
        return false;
    }





    /** 
     * @returns {}
     */

    _build(part,quality,dependencies) {
        this._setContent(part,quality,'-');
    }

    
    // /**
    //  * @returns {ParsedConnectionString}
    //  */

    // toJSON() {
    //     return {
    //         UUID: this.UUID,
    //         from: this.fromConnector.toJSON(),
    //         to: this.toConnector.toJSON(),
    //     }
    // }
}

export { Connection }