from vispy.scene.visuals import Compound, Ellipse

from napari._vispy.overlays.base import ViewerOverlayMixin, VispyCanvasOverlay


class VispyBrushCircleOverlay(ViewerOverlayMixin, VispyCanvasOverlay):
    def __init__(self, *, viewer, overlay, parent=None):
        self._white_circle = Ellipse(
            center=(0, 0),
            color=(0, 0, 0, 0.0),
            border_color='white',
            border_method='agg',
        )
        self._black_circle = Ellipse(
            center=(0, 0),
            color=(0, 0, 0, 0.0),
            border_color='black',
            border_method='agg',
        )

        super().__init__(
            node=Compound([self._white_circle, self._black_circle]),
            viewer=viewer,
            overlay=overlay,
            parent=parent,
        )

        self._last_mouse_pos = None

        self.overlay.events.size.connect(self._on_size_change)
        self.node.events.canvas_change.connect(self._on_canvas_change)
        self.viewer.events.mouse_over_canvas.connect(
            self._on_mouse_over_canvas
        )
        # no need to connect position, since that's in the base classes of CanvasOverlay

        self.reset()

        # manually connect this once once and get the correct canvas
        if parent is not None:
            parent.scene.canvas.events.mouse_move.connect(self._on_mouse_move)

    def _on_position_change(self, event=None):
        self._set_position(self.overlay.position)

    def _on_size_change(self, event=None):
        self._white_circle.radius = self.overlay.size / 2
        self._black_circle.radius = self._white_circle.radius - 1

    def _on_visible_change(self):
        if self._last_mouse_pos is not None:
            self._set_position(self._last_mouse_pos)
        self.node.visible = (
            self.overlay.visible and self.viewer.mouse_over_canvas
        )

    def _on_mouse_move(self, event):
        self._last_mouse_pos = event.pos
        if self.overlay.visible:
            self.overlay.position = event.pos.tolist()

    def _set_position(self, pos):
        if not self.overlay.position_is_frozen:
            self.node.transform.translate = [pos[0], pos[1], 0, 0]

    def _on_canvas_change(self, event):
        if event.new is not None:
            event.new.events.mouse_move.connect(self._on_mouse_move)
        if event.old is not None:
            event.old.events.mouse_move.disconnect(self._on_mouse_move)

    def _on_mouse_over_canvas(self):
        if self.viewer.mouse_over_canvas:
            # Move the cursor outside the canvas when the mouse leaves it.
            # It fixes the bug described in PR #5763:
            # https://github.com/napari/napari/pull/5763#issuecomment-1523182141
            self._set_position((-1000, -1000))
            self.node.visible = self.overlay.visible
        else:
            if self.overlay.visible:
                self.node.visible = self.overlay.position_is_frozen
            else:
                self.node.visible = False

    def reset(self):
        super().reset()
        self._on_size_change()
        self._last_mouse_pos = None

    def close(self):
        self.node.events.canvas_change.disconnect(self._on_canvas_change)
        super().close()
