    import Logic from '../Logic';

    import Box from './Box';

    /** @module editorElements.Gate */

    /**
     * Gate is a box that processes the states of its input connectors and returns the result in its output connectors.
     * @extends Box
     */
    export default class Gate extends Box {
        /**
         * @param {App} appInstance  instance of [App](./module-App.html)
         * @param {string} name       name of the gate (and, not, xor...)
         */
        constructor(appInstance, name) {
            const width = 9;
            const height = 4;

            super(appInstance, name, 'gate', width, height);

            // ADD CONNECTORS

            let specialNodes = [];

            // output
            this.addConnector(width, height / 2, false);

            // block the output connector
            specialNodes.push({
                x: width,
                y: height / 2
            });

            if (this.name === 'not' || this.name === 'repeater') {
                // input
                this.addConnector(0, height / 2, true);
                // block the input connector
                specialNodes.push({
                    x: 0,
                    y: height / 2
                });
            } else {
                // input
                this.addConnector(0, height / 4, true);
                this.addConnector(0, height / (4 / 3), true);

                // block the input connectors
                specialNodes.push({
                    x: 0,
                    y: height / 4
                });
                specialNodes.push({
                    x: 0,
                    y: height / (4 / 3)
                });

                // add one blocked node between the inputs (for better looking wiring)
                specialNodes.push({
                    x: 0,
                    y: height / 2
                });
            }

            this.generateBlockNodes(...specialNodes);

            this.refreshState();
        }

        /**
         * array of valid gate names
         * @type {Set}
         */
        static get validGates() {
            // return new Set(["not", "and", "or", "nand", "nor", "xor", "xnor", "repeater"]);
            return new Set(['not', 'and', 'or', 'nand', 'nor', 'xor', 'xnor']);
        }

        generateBlockNodes(...specialNodes) {
            if (specialNodes !== undefined) {
                super.generateBlockNodes(0, 1, 0, 1, ...specialNodes);
            } else {
                super.generateBlockNodes(0, 1, 0, 1);
            }
        }

        /**
         * proccess the input connector states and reflect them in the output connector states according
         * to the logic corresponding to this gate's name
         */
        refreshState() {
            // map gate names to their logic functions
            const stateMap = {
                and: () => Logic.and(this.connectors[1].state, this.connectors[2].state),
                nand: () => Logic.nand(this.connectors[1].state, this.connectors[2].state),
                nor: () => Logic.nor(this.connectors[1].state, this.connectors[2].state),
                not: () => Logic.not(this.connectors[1].state),
                or: () => Logic.or(this.connectors[1].state, this.connectors[2].state),
                xnor: () => Logic.xnor(this.connectors[1].state, this.connectors[2].state),
                xor: () => Logic.xor(this.connectors[1].state, this.connectors[2].state),
                repeater: () => this.connectors[1].state
            };

            let state = Logic.state.unknown;

            if (stateMap[this.name]) {
                state = stateMap[this.name]();
            }

            // notify the simulator about this change
            this.appInstance.simulation.notifyChange(this.connectors[0].id, state);
        }
    }