# window.py
#
# Copyright 2024 Lo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later

import gi
import re as regex
from gi.repository import Adw, Gtk, Gio, GObject


from .grid_card import NucleusGridCard
from .element_info import NucleusElementInfo
from .electron_shell_dialog import ElectronShellDialog
from .utils import get_category_color


@Gtk.Template(resource_path="/page/codeberg/lo_vely/Nucleus/window.ui")
class NucleusWindow(Adw.ApplicationWindow):
    __gtype_name__ = "NucleusWindow"

    stack = Gtk.Template.Child()
    results_stack = Gtk.Template.Child()
    searchbar = Gtk.Template.Child()
    search_entry = Gtk.Template.Child()
    search_listbox = Gtk.Template.Child()
    results_count = 0

    bottom_sheet = Gtk.Template.Child()
    periodic_table = Gtk.Template.Child()
    split_view = Gtk.Template.Child()
    sidebar_scrolled_window = Gtk.Template.Child()
    close_sidebar_button = Gtk.Template.Child()
    legend = Gtk.Template.Child()

    last_selected_element: Gtk.ToggleButton = GObject.Property(type=Gtk.ToggleButton, default=None)
    current_element_name: str = GObject.Property(type=str, default=" ")
    show_sidebar_title: bool = GObject.Property(type=bool, default=False)
    element_source_link: str = GObject.Property(type=str)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.settings = Gio.Settings(schema_id="page.codeberg.lo_vely.Nucleus")
        self.settings.bind("width", self, "default-width", Gio.SettingsBindFlags.DEFAULT)
        self.settings.bind("height", self, "default-height", Gio.SettingsBindFlags.DEFAULT)
        self.settings.bind("is-maximized", self, "maximized", Gio.SettingsBindFlags.DEFAULT)

        from .periodic_table_data import data as json_file # Import translatable database

        for element in json_file["elements"]:
            row_symbol_center_box = Gtk.CenterBox(
                width_request=35,
                halign=Gtk.Align.CENTER,
            )
            row_symbol = Gtk.Label(
                label=element['symbol'],
                halign=Gtk.Align.CENTER,
                css_classes=["title-3"]
            )
            row = Adw.ActionRow(
                title=element['name'],
                subtitle=f"{element['number']} ⸱ {element['category'].title()}",
                activatable=True,
            )

            row_symbol_center_box.set_center_widget(row_symbol)
            row.add_prefix(row_symbol_center_box)
            row.element = element

            self.search_listbox.append(row)

            card = NucleusGridCard(element)
            get_category_color(card, element['category'])
            card.connect("clicked", self.on_grid_card_clicked, element)
            row.connect("activated", self.on_search_row_clicked, card, element)

            self.periodic_table.attach(
                card,
                element['xpos'],
                element['ypos'],
                width=1,
                height=1
            )

        #Grid numbers
        for number in range(1, 19):
            self.periodic_table.attach(
                child=Gtk.Label(
                    label=str(number),
                    margin_bottom=6,
                    css_classes=['dimmed']
                ),
                column=number,
                row=0,
                width=1,
                height=1
            )

        for number in range(1, 9):
            self.periodic_table.attach(
                child=Gtk.Label(
                    label=str(number),
                    margin_end=6,
                    css_classes=['dimmed']
                ),
                column=0,
                row=number,
                width=1,
                height=1
            )

        self.periodic_table.attach(self.legend, column=2, row=1, width=2, height=1)

        self.bottom_sheet.connect("notify::open", self.on_bottom_sheet_open_changed)

        self.search_listbox.set_filter_func(self.search_filter)
        self.searchbar.connect_entry(self.search_entry)
        self.searchbar.connect(
            "notify::search-mode-enabled",
            lambda *_: self.stack.set_visible_child_name (
                "search-view" if self.searchbar.get_search_mode() else "table-view"
            ),
        )

        self.search_entry.connect("search-changed", self.on_search_changed)
        self.sidebar_scrolled_window.props.vadjustment.connect("value-changed", self._on_vadjustment_value_changed)

        if self.props.default_width <= 445:
            self.searchbar.set_search_mode(True)


    def _on_vadjustment_value_changed(self, vadjustment) -> None:
        if vadjustment.props.value > 120:
            self.show_sidebar_title = True
        else:
            self.show_sidebar_title = False


    @Gtk.Template.Callback()
    def launch_source(self, _button) -> None:
        Gtk.UriLauncher(uri=self.props.element_source_link).launch(self, None)


    def search_filter(self, row):
        match = regex.search(
            self.search_entry.props.text,
            row.props.title + row.props.subtitle + row.element["symbol"],
            regex.IGNORECASE
        )
        if match:
            self.results_count += 1
        return match


    def on_search_changed(self, _search_widget):
        self.search_listbox.invalidate_filter()

        if self.results_count == 0:
            self.results_stack.set_visible_child_name("no-results")
        else:
            self.results_stack.set_visible_child_name("results")

        self.results_count = 0


    def on_search_row_clicked(self, row, grid_card, data):
        self.open_element_details(data)

        if self.last_selected_element:
            self.last_selected_element.props.active = False

        self.last_selected_element = grid_card
        self.last_selected_element.props.active = True


    def on_bottom_sheet_open_changed(self, _x, _y):
        if not self.bottom_sheet.props.open:
            self.close_element_details()


    @Gtk.Template.Callback()
    def close_element_details(self, _clicked_button=None):
        if self.last_selected_element is not None:
            self.last_selected_element.props.active = False
            self.last_selected_element.grab_focus()

        self.split_view.props.show_sidebar = False
        self.bottom_sheet.props.open = False


    def open_element_details(self, data):
        self.bottom_sheet.props.open = True
        self.split_view.props.show_sidebar = True
        self.load_chem_info(data)


    def on_grid_card_clicked(self, button, data):
        if self.last_selected_element is None:
            self.last_selected_element = button

        if button != self.last_selected_element:
            self.last_selected_element.props.active = False
            self.last_selected_element = button

            self.open_element_details(data)

        elif button == self.last_selected_element:
            if not self.split_view.props.show_sidebar:
                self.open_element_details(data)
            else:
                self.close_element_details()


    def load_chem_info(self, data) -> None:
        self.props.element_source_link = data['source']
        info = NucleusElementInfo(data)

        info.electron_shell_row.connect("activated", self._present_shell_dialog, data)
        self.current_element_name = data['name']

        if self.close_sidebar_button.props.visible:
            self.close_sidebar_button.grab_focus()

        self.sidebar_scrolled_window.props.child = info


    def _present_shell_dialog(self, _x, data) -> None:
        dialog = ElectronShellDialog(data['name'], data['shells'])
        dialog.present(self)
