import { TextAnnotation, TextAnnotationView } from "./text_annotation";
import { compute_angle, invert_angle, atan2 } from "../../core/util/math";
import { CoordinateUnits, AngleUnits, Direction } from "../../core/enums";
import { assert } from "../../core/util/assert";
import { Signal } from "../../core/signaling";
import { rotate_around } from "../../core/util/affine";
import { TextAnchor } from "../common/kinds";
import * as resolve from "../common/resolve";
import { Coordinate } from "../coordinates/coordinate";
function xy(x, y) {
    return { x, y };
}
export class LabelView extends TextAnnotationView {
    static __name__ = "LabelView";
    get mappers() {
        function mapper(units, scale, view, canvas) {
            switch (units) {
                case "canvas": return canvas;
                case "screen": return view;
                case "data": return scale;
            }
        }
        const overlay = this.model;
        const parent = this.layout ?? this.plot_view.frame;
        const { x_scale, y_scale } = this.coordinates;
        const { x_view, y_view } = parent.bbox;
        const { x_screen, y_screen } = this.plot_view.canvas.bbox;
        const xy = {
            x: mapper(overlay.x_units, x_scale, x_view, x_screen),
            y: mapper(overlay.y_units, y_scale, y_view, y_screen),
        };
        return xy;
    }
    get anchor() {
        const { align, baseline } = this.visuals.text.values();
        return resolve.text_anchor(this.model.anchor, align, baseline);
    }
    get angle() {
        const { angle, angle_units, direction } = this.model;
        return compute_angle(angle, angle_units, direction);
    }
    get origin() {
        const { mappers } = this;
        const { x, y, x_offset, y_offset } = this.model;
        const compute = (dim, value, mapper) => {
            return value instanceof Coordinate ? this.resolve_as_scalar(value, dim) : mapper.compute(value);
        };
        const sx = compute("x", x, mappers.x) + x_offset;
        const sy = compute("y", y, mappers.y) - y_offset; // TODO this needs to be unified with the rest of bokehjs
        return { sx, sy };
    }
    interactive_hit(sx, sy) {
        if (!this.model.visible || !this.model.editable) {
            return false;
        }
        return this._hit_test(sx, sy) == "area";
    }
    _hit_test(cx, cy) {
        const { sx, sy, anchor, angle, width, height } = this._rect;
        const { x, y } = rotate_around(xy(cx, cy), xy(sx, sy), -angle);
        const left = sx - anchor.x * width;
        const top = sy - anchor.y * height;
        const right = left + width;
        const bottom = top + height;
        if (left <= x && x <= right && top <= y && y <= bottom) {
            return "area";
        }
        else {
            return null;
        }
    }
    _can_hit(_target) {
        return true;
    }
    _pan_state = null;
    on_pan_start(ev) {
        if (this.model.visible && this.model.editable) {
            const { sx, sy } = ev;
            const target = this._hit_test(sx, sy);
            if (target != null && this._can_hit(target)) {
                this._pan_state = {
                    angle: this.angle,
                    base: { sx, sy },
                    target,
                    action: "rotate",
                };
                this.model.pan.emit(["pan:start", ev.modifiers]);
                return true;
            }
        }
        return false;
    }
    on_pan(ev) {
        assert(this._pan_state != null);
        const { dx, dy } = ev;
        const { angle, base } = this._pan_state;
        const { origin } = this;
        const angle0 = atan2([origin.sx, origin.sy], [base.sx, base.sy]);
        const angle1 = atan2([origin.sx, origin.sy], [base.sx + dx, base.sy + dy]);
        const da = angle1 - angle0;
        const na = angle + da;
        const nna = na % (2 * Math.PI);
        const { angle_units, direction } = this.model;
        this.model.angle = invert_angle(nna, angle_units, direction);
        this.model.pan.emit(["pan", ev.modifiers]);
    }
    on_pan_end(ev) {
        this._pan_state = null;
        this.model.pan.emit(["pan:end", ev.modifiers]);
    }
    cursor(sx, sy) {
        const target = this._pan_state?.target ?? this._hit_test(sx, sy);
        if (target == null || !this._can_hit(target)) {
            return null;
        }
        return "var(--bokeh-cursor-rotate)";
    }
}
export class Label extends TextAnnotation {
    static __name__ = "Label";
    constructor(attrs) {
        super(attrs);
    }
    static {
        this.prototype.default_view = LabelView;
        this.define(({ Bool, Float, Angle, Or, Ref }) => ({
            anchor: [TextAnchor, "auto"],
            x: [Or(Float, Ref(Coordinate))],
            y: [Or(Float, Ref(Coordinate))],
            x_units: [CoordinateUnits, "data"],
            y_units: [CoordinateUnits, "data"],
            x_offset: [Float, 0],
            y_offset: [Float, 0],
            angle: [Angle, 0],
            angle_units: [AngleUnits, "rad"],
            direction: [Direction, "anticlock"],
            editable: [Bool, false],
        }));
    }
    pan = new Signal(this, "pan");
}
//# sourceMappingURL=label.js.map