"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_STYLE = void 0;
const complexity_1 = require("../../../../../client/complexity");
const mergeProps_1 = require("../../../../../client/component/mergeProps");
const element_1 = require("../../../../../client/element");
const id_1 = require("../../../../../client/id");
const parentElement_1 = require("../../../../../client/platform/web/parentElement");
const simulation_1 = require("../../../../../client/simulation");
const spec_1 = require("../../../../../client/spec");
const geometry_1 = require("../../../../../client/util/geometry");
const LINK_DISTANCE_1 = require("../../../../../constant/LINK_DISTANCE");
const emptySpec_1 = require("../../../../../spec/emptySpec");
const object_1 = require("../../../../../util/object");
const f_1 = require("../../../../f/object/Keys/f");
const Component_1 = require("../Editor/Component");
const Component_2 = require("../Minimap/Component");
exports.DEFAULT_STYLE = {};
class Minigraph extends element_1.Element {
    constructor($props, $system) {
        super($props, $system);
        this._nodes = {};
        this._links = {};
        this._subgraph_to_node_id = [];
        this._node_id_to_subgraph = {};
        this._force = (alpha) => {
            const node_entries = Object.entries(this._nodes);
            const node_n = node_entries.length;
            for (let i = 0; i < node_n; i++) {
                const [a_id, a] = node_entries[i];
                const a_subgraph = this._node_id_to_subgraph[a_id];
                for (let j = i + 1; j < node_n; j++) {
                    const [b_id, b] = node_entries[j];
                    const b_subgraph = this._node_id_to_subgraph[b_id];
                    const surface_d = (0, geometry_1.surfaceDistance)(a, b);
                    const { u } = surface_d;
                    let { l } = (0, geometry_1.surfaceDistance)(a, b);
                    l = Math.max(l, 1);
                    const same_subgraph = a_subgraph === b_subgraph;
                    const D = same_subgraph ? Component_1.SUBGRAPH_MAX_D : Component_1.NOT_SUBGRAPH_MAX_D;
                    if (l < D) {
                        const k = (-90 * alpha) / l;
                        b.vx -= u.x * k;
                        b.vy -= u.y * k;
                        a.vx += u.x * k;
                        a.vy += u.y * k;
                    }
                }
                a.y -= (a.y * alpha) / 6;
            }
            const link_entries = Object.entries(this._links);
            const link_n = link_entries.length;
            for (let i = 0; i < link_n; i++) {
                const [link_id] = link_entries[i];
                const { source, target } = (0, id_1.segmentLinkId)(link_id);
                const a = this._nodes[source];
                const b = this._nodes[target];
                let { l, d } = (0, geometry_1.surfaceDistance)(b, a);
                l = Math.max(l, 1);
                d = Math.max(d, 1);
                const tl = LINK_DISTANCE_1.LINK_DISTANCE;
                const ax = a.x;
                const bx = b.x;
                const ay = a.y;
                const by = b.y;
                const k = alpha / d;
                const ll = (l - tl) * k;
                const x = (bx - ax) * ll;
                const y = (by - ay) * ll;
                b.vx -= x;
                b.vy -= y;
                a.vx += x;
                a.vy += y;
                const my = (ay + by) / 2;
                b.vy += ((my - by) * alpha) / 3;
                a.vy += ((my - ay) * alpha) / 3;
            }
        };
        this._tick = () => {
            // console.log('Minigraph', '_tick')
            this._minimap.tick();
        };
        this._node_count = 0;
        this._reset = () => {
            // console.log('Minigraph', '_reset')
            const specs = this.$system.specs;
            const { bundle } = this.$props;
            const { spec = (0, emptySpec_1.emptyGraphSpec)(), specs: _specs } = bundle;
            const { units = {}, merges } = spec;
            const nodes = (0, object_1.mapObjVK)(units, (unit_spec, unit_id) => {
                var _a, _b;
                this._node_count++;
                const { id, metadata = {} } = unit_spec;
                const { position = { x: 0, y: 0 }, component = {} } = metadata;
                const { x, y } = position;
                const r = (0, complexity_1.getSpecRadius)(specs, id, true);
                let width = 2 * r;
                let height = 2 * r;
                const is_component = (0, spec_1.isComponentId)(specs, id);
                if (is_component) {
                    const { width: _width, height: _height } = component;
                    const spec = (0, spec_1.getSpec)(specs, id);
                    const { component: _component = {} } = spec;
                    const { defaultWidth, defaultHeight } = _component;
                    width = (_a = _width !== null && _width !== void 0 ? _width : defaultWidth) !== null && _a !== void 0 ? _a : 120;
                    height = (_b = _height !== null && _height !== void 0 ? _height : defaultHeight) !== null && _b !== void 0 ? _b : 120;
                }
                const shape = is_component ? 'rect' : 'circle';
                return {
                    _x: x,
                    _y: y,
                    ax: 0,
                    ay: 0,
                    x,
                    y,
                    fx: undefined,
                    fy: undefined,
                    shape,
                    r,
                    width,
                    height,
                    vx: 0,
                    vy: 0,
                    hx: 0,
                    hy: 0,
                };
            });
            const links = {};
            const subgraph_to_node_id = [];
            const node_id_to_subgraph = {};
            for (const merge_id in merges) {
                const merge = merges[merge_id];
                const merge_input_unit = {};
                const merge_output_unit = {};
                for (const unit_id in merge) {
                    const unit = merge[unit_id];
                    const { input = {}, output = {} } = unit;
                    if ((0, f_1.keys)(input).length > 0) {
                        merge_input_unit[unit_id] = true;
                    }
                    if ((0, f_1.keys)(output).length > 0) {
                        merge_output_unit[unit_id] = true;
                    }
                }
                for (const input_unit_id in merge_input_unit) {
                    let input_unit_sub_graph_id = node_id_to_subgraph[input_unit_id];
                    let input_unit_subgraph = subgraph_to_node_id[input_unit_sub_graph_id];
                    for (const output_unit_id in merge_output_unit) {
                        const output_unit_sub_graph_id = node_id_to_subgraph[output_unit_id];
                        const output_unit_subgraph = subgraph_to_node_id[output_unit_sub_graph_id];
                        if (input_unit_sub_graph_id !== undefined &&
                            output_unit_sub_graph_id !== undefined) {
                            if (input_unit_sub_graph_id < output_unit_sub_graph_id) {
                                for (const unit_id of output_unit_subgraph) {
                                    output_unit_subgraph.delete(unit_id);
                                    input_unit_subgraph.add(unit_id);
                                    node_id_to_subgraph[unit_id] = input_unit_sub_graph_id;
                                }
                            }
                            else if (input_unit_sub_graph_id > output_unit_sub_graph_id) {
                                for (const unit_id of input_unit_subgraph) {
                                    input_unit_subgraph.delete(unit_id);
                                    output_unit_subgraph.add(unit_id);
                                    node_id_to_subgraph[unit_id] = output_unit_sub_graph_id;
                                }
                            }
                        }
                        else if (input_unit_sub_graph_id !== undefined) {
                            input_unit_subgraph.add(output_unit_id);
                            node_id_to_subgraph[output_unit_id] = input_unit_sub_graph_id;
                        }
                        else if (output_unit_sub_graph_id !== undefined) {
                            output_unit_subgraph.add(input_unit_id);
                            node_id_to_subgraph[input_unit_id] = output_unit_sub_graph_id;
                        }
                        else {
                            const subgraph = new Set();
                            subgraph_to_node_id.push(subgraph);
                            const subgraph_id = subgraph_to_node_id.length - 1;
                            subgraph.add(input_unit_id);
                            node_id_to_subgraph[input_unit_id] = subgraph_id;
                            subgraph.add(output_unit_id);
                            node_id_to_subgraph[output_unit_id] = subgraph_id;
                            input_unit_sub_graph_id = subgraph_id;
                            input_unit_subgraph = subgraph;
                        }
                        const merge_link_id = (0, id_1.getLinkId)(output_unit_id, input_unit_id);
                        links[merge_link_id] = links[merge_link_id] || 0;
                        links[merge_link_id]++;
                    }
                }
            }
            this._subgraph_to_node_id = subgraph_to_node_id;
            this._node_id_to_subgraph = node_id_to_subgraph;
            this._nodes = nodes;
            this._links = links;
            (0, mergeProps_1.default)(this._minimap, {
                nodes,
                links,
            });
            this._simulation.nodes(nodes);
            if (this._node_count > 0) {
                this._simulation.start();
            }
        };
        const { width, height } = this.$props;
        const minimap = new Component_2.default({
            width,
            height,
            style: {},
            nodes: {},
            links: {},
            padding: LINK_DISTANCE_1.LINK_DISTANCE,
        }, this.$system);
        this._minimap = minimap;
        const simulation = new simulation_1.Simulation({
            force: this._force,
        });
        simulation.addListener('tick', this._tick);
        this._simulation = simulation;
        const $element = (0, parentElement_1.default)($system);
        this.$element = $element;
        this.registerRoot(minimap);
        this._reset();
    }
    onPropChanged(prop, current) {
        if (prop === 'bundle') {
            this._reset();
        }
        else if (prop === 'style') {
            this._minimap.setProp('style', Object.assign(Object.assign({}, exports.DEFAULT_STYLE), current));
        }
    }
}
exports.default = Minigraph;
//# sourceMappingURL=Component.js.map