from typing import Collection

from cairo import Context as CairoContext
from gi.repository import Gtk

from gaphas.item import Item


class RubberbandState:
    def __init__(self) -> None:
        self.reset()

    def reset(self) -> None:
        self.x0 = self.y0 = self.x1 = self.y1 = 0


class RubberbandPainter:
    """The rubberband painter should be used in conjunction with the rubberband
    tool.

    ``RubberbandState`` should be shared between the two.
    """

    def __init__(self, rubberband_state: RubberbandState) -> None:
        self.rubberband_state = rubberband_state

    def paint(self, items: Collection[Item], cairo: CairoContext) -> None:
        data = self.rubberband_state
        x0, y0, x1, y1 = data.x0, data.y0, data.x1, data.y1
        if x0 != x1 or y0 != y1:
            cairo.identity_matrix()
            cairo.rectangle(min(x0, x1), min(y0, y1), abs(x1 - x0), abs(y1 - y0))
            cairo.set_source_rgba(0.9, 0.9, 0.9, 0.3)
            cairo.fill_preserve()
            cairo.set_line_width(2.0)
            cairo.set_dash((7.0, 5.0), 0)
            cairo.set_source_rgba(0.5, 0.5, 0.7, 0.7)
            cairo.stroke()


def rubberband_tool(view, rubberband_state):
    """Rubberband selection tool.

    Should be used in conjunction with ``RubberbandPainter``.
    """
    gesture = (
        Gtk.GestureDrag.new(view)
        if Gtk.get_major_version() == 3
        else Gtk.GestureDrag.new()
    )
    gesture.connect("drag-begin", on_drag_begin, rubberband_state)
    gesture.connect("drag-update", on_drag_update, rubberband_state)
    gesture.connect("drag-end", on_drag_end, rubberband_state)
    return gesture


def on_drag_begin(gesture, start_x, start_y, rubberband_state):
    if gesture.set_state(Gtk.EventSequenceState.CLAIMED):
        rubberband_state.x0 = rubberband_state.x1 = start_x
        rubberband_state.y0 = rubberband_state.y1 = start_y


def on_drag_update(gesture, offset_x, offset_y, rubberband_state):
    rubberband_state.x1 = rubberband_state.x0 + offset_x
    rubberband_state.y1 = rubberband_state.y0 + offset_y
    view = gesture.get_widget()
    view.update_back_buffer()


def on_drag_end(gesture, offset_x, offset_y, rubberband_state):
    view = gesture.get_widget()
    x0 = rubberband_state.x0
    y0 = rubberband_state.y0
    x1 = x0 + offset_x
    y1 = y0 + offset_y
    items = view.get_items_in_rectangle(
        (min(x0, x1), min(y0, y1), abs(x1 - x0), abs(y1 - y0)), contain=True
    )
    view.selection.select_items(*items)
    rubberband_state.reset()
    view.update_back_buffer()
