import { isField } from "../../../core/vectorization";
import { dict } from "../../../core/util/object";
import { isArray } from "../../../core/util/types";
import { GlyphRenderer } from "../../renderers/glyph_renderer";
import { PolyTool, PolyToolView } from "./poly_tool";
import { tool_icon_poly_draw } from "../../../styles/icons.css";
export class PolyDrawToolView extends PolyToolView {
    static __name__ = "PolyDrawToolView";
    _drawing = false;
    _initialized = false;
    _tap(ev) {
        if (this._drawing) {
            this._draw(ev, "add", true);
        }
        else {
            this._select_event(ev, this._select_mode(ev), this.model.renderers);
        }
    }
    _draw(ev, mode, emit = false) {
        const renderer = this.model.renderers[0];
        const point = this._map_drag(ev.sx, ev.sy, renderer);
        if (!this._initialized) {
            this.activate(); // Ensure that activate has been called
        }
        if (point == null) {
            return;
        }
        const [x, y] = this._snap_to_vertex(ev, ...point);
        const cds = renderer.data_source;
        const data = dict(cds.data);
        const glyph = renderer.glyph;
        const xkey = isField(glyph.xs) ? glyph.xs.field : null;
        const ykey = isField(glyph.ys) ? glyph.ys.field : null;
        if (mode == "new") {
            this._pop_glyphs(cds, this.model.num_objects);
            if (xkey != null) {
                cds.get_array(xkey).push([x, x]);
            }
            if (ykey != null) {
                cds.get_array(ykey).push([y, y]);
            }
            this._pad_empty_columns(cds, [xkey, ykey]);
        }
        else if (mode == "edit") {
            if (xkey != null) {
                const column = data.get(xkey) ?? [];
                const xs = column[column.length - 1];
                xs[xs.length - 1] = x;
            }
            if (ykey != null) {
                const column = data.get(ykey) ?? [];
                const ys = column[column.length - 1];
                ys[ys.length - 1] = y;
            }
        }
        else if (mode == "add") {
            if (xkey != null) {
                const column = data.get(xkey) ?? [];
                const xidx = column.length - 1;
                let xs = cds.get_array(xkey)[xidx];
                const nx = xs[xs.length - 1];
                xs[xs.length - 1] = x;
                if (!isArray(xs)) {
                    xs = Array.from(xs);
                    column[xidx] = xs;
                }
                xs.push(nx);
            }
            if (ykey != null) {
                const column = data.get(ykey) ?? [];
                const yidx = column.length - 1;
                let ys = cds.get_array(ykey)[yidx];
                const ny = ys[ys.length - 1];
                ys[ys.length - 1] = y;
                if (!isArray(ys)) {
                    ys = Array.from(ys);
                    column[yidx] = ys;
                }
                ys.push(ny);
            }
        }
        this._emit_cds_changes(cds, true, false, emit);
    }
    _show_vertices() {
        if (!this.model.active) {
            return;
        }
        const xs = [];
        const ys = [];
        for (let i = 0; i < this.model.renderers.length; i++) {
            const renderer = this.model.renderers[i];
            const { glyph, data_source } = renderer;
            const xkey = isField(glyph.xs) ? glyph.xs.field : null;
            const ykey = isField(glyph.ys) ? glyph.ys.field : null;
            if (xkey != null) {
                for (const array of data_source.get_array(xkey)) {
                    xs.push(...array);
                }
            }
            if (ykey != null) {
                for (const array of data_source.get_array(ykey)) {
                    ys.push(...array);
                }
            }
            if (this._drawing && (i == (this.model.renderers.length - 1))) {
                // Skip currently drawn vertex
                xs.splice(xs.length - 1, 1);
                ys.splice(ys.length - 1, 1);
            }
        }
        this._set_vertices(xs, ys);
    }
    _press(ev) {
        if (!this.model.active) {
            return;
        }
        if (this._drawing) {
            this._drawing = false;
            this._draw(ev, "edit", true);
        }
        else {
            this._drawing = true;
            this._draw(ev, "new", true);
        }
    }
    _move(ev) {
        if (this._drawing) {
            this._draw(ev, "edit");
        }
    }
    _remove() {
        const renderer = this.model.renderers[0];
        const { glyph, data_source } = renderer;
        const xkey = isField(glyph.xs) ? glyph.xs.field : null;
        const ykey = isField(glyph.ys) ? glyph.ys.field : null;
        const data = dict(data_source.data);
        if (xkey != null) {
            const column = data.get(xkey) ?? [];
            const xidx = column.length - 1;
            const xs = data_source.get_array(xkey)[xidx];
            xs.splice(xs.length - 1, 1);
        }
        if (ykey != null) {
            const column = data.get(ykey) ?? [];
            const yidx = column.length - 1;
            const ys = data_source.get_array(ykey)[yidx];
            ys.splice(ys.length - 1, 1);
        }
        this._emit_cds_changes(data_source);
    }
    _keyup(ev) {
        if (!this.model.active || !this._mouse_in_frame) {
            return;
        }
        for (const renderer of this.model.renderers) {
            if (ev.key == "Backspace") {
                this._delete_selected(renderer);
            }
            else if (ev.key == "Escape") {
                if (this._drawing) {
                    this._remove();
                    this._drawing = false;
                }
                renderer.data_source.selection_manager.clear();
            }
        }
    }
    _pan_start(ev) {
        if (!this.model.drag) {
            return;
        }
        this._select_event(ev, "append", this.model.renderers);
        this._basepoint = [ev.sx, ev.sy];
    }
    _pan(ev) {
        if (this._basepoint == null || !this.model.drag) {
            return;
        }
        const [bx, by] = this._basepoint;
        // Process polygon/line dragging
        for (const renderer of this.model.renderers) {
            const basepoint = this._map_drag(bx, by, renderer);
            const point = this._map_drag(ev.sx, ev.sy, renderer);
            if (point == null || basepoint == null) {
                continue;
            }
            const cds = renderer.data_source;
            const { glyph } = renderer;
            const xkey = isField(glyph.xs) ? glyph.xs.field : null;
            const ykey = isField(glyph.ys) ? glyph.ys.field : null;
            if (xkey == null && ykey == null) {
                continue;
            }
            const [x, y] = point;
            const [px, py] = basepoint;
            const [dx, dy] = [x - px, y - py];
            const data = dict(cds.data);
            for (const index of cds.selected.indices) {
                let length, xs, ys;
                if (xkey != null) {
                    const column = data.get(xkey) ?? [];
                    xs = column[index];
                }
                if (ykey != null) {
                    const column = data.get(ykey) ?? [];
                    ys = column[index];
                    length = ys.length;
                }
                else {
                    length = xs.length;
                }
                for (let i = 0; i < length; i++) {
                    if (xs) {
                        xs[i] += dx;
                    }
                    if (ys) {
                        ys[i] += dy;
                    }
                }
            }
            cds.change.emit();
        }
        this._basepoint = [ev.sx, ev.sy];
    }
    _pan_end(ev) {
        if (!this.model.drag) {
            return;
        }
        this._pan(ev);
        for (const renderer of this.model.renderers) {
            this._emit_cds_changes(renderer.data_source);
        }
        this._basepoint = null;
    }
    activate() {
        if (this.model.vertex_renderer == null || !this.model.active) {
            return;
        }
        this._show_vertices();
        if (!this._initialized) {
            for (const renderer of this.model.renderers) {
                const cds = renderer.data_source;
                cds.connect(cds.properties.data.change, () => this._show_vertices());
            }
        }
        this._initialized = true;
    }
    deactivate() {
        if (this._drawing) {
            this._remove();
            this._drawing = false;
        }
        if (this.model.vertex_renderer != null) {
            this._hide_vertices();
        }
    }
}
export class PolyDrawTool extends PolyTool {
    static __name__ = "PolyDrawTool";
    constructor(attrs) {
        super(attrs);
    }
    static {
        this.prototype.default_view = PolyDrawToolView;
        this.define(({ Bool, Int, List, Ref }) => ({
            drag: [Bool, true],
            num_objects: [Int, 0],
            renderers: [List(Ref((GlyphRenderer))), []],
        }));
    }
    tool_name = "Polygon Draw Tool";
    tool_icon = tool_icon_poly_draw;
    event_type = ["pan", "tap", "press", "move"];
    default_order = 3;
}
//# sourceMappingURL=poly_draw_tool.js.map