#!/usr/bin/env python3
#
# Copyright 2008 Jose Fonseca
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#


import operator
import re
import sys

import gi

gi.require_version("Gtk", "3.0")

import xdot.ui
from gi.repository import Gdk, Gio, GObject, Gtk


class ControlPanel(Gtk.Box):
    def __init__(self):
        super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=5)
        # Place the control panel in the top right
        self.set_halign(Gtk.Align.END)
        self.set_valign(Gtk.Align.START)

        # Add the .control-panel CSS class to this widget
        context = self.get_style_context()
        context.add_class("control-panel")

    def add_group(self, group):
        self.pack_start(group, False, False, 0)


class ControlPanelGroup(Gtk.Expander):
    def __init__(self, title: str):
        Gtk.Expander.__init__(self, label=title)
        self._inner = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)

        # Set the size request to 200 pixels wide
        self.set_size_request(200, -1)

        # Add a bit of margin after the header
        self._inner.set_margin_top(5)

        self.add(self._inner)

    def add_row(self, widget):
        self._inner.pack_start(widget, False, False, 0)


class MyControlPanel(ControlPanel):
    def __init__(self):
        super().__init__()
        self._first_panel = ControlPanelGroup("Some Buttons")
        self._first_panel.add_row(Gtk.Button(label="Button 1"))
        self._first_panel.add_row(Gtk.Button(label="Button 2"))
        self.add_group(self._first_panel)

        self._second_panel = ControlPanelGroup("Extra Settings")
        self._second_panel.add_row(Gtk.Button(label="Button 3"))
        self._second_panel.add_row(Gtk.Button(label="Button 4"))
        self._second_panel.add_row(Gtk.CheckButton.new_with_label("First checkbox"))
        self._second_panel.add_row(Gtk.CheckButton.new_with_label("Second checkbox"))

        combo = Gtk.ComboBoxText()
        combo.append("first", "First Choice")
        combo.append("second", "Second Choice")
        combo.append("third", "Third Choice")
        combo.append("forth", "This one is quite long")
        combo.set_active_id("first")
        self._second_panel.add_row(combo)

        self.add_group(self._second_panel)


def __icon_button__(icon_name: str) -> Gtk.Button:
    icon = Gio.ThemedIcon(name=icon_name)
    image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
    button = Gtk.Button()
    button.add(image)
    return button


def get_handler_id(obj, signal_name):
    signal_id, detail = GObject.signal_parse_name(signal_name, obj, True)
    return GObject.signal_handler_find(
        obj, GObject.SignalMatchType.ID, signal_id, detail, None, None, None
    )


