# message_parser.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
from gi.repository import GLib
import gettext
import meshtastic.serial_interface
import meshtastic.mesh_interface
from pubsub import pub
import json
import time
import os

import gtk_meshtastic_client.utils as utils

def process_packet_cb(packet) -> bool:
    app = Gtk.Application.get_default()
    win = Gtk.Application.get_active_window(app)

    startup_timeout = win.connection_page_bin.message_parser.startup_timeout

    app.logger.debug("New message")

    if not hasattr(win.connection_page_bin, 'interface'):
        app.logger.warning("Warning: Received packet but you are disconnected!")
        return GLib.SOURCE_REMOVE

    if packet['decoded'].get('portnum') == 'TELEMETRY_APP':
        if not startup_timeout:
            win.nearby_nodes_page_bin.packet_node_update(packet, win.connection_page_bin.interface)
        else:
            app.logger.debug("Startup Timer active, not updating telemetry")
        return GLib.SOURCE_REMOVE

    elif packet['decoded'].get('portnum') == 'NODEINFO_APP':
        if not startup_timeout:
            win.nearby_nodes_page_bin.packet_node_update(packet, win.connection_page_bin.interface)
        else:
            app.logger.debug("Startup Timer active, not updating Node Info")
        return GLib.SOURCE_REMOVE

    elif packet['decoded'].get('portnum') == 'POSITION_APP':
        if not startup_timeout:
            win.nearby_nodes_page_bin.packet_node_update(packet, win.connection_page_bin.interface)
        else:
            app.logger.debug("Startup Timer active, not updating position")
        return GLib.SOURCE_REMOVE

    elif packet['decoded'].get('portnum') == 'TEXT_MESSAGE_APP':
        app.logger.debug("Processing Text Message")
        win.channels_page_bin.new_text_message(packet, win.connection_page_bin.interface)
        return GLib.SOURCE_REMOVE

    elif packet['decoded'].get('portnum') == 'ROUTING_APP':
        if 'priority' in packet:
            if packet['priority'] == "ACK":
                 win.channels_page_bin.process_ack_message(packet, win.connection_page_bin.interface)
                 return GLib.SOURCE_REMOVE
        if packet['to'] == win.connection_page_bin.interface.myInfo.my_node_num:
            win.channels_page_bin.process_routing_app_message(packet, win.connection_page_bin.interface)
            return GLib.SOURCE_REMOVE

    return GLib.SOURCE_REMOVE

class MessageParser():

    startup_timeout = True

    def connection_timeout_cb(self) -> bool:
        self.startup_timeout = False
        self.logger.debug("Startup timeout is now False")
        return GLib.SOURCE_REMOVE

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

        if app.app_settings.get_log_packets():
            json_packet = json.dumps(packet, indent=4, default=lambda s: " ".join(str(s).split()))
            if os.path.exists(utils.messages_file_path):
                with open(utils.messages_file_path, 'r+') as json_file:
                    content = json_file.read().strip()
                    if content:
                        if content.endswith(']'):
                            content = content[:-1].rstrip() + ',\n'
                        else:
                            content = '['
                        json_file.seek(0)
                        json_file.write(content + json_packet + '\n]')
                    else:
                        json_file.seek(0)
                        json_file.write('[' + json_packet + '\n]')
                    json_file.truncate()
            else:
                with open(utils.messages_file_path, 'w') as json_file:
                    json_file.write('[\n' + json_packet + '\n]')

        if app.app_settings.get_print_packets():
            print(f"{packet} \n\n")

        if 'decoded' in packet:
            app = Gtk.Application.get_default()
            win = Gtk.Application.get_active_window(app)

            """
            For some reason if this is all synchronous, GTK does a lot of weird segfaults
            This is also longer than the connection page timeout to ensure that any packets
            that come right on connection are processed after the nearby nodes page
            is set up
            """
            GLib.timeout_add(100, process_packet_cb, packet)
            return

    def reset_connection_timeout(self):
        self.startup_timeout = True
        self.logger.debug("Startup timeout is now True")

    """
    When there is a connection, there's a whole bunch of misc node update messages
    that are stored that happen, but we already get our updates from the node database.
    As such, we are making a small timeout (10 seconds) so we do not process them.
    """
    def start_connection_timeout(self):
        GLib.timeout_add(10000, self.connection_timeout_cb)

    def __init__(self):
        app = Gtk.Application.get_default()
        self.logger = app.logger

        # The python library will automatically process any messages stored, so there
        # is no need to manually scan for messages on connection
        pub.subscribe(self.onReceive, 'meshtastic.receive')
