from __future__ import annotations


class RangeSelector:
    def __init__(self, mode="x", axis=None, color="red", linestyle="dashed",
                 linewidth=0.5, print_level=1, on_selected=None):
        if axis is None:
            raise ValueError("axis is required")
        self.ax = axis
        self.color = color
        self.linestyle = linestyle
        self.linewidth = linewidth
        self.print_level = print_level
        self.on_selected = on_selected
        self.x0 = self.x1 = self.y0 = self.y1 = 0.0
        self.vline0 = self.vline1 = self.hline0 = self.hline1 = None
        self.active = True
        self.set_mode(mode)
        canvas = self.ax.figure.canvas
        self.button_press = canvas.mpl_connect("button_press_event", self.on_press)
        self.button_release = canvas.mpl_connect("button_release_event", self.on_release)
        self.motion = canvas.mpl_connect("motion_notify_event", self.on_mouse_move)

    def set_mode(self, mode):
        self.moving = False
        self.mode_x = "x" in mode
        self.mode_y = "y" in mode
        if self.vline0 is None:
            self.vline0 = self.ax.axvline(self.x0, color=self.color, linestyle=self.linestyle, linewidth=self.linewidth)
            self.vline1 = self.ax.axvline(self.x1, color=self.color, linestyle=self.linestyle, linewidth=self.linewidth)
            self.hline0 = self.ax.axhline(self.y0, color=self.color, linestyle=self.linestyle, linewidth=self.linewidth)
            self.hline1 = self.ax.axhline(self.y1, color=self.color, linestyle=self.linestyle, linewidth=self.linewidth)
        for line in (self.vline0, self.vline1, self.hline0, self.hline1):
            line.set_visible(False)

    def set_active(self, flag=True):
        self.active = bool(flag)

    def on_press(self, event):
        if not self.active or event.button != 1 or event.inaxes is not self.ax:
            return
        self.moving = True
        self.x0, self.y0 = event.xdata, event.ydata
        if self.mode_x:
            self.vline0.set_xdata([self.x0]); self.vline0.set_visible(True)
        if self.mode_y:
            self.hline0.set_ydata([self.y0]); self.hline0.set_visible(True)
        self.ax.figure.canvas.draw_idle()

    def on_mouse_move(self, event):
        if not self.active or not self.moving or event.inaxes is not self.ax:
            return
        self.x1, self.y1 = event.xdata, event.ydata
        if self.mode_x:
            self.vline1.set_xdata([self.x1]); self.vline1.set_visible(True)
        if self.mode_y:
            self.hline1.set_ydata([self.y1]); self.hline1.set_visible(True)
        self.ax.figure.canvas.draw_idle()

    def on_release(self, event):
        if not self.moving:
            return
        if event.xdata is not None: self.x1 = event.xdata
        if event.ydata is not None: self.y1 = event.ydata
        self.finalize()

    def finalize(self):
        if self.print_level and self.mode_x:
            print(f"Selected x range: {self.x0} - {self.x1}")
        if self.print_level and self.mode_y:
            print(f"Selected y range: {self.y0} - {self.y1}")
        self.moving = False
        self.ax.figure.canvas.draw_idle()
        if self.on_selected is not None:
            self.on_selected(self.x0, self.x1, self.y0, self.y1)

    def clear(self):
        for line in (self.vline0, self.vline1, self.hline0, self.hline1):
            line.set_visible(False)
        self.ax.figure.canvas.draw_idle()

    def disconnect(self):
        canvas = self.ax.figure.canvas
        for cid in (self.button_press, self.button_release, self.motion):
            canvas.mpl_disconnect(cid)