class MainView(Gtk.Grid):
    def __init__(self):
        super().__init__()
        self.searchbar = Gtk.SearchBar()
        self.dot = xdot.ui.DotWidget()
        self.searchentry = Gtk.SearchEntry()
        self.searchbar.connect_entry(self.searchentry)
        self.searchbar.add(self.searchentry)

        self.searchentry.connect("search-changed", self.search)
        self.searchentry.connect("stop-search", self.search_close)

        # Create the overlay widget
        self._overlay = Gtk.Overlay()

        # Add our editor and control panel as overlays
        self._overlay.add_overlay(self.dot)
        self._overlay.add_overlay(MyControlPanel())

        self.attach(self.searchbar, 0, 1, 1, 1)
        self.attach(self._overlay, 0, 0, 1, 1)
        self.dot.set_hexpand(True)
        self.dot.set_vexpand(True)

        self.dot.grab_focus()
        self.dot.connect("clicked", self.on_click)
        self.dot.disconnect(get_handler_id(self.dot, "key-press-event"))
        self.dot.connect("key-press-event", self.on_key_pressed)
        self.dot.connect("button-press-event", self.on_focus)

    def open_dotcode(self, path):
        fp = open(path, "rb")
        self.dot.set_dotcode(fp.read(), path)

    def set_dotcode(self, code):
        try:
            self.dot.set_dotcode(code)
        except AssertionError as e:
            print("something wrong:", e)

    def find_text(self, entry_text):
        found_items = []
        dot_widget = self.dot
        try:
            regexp = re.compile(entry_text)
        except re.error as err:
            sys.stderr.write('warning: re.compile() failed with error "%s"\n' % err)
            return []
        for element in (
            dot_widget.graph.nodes + dot_widget.graph.edges + dot_widget.graph.shapes
        ):
            if element.search_text(regexp):
                found_items.append(element)
        return sorted(found_items, key=operator.methodcaller("get_text"))

    def search(self, _):
        txt = self.searchentry.get_text()
        if txt:
            items = self.find_text(txt)
            self.dot.set_highlight(items, search=True)
        else:
            self.dot.set_highlight(None, search=True)

    def search_close(
        self,
        _,
    ):
        self.dot.grab_focus()

    def on_click(self, _, url, event):
        print(url, event)
        # center element
        x, y = int(event.x), int(event.y)
        jump = self.dot.get_jump(x, y)
        if jump is not None:
            self.dot.animate_to(jump.x, jump.y)

    def on_focus(self, widget, _):
        self.dot.grab_focus()

    def on_key_pressed(self, _, event):
        if event.keyval in (Gdk.KEY_f, Gdk.KEY_slash):
            if self.searchbar.get_search_mode():
                self.searchentry.grab_focus()
            else:
                self.searchbar.set_search_mode(True)
            return True
        if event.keyval == Gdk.KEY_w:
            self.dot.zoom_to_fit()
            return True
        if event.keyval == Gdk.KEY_plus:
            self.dot.zoom_image(self.dot.zoom_ratio * 1.1)
            return True
        if event.keyval == Gdk.KEY_minus:
            self.dot.zoom_image(self.dot.zoom_ratio / 1.1)
            return True
        if event.keyval == Gdk.KEY_equal:
            self.dot.zoom_image(1.0)
            return True
        if event.keyval == Gdk.KEY_Left:
            self.dot.x -= self.dot.POS_INCREMENT / self.dot.zoom_ratio
            self.dot.queue_draw()
            return True
        if event.keyval == Gdk.KEY_Right:
            self.dot.x += self.dot.POS_INCREMENT / self.dot.zoom_ratio
            self.dot.queue_draw()
            return True
        if event.keyval == Gdk.KEY_Up:
            self.dot.y -= self.dot.POS_INCREMENT / self.dot.zoom_ratio
            self.dot.queue_draw()
            return True
        if event.keyval == Gdk.KEY_Down:
            self.dot.y += self.dot.POS_INCREMENT / self.dot.zoom_ratio
            self.dot.queue_draw()
            return True


class MyDotWindow(Gtk.Window):
    def __init__(self):
        super().__init__(title="Test")
        self.set_default_size(400, 200)
        self._main_view = MainView()
        box = Gtk.VBox()
        paned1 = Gtk.Paned()
        paned2 = Gtk.Paned()
        button1 = Gtk.Button(label="Button1")
        button2 = Gtk.Button(label="Button2")
        paned1.add1(button1)
        paned1.add2(paned2)
        paned2.pack1(self._main_view, True, False)
        paned2.pack2(button2, False, True)
        box.pack_start(paned1, True, True, 0)

        self.add(box)
        self.show_all()

    def open_dotcode(self, path):
        self._main_view.open_dotcode(path)


def install_css():
    screen = Gdk.Screen.get_default()
    provider = Gtk.CssProvider()
    provider.load_from_data(
        b"""
      .control-panel {
        background-color: rgba(255,255,255, 0.8);
        padding: 4px;
        border-bottom-left-radius: 4px;
      }
      """
    )
    Gtk.StyleContext.add_provider_for_screen(screen, provider, 600)


def main():
    install_css()
    window = MyDotWindow()
    window.open_dotcode("../output/Test.dot")
    window.connect("delete-event", Gtk.main_quit)
    window.show_all()
    Gtk.main()


if __name__ == "__main__":
    main()
