# mearby_nodes_page.py
#
# Copyright 2024 Christopher Talbot
#
# 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

from gi.repository import Adw
from gi.repository import Gtk
import gettext
import meshtastic.serial_interface
import meshtastic.mesh_interface
from pubsub import pub

import gtk_meshtastic_client.node_row as node_row
import gtk_meshtastic_client.utils as utils
import gtk_meshtastic_client.nearby_nodes_page as nearby_nodes_page

@Gtk.Template(resource_path='/org/kop316/meshtastic/ui/nearby_nodes_page.ui')
class NearbyNodesPageBin(Adw.Bin):
    __gtype_name__ = 'NearbyNodesPageBin'

    nearby_nodes_list_box = Gtk.Template.Child()
    own_node = Gtk.Template.Child()
    search_entry = Gtk.Template.Child()
    search_buffer = Gtk.Template.Child()

    nearby_nodes_list_box_children = 0

    def _filter_func(self, row, list_row, data):
        filter_text = self.search_buffer.get_text()
        if filter_text == "":
            return True

        title = row.get_title()
        if filter_text in title:
            return True

        short_name = row.short_name_label.get_label()
        if filter_text in short_name:
            return True

        return False

    def _sort_func(self, node1, node2, data=None):
        if node1.favorited and not node2.favorited:
            return -1

        if not node1.favorited and node2.favorited:
            return 1

        return node2.lastHeard - node1.lastHeard

    def add_node_to_nearby_nodes(self, new_node):
        self.nearby_nodes_list_box.append(new_node)
        self.nearby_nodes_list_box_children += 1

    def reset_all_nodes(self):
        self.own_node.reset_node()

        self.nearby_nodes_list_box.remove_all()
        self.nearby_nodes_list_box_children = 0

    def populate_all_nodes(self, interface):
        app = Gtk.Application.get_default()
        win = Gtk.Application.get_active_window(app)

        for node in list(interface.nodes.values()):
            if node["num"] == interface.myInfo.my_node_num:
                self.own_node.populate_node(node)
                continue

            new_node = node_row.NodeRow()
            new_node.populate_node(node)
            """
            Since we added a sort function, the rows will automatically sort when
            appended, no need to manually specifiy
            """
            self.nearby_nodes_list_box.append(new_node)

            self.nearby_nodes_list_box_children += 1

        win.connection_page_bin.database.retrieve_favorite_nodes(self.nearby_nodes_list_box, self.nearby_nodes_list_box_children)

    def is_node_own_node(self, num):
        if num == self.own_node.num:
            node_to_change = self.own_node
            return True

        return False

    def find_node_by_id(self, num):
        if num == self.own_node.num:
            node_to_change = self.own_node
            return node_to_change
        else:
            for x in range(self.nearby_nodes_list_box_children):
                node_to_test = self.nearby_nodes_list_box.get_row_at_index(x)
                if num == node_to_test.num:
                    node_to_change = node_to_test
                    return node_to_change

        return

    """
    The Telemetry app comes in about every minute or so. Since it does that,
    we just update all of the Node UIs when any update comes in.
    """
    def packet_node_update(self, packet, interface):
        app = Gtk.Application.get_default()
        win = Gtk.Application.get_active_window(app)

        node_to_change = self.find_node_by_id(packet['from'])
        if not node_to_change:
            node_to_change = node_row.NodeRow()
            self.nearby_nodes_list_box.prepend(node_to_change)
            self.nearby_nodes_list_box_children += 1

        node_to_change.update_node_with_packet(packet)
        node_to_change.update_node_ui()
        win.maps_page_bin.goto_map_last_seen_first_load()

        for x in range(self.nearby_nodes_list_box_children):
            node = self.nearby_nodes_list_box.get_row_at_index(x)
            node.update_node_time()

        if not self.is_node_own_node(packet['from']):
            self.nearby_nodes_list_box.invalidate_sort()

    @Gtk.Template.Callback()
    def _search_bar_button_clicked_cb(self, button):
        visible = self.search_entry.get_visible()
        self.search_entry.set_visible(not visible)
        if visible:
            self.search_buffer.set_text("", -1)
            self.nearby_nodes_list_box.invalidate_filter()

    @Gtk.Template.Callback()
    def _search_buffer_deleted_text_cb (self, position, n_chars, user_data):
        self.nearby_nodes_list_box.invalidate_filter()

    @Gtk.Template.Callback()
    def _search_buffer_inserted_text_cb(self, position, chars, n_chars, user_data):
        self.nearby_nodes_list_box.invalidate_filter()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        app = Gtk.Application.get_default()

        self.logger = app.logger
        self.nearby_nodes_list_box.set_sort_func(sort_func=self._sort_func)
        self.nearby_nodes_list_box.set_filter_func(self._filter_func, None, None)

        self.own_node.set_title("Disconnected")
        self.own_node.set_own_node(True)
