"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.watchUnitIO = watchUnitIO;
const GRAPH_DEFAULT_EVENTS_1 = require("../constant/GRAPH_DEFAULT_EVENTS");
const emptyIO_1 = require("../spec/emptyIO");
const f_1 = require("../system/core/object/ForEachKeyValue/f");
const array_1 = require("../util/array");
const callAll_1 = require("../util/call/callAll");
const watchInput_1 = require("./watchInput");
const watchOutput_1 = require("./watchOutput");
const watchRefInput_1 = require("./watchRefInput");
const watchRefOutput_1 = require("./watchRefOutput");
const watchUnitErr_1 = require("./watchUnitErr");
const watchUnitEvent_1 = require("./watchUnitEvent");
const watchUnitIOSpec_1 = require("./watchUnitIOSpec");
function watchUnitIO(unit, events = GRAPH_DEFAULT_EVENTS_1.GRAPH_DEFAULT_EVENTS, callback) {
    let all = [];
    const watch_data_input = events.includes('input');
    const watch_ref_input = events.includes('ref_input');
    const watch_data_output = events.includes('output');
    const watch_ref_output = events.includes('ref_output');
    const watch_rename_input = events.includes('rename_input');
    const watch_rename_output = events.includes('rename_output');
    const pin_listener_map = (0, emptyIO_1.emptyIO)({}, {});
    const watchPin = (kind, type, pinId, pin) => {
        const unlisten = {
            ref: {
                input: watchRefInput_1.watchRefInput,
                output: watchRefOutput_1.watchRefOutput,
            },
            data: {
                input: watchInput_1.watchDataInput,
                output: watchOutput_1.watchDataOutput,
            },
        }[kind][type](pinId, pin, callback);
        pin_listener_map[type][pinId] = unlisten;
        all.push(unlisten);
        return unlisten;
    };
    const makeWatchPin = (type, kind) => {
        return (pin, pinId) => {
            watchPin(kind, type, pinId, pin);
        };
    };
    const unlistenPin = (type, pinId) => {
        const unlisten = pin_listener_map[type][pinId];
        if (unlisten) {
            unlisten();
            (0, array_1.remove)(all, unlisten);
            delete pin_listener_map.input[pinId];
        }
    };
    const listenPin = (type, pinId) => {
        const pin = unit.getPin(type, pinId);
        const ref = unit.isPinRef(type, pinId);
        if (ref && watch_ref_input) {
            watchPin('ref', type, pinId, pin);
        }
        else if (!ref && watch_data_input) {
            watchPin('data', type, pinId, pin);
        }
    };
    if (watch_data_input) {
        (0, f_1.default)(unit.getDataInputs(), makeWatchPin('input', 'data'));
    }
    if (watch_ref_input) {
        (0, f_1.default)(unit.getRefInputs(), makeWatchPin('input', 'ref'));
    }
    if (watch_data_input || watch_ref_input) {
        all.push(unit.addListener('set_input', (pinId, pin, { ref }) => {
            listenPin('input', pinId);
        }));
    }
    unit.addListener('remove_input', (pinId, pin) => {
        const unlisten = pin_listener_map.input[pinId];
        if (unlisten) {
            unlisten();
            (0, array_1.remove)(all, unlisten);
            delete pin_listener_map.output[pinId];
        }
    });
    all.push(unit.addListener('rename_input', (name, newName) => {
        unlistenPin('input', name);
        listenPin('input', newName);
    }));
    all.push(unit.addListener('rename_output', (name, newName) => {
        unlistenPin('output', name);
        listenPin('output', newName);
    }));
    if (watch_rename_input) {
        all.push((0, watchUnitEvent_1.watchUnitRenamePinEvent)('rename_input', unit, callback));
    }
    all.push(unit.addListener('remove_output', (pinId, pin) => {
        const unlisten = pin_listener_map.output[pinId];
        if (unlisten) {
            unlisten();
            (0, array_1.remove)(all, unlisten);
            delete pin_listener_map.output[pinId];
        }
    }));
    if (watch_data_output) {
        (0, f_1.default)(unit.getDataOutputs(), makeWatchPin('output', 'data'));
    }
    if (watch_ref_output) {
        (0, f_1.default)(unit.getRefOutputs(), makeWatchPin('output', 'ref'));
    }
    if (watch_data_output || watch_ref_output) {
        all.push(unit.addListener('set_output', (pinId, pin, { ref }) => {
            if (ref && watch_ref_output) {
                watchPin('ref', 'output', pinId, pin);
            }
            else if (!ref && watch_data_output) {
                watchPin('data', 'output', pinId, pin);
            }
        }));
    }
    if (watch_rename_output) {
        all.push((0, watchUnitEvent_1.watchUnitRenamePinEvent)('rename_output', unit, callback));
    }
    if (unit.isElement()) {
        if (events.includes('append_child')) {
            // @ts-ignore
            all.push((0, watchUnitEvent_1.watchComponentAppendEvent)('append_child', unit, callback));
        }
        if (events.includes('append_children')) {
            all.push(
            // @ts-ignore
            (0, watchUnitEvent_1.watchComponentAppendChildrenEvent)('append_children', unit, callback));
        }
        if (events.includes('remove_child')) {
            // @ts-ignore
            all.push((0, watchUnitEvent_1.watchComponentRemoveEvent)('remove_child', unit, callback));
        }
    }
    all.push((0, watchUnitErr_1.watchUnitErr)(unit, callback, events));
    all.push((0, watchUnitIOSpec_1.watchUnitIOSpec)(unit, callback, events));
    let unlisten = (0, callAll_1.callAll)(all);
    return unlisten;
}
//# sourceMappingURL=watchUnitIO.js.map