import gi

gi.require_version("GtkSource", "5")
from gi.repository import Adw, GObject, GtkSource, Pango

import logging


class FocusModeHelper(GObject.Object):

    # Adwaita light_4
    UNFOCUSED_COLOUR_LIGHT = "#c0bfbc"
    # Adwaita dark_2
    UNFOCUSED_COLOUR_DARK = "#5e5c64"

    def __init__(self, buffer: GtkSource.Buffer) -> None:
        super().__init__()
        self.__buffer = buffer
        self.__handler_ids = []
        self.__active = False

        start_iter = buffer.get_start_iter()
        self.__last_start = buffer.create_mark(None, start_iter)
        self.__last_end = buffer.create_mark(None, start_iter)

        style_manager = Adw.StyleManager.get_default()
        style_manager.connect("notify::dark", self.__on_style_change)
        init_colour = (
            self.UNFOCUSED_COLOUR_DARK if style_manager.get_dark() else self.UNFOCUSED_COLOUR_LIGHT
        )

        self.__tag = buffer.create_tag(
            "unfocused", foreground=init_colour, underline=Pango.Underline.NONE
        )

    @GObject.Property(type=bool, default=False)
    def active(self) -> bool:
        return self.__active

    @active.setter
    def active(self, value: bool) -> None:
        self.__active = value
        if self.__active and len(self.__handler_ids) == 0:
            self.__activate()
        elif not self.__active and len(self.__handler_ids) > 0:
            self.__deactivate()

    def __activate(self) -> None:
        logging.debug("Activating focus mode")
        for signal in ("cursor-moved", "changed"):
            handler_id = self.__buffer.connect(signal, lambda _o: self.__refresh(initial=False))
            self.__handler_ids.append(handler_id)
        self.__reset_marks()
        self.__refresh(initial=True)

    def __deactivate(self) -> None:
        logging.debug("Deactivating focus mode")
        self.__buffer.remove_tag(
            self.__tag, self.__buffer.get_start_iter(), self.__buffer.get_end_iter()
        )
        for handler_id in self.__handler_ids:
            self.__buffer.disconnect(handler_id)
        self.__handler_ids = []

    def __refresh(self, initial: bool) -> None:
        buffer = self.__buffer

        # Determine current sentence
        start_iter = buffer.get_iter_at_mark(buffer.get_insert())
        if not start_iter.starts_sentence() and not start_iter.starts_line():
            start_iter.backward_sentence_start()
        end_iter = start_iter.copy()
        if not start_iter.ends_line():
            end_iter.forward_sentence_end()

        # Check if changed
        last_start = buffer.get_iter_at_mark(self.__last_start)
        last_end = buffer.get_iter_at_mark(self.__last_end)
        start_changed = not last_start.equal(start_iter) or initial
        end_changed = not last_end.equal(end_iter) or initial
        if not start_changed and not end_changed:
            return

        # Untag last sections
        if start_changed:
            buffer.remove_tag(self.__tag, buffer.get_start_iter(), last_start)
        if end_changed:
            buffer.remove_tag(self.__tag, last_end, buffer.get_end_iter())

        # Tag new sections
        if start_changed:
            if not start_iter.is_start():
                buffer.apply_tag(self.__tag, buffer.get_start_iter(), start_iter)
            buffer.move_mark(self.__last_start, start_iter)
        if end_changed:
            if not end_iter.is_end():
                buffer.apply_tag(self.__tag, end_iter, buffer.get_end_iter())
            buffer.move_mark(self.__last_end, end_iter)

    def __on_style_change(
        self,
        style_manager: Adw.StyleManager,
        _value: GObject.ParamSpec,
    ) -> None:
        if style_manager.get_dark():
            self.__tag.set_property("foreground", self.UNFOCUSED_COLOUR_DARK)
        else:
            self.__tag.set_property("foreground", self.UNFOCUSED_COLOUR_LIGHT)

    def __reset_marks(self) -> None:
        start_iter = self.__buffer.get_start_iter()
        self.__buffer.move_mark(self.__last_start, start_iter)
        self.__buffer.move_mark(self.__last_end, start_iter)
