import { GestureTool, GestureToolView } from "./gesture_tool";
import { GlyphRenderer } from "../../renderers/glyph_renderer";
import { GraphRenderer } from "../../renderers/graph_renderer";
import { DataRenderer } from "../../renderers/data_renderer";
import { compute_renderers } from "../../util";
import { SelectionGeometry } from "../../../core/bokeh_events";
import { Signal0 } from "../../../core/signaling";
import { unreachable } from "../../../core/util/assert";
import { uniq } from "../../../core/util/array";
import * as icons from "../../../styles/icons.css";
export class SelectToolView extends GestureToolView {
    static __name__ = "SelectToolView";
    connect_signals() {
        super.connect_signals();
        this.model.invert.connect(() => this._invert_selection());
        this.model.clear.connect(() => this._clear_selection());
    }
    get computed_renderers() {
        const { renderers } = this.model;
        const all_renderers = this.plot_view.model.data_renderers;
        return compute_renderers(renderers, all_renderers);
    }
    _computed_renderers_by_data_source() {
        const renderers_by_source = new Map();
        for (const r of this.computed_renderers) {
            let source;
            if (r instanceof GlyphRenderer) {
                source = r.data_source;
            }
            else if (r instanceof GraphRenderer) {
                source = r.node_renderer.data_source;
            }
            else {
                continue;
            }
            const renderers = renderers_by_source.get(source) ?? [];
            renderers_by_source.set(source, [...renderers, r]);
        }
        return renderers_by_source;
    }
    _clear_overlay() { }
    _clear_other_overlays() {
        for (const view of this.plot_view.tool_views.values()) {
            if (view instanceof SelectToolView && view != this) {
                view._clear_overlay();
            }
        }
    }
    _clear_selection() {
        const { computed_renderers } = this;
        const selection_managers = uniq(computed_renderers.map((r) => r.selection_manager));
        for (const selection_manager of selection_managers) {
            selection_manager.clear();
        }
        this.plot_view.request_paint(...computed_renderers);
    }
    _invert_selection() {
        const { computed_renderers } = this;
        const selection_managers = uniq(computed_renderers.map((r) => r.selection_manager));
        for (const selection_manager of selection_managers) {
            selection_manager.invert();
        }
        this.plot_view.request_paint(...computed_renderers);
    }
    _select_mode(modifiers) {
        const { shift, ctrl } = modifiers;
        if (!shift && !ctrl) {
            return this.model.mode;
        }
        else if (shift && !ctrl) {
            return "append";
        }
        else if (!shift && ctrl) {
            return "intersect";
        }
        else if (shift && ctrl) {
            return "subtract";
        }
        else {
            unreachable();
        }
    }
    _keyup(ev) {
        if (!this.model.active) {
            return;
        }
        if (ev.key == "Escape") {
            this._clear_selection();
        }
    }
    _emit_selection_event(geometry, final = true) {
        const { x_scale, y_scale } = this.plot_view.frame;
        const geometry_data = (() => {
            switch (geometry.type) {
                case "point": {
                    const { sx, sy } = geometry;
                    const x = x_scale.invert(sx);
                    const y = y_scale.invert(sy);
                    return { ...geometry, x, y };
                }
                case "span": {
                    const { sx, sy } = geometry;
                    const x = x_scale.invert(sx);
                    const y = y_scale.invert(sy);
                    return { ...geometry, x, y };
                }
                case "rect": {
                    const { sx0, sx1, sy0, sy1 } = geometry;
                    const [x0, x1] = x_scale.r_invert(sx0, sx1);
                    const [y0, y1] = y_scale.r_invert(sy0, sy1);
                    return { ...geometry, x0, y0, x1, y1 };
                }
                case "poly": {
                    const { sx, sy } = geometry;
                    const x = x_scale.v_invert(sx);
                    const y = y_scale.v_invert(sy);
                    return { ...geometry, x, y };
                }
            }
        })();
        this.plot_view.model.trigger_event(new SelectionGeometry(geometry_data, final));
    }
}
export class SelectTool extends GestureTool {
    static __name__ = "SelectTool";
    invert = new Signal0(this, "invert");
    clear = new Signal0(this, "clear");
    constructor(attrs) {
        super(attrs);
    }
    static {
        this.define(({ List, Ref, Or, Auto }) => ({
            renderers: [Or(List(Ref(DataRenderer)), Auto), "auto"],
        }));
    }
    get menu() {
        return [
            {
                icon: icons.tool_icon_replace_mode,
                tooltip: "Replace the current selection",
                active: () => this.mode == "replace",
                handler: () => {
                    this.mode = "replace";
                    this.active = true;
                },
            }, {
                icon: icons.tool_icon_append_mode,
                tooltip: "Append to the current selection (Shift)",
                active: () => this.mode == "append",
                handler: () => {
                    this.mode = "append";
                    this.active = true;
                },
            }, {
                icon: icons.tool_icon_intersect_mode,
                tooltip: "Intersect with the current selection (Ctrl)",
                active: () => this.mode == "intersect",
                handler: () => {
                    this.mode = "intersect";
                    this.active = true;
                },
            }, {
                icon: icons.tool_icon_subtract_mode,
                tooltip: "Subtract from the current selection (Shift+Ctrl)",
                active: () => this.mode == "subtract",
                handler: () => {
                    this.mode = "subtract";
                    this.active = true;
                },
            }, {
                icon: icons.tool_icon_xor_mode,
                tooltip: "Symmetric difference with the current selection",
                active: () => this.mode == "xor",
                handler: () => {
                    this.mode = "xor";
                    this.active = true;
                },
            },
            null,
            {
                icon: icons.tool_icon_invert_selection,
                tooltip: "Invert the current selection",
                handler: () => {
                    this.invert.emit();
                },
            }, {
                icon: icons.tool_icon_clear_selection,
                tooltip: "Clear the current selection and/or selection overlay (Esc)",
                handler: () => {
                    this.clear.emit();
                },
            },
        ];
    }
}
//# sourceMappingURL=select_tool.js.map