# utils.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 datetime import datetime, timezone
from enum import Enum
from gi.repository import GLib
import re

meshtastic_config_dir = GLib.get_user_config_dir() + '/gtk_meshtastic_client'
messages_file_path = meshtastic_config_dir + '/messages.json'

def idToHex(nodeId):
    return '!' + hex(nodeId)[2:]

time_ago_cutoff_hours = 12

def linkifyMessage(message) -> str:
    #From https://stackoverflow.com/questions/7676255/find-and-replace-urls-in-a-block-of-text-return-text-list-of-urls
    urlpattern = re.compile(r"""(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))""")

    linkified_message = urlpattern.sub(r'<a href="\1">\1</a>', message)

    if linkified_message == message:
        return message

    """
    If there isn't an http in the link, just manually add it so it will work
    """
    s = linkified_message.split()
    for i in range(len(s)):
        item = s.pop(0)
        if item.startswith("href=\"") and not item.startswith("href=\"http"):
            linkified_message = linkified_message.replace("href=\"", "href=\"http://")
            break

    return linkified_message

def getTimeAgoSec(delta_secs: int) -> str:
    """ Allow an error offset of 1 minute """
    if delta_secs <= 60 and delta_secs >= -60:
        return "Under 1 Minute"

    # initializing string
    date_string = ""

    mins = int(delta_secs // 60) % 60
    hours = int(delta_secs // (60*60)) % 24
    days = int(delta_secs // (60*60*24))

    if days == 1:
        date_string = date_string + "1 Day "
    elif days > 1:
        date_string = date_string + str(days) + " Days "

    if hours == 1:
        date_string = date_string + "1 Hour "
    elif hours > 1:
        date_string = date_string + str(hours) + " Hours "

    if mins == 1:
        date_string = date_string + "1 Minute"
    else:
        date_string = date_string + str(mins) + " Minutes"

    return date_string

def getTimeAgoDate(ts):
    """Format how long ago have we heard from this node (aka timeago)."""
    if ts is None:
        return None
    now = datetime.now()
    timestamp = datetime.fromtimestamp(ts)
    delta = now - timestamp
    delta_secs = int(delta.total_seconds())
    hours = delta_secs // (60*60)
    if delta_secs <= 60 and delta_secs >= -60:
        return "Just Now"
    elif hours < time_ago_cutoff_hours:
        return getTimeAgoSec(delta_secs) + " ago"
    else:
        return timestamp.ctime()

def getUnixTimestampNow():
    return int(datetime.now(timezone.utc).timestamp())

def gga_sentence_builder(lat, lng, alt):
    degrees_lat = abs(lat)
    degrees_lat, minutes_lat = divmod(degrees_lat * 60, 60)
    degrees_lat = int(degrees_lat * 100)

    degrees_lon = abs(lng)
    degrees_lon, minutes_lon = divmod(degrees_lon * 60, 60)
    degrees_lon = int(degrees_lon * 100)

    nmea = "$" + "GN" + "GGA" + ","
    nmea = nmea + str(datetime.utcnow().strftime("%H%M%S.%f")[0:9]) + ","

    nmea = nmea + str(f"{degrees_lat + minutes_lat:.5f}".zfill(10)) + ","
    nmea = nmea + ("S" if lat < 0 else "N") + ","

    nmea = nmea + str(f"{degrees_lon + minutes_lon:.5f}".zfill(11)) + ","
    nmea = nmea + ("W" if lng < 0 else "E") + ","

    # GPS Quality
    nmea = nmea + "1" + ","
    # Satelites
    nmea = nmea +  "0,"
    # HDOP
    nmea = nmea + "0,"
    # Altitude
    nmea = nmea + str(alt) + ","
    # AltVal
    nmea = nmea + "M" + ","
    # Geoidal separation
    nmea = nmea + "," + "M,"
    # Differential corrections
    nmea = nmea + "," + ","
    nmea = nmea + "*"

    cksum = 0
    for sub in nmea:
        cksum ^= ord(sub)
    nmea = nmea + f"{cksum:02X}"

    return nmea

class MsgDirection(Enum):
    In = 0
    Out = 1

"""
As far as I can tell, this isn't in the
Meshtastic Library
"""
class ChannelRole(Enum):
    Disabled = 0
    Primary = 1
    Secondary = 2
