#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <errno.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <libnotify/notify.h>
#include <stdlib.h>
#include <sys/sysinfo.h>

#include "update-notifier.h"

#define STATUS_PATH "/var/snap/canonical-livepatch/current/status"

static void
init_notification ()
{
    notify_init ("update-notifier");
}

static void
init_gettext ()
{
    setlocale (LC_ALL, "");
    bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
    textdomain (GETTEXT_PACKAGE);
}

static void
show_notification (const char *summary, const char *body, const char *icon)
{
    NotifyNotification *n = notify_notification_new (summary, body, icon);
    notify_notification_set_timeout (n, 60000);
    notify_notification_show (n, NULL);
    g_object_unref (n);
}

static void
get_event_from_file (const char* filename, char **event, char **description)
{
    g_autofree gchar *content = NULL;

    g_return_if_fail (filename != NULL);
    g_return_if_fail (event != NULL);
    g_return_if_fail (description != NULL);

    *event = *description = NULL;

    g_file_get_contents (filename, &content, NULL, NULL);

    if (content) {
        gchar **strings = g_strsplit (content, " ", 2);

        if (g_strv_length (strings) > 0)
            *event = g_strdup (g_strstrip (strings[0]));
        if (g_strv_length (strings) > 1)
            *description = g_strdup (g_strstrip (strings[1]));

        g_strfreev (strings);
    }
}

static long
get_uptime ()
{
    struct sysinfo info;

    if (sysinfo (&info) == -1) {
        g_critical ("Failed to get uptime: %m");
        return -1;
    }

    return info.uptime;
}

static gboolean
file_modified_after_boot (const char* filename)
{
    GStatBuf status_stat;
    long uptime;
    time_t boot_timestamp;

    /* In case of error it's safer to assume that the status file has been
       modified after boot in order to not miss the notification. */
    if (g_stat (STATUS_PATH, &status_stat) == -1)
        return TRUE;

    if ((uptime = get_uptime ()) == -1)
        return TRUE;

    boot_timestamp = time (NULL) - (time_t) uptime;
    return difftime (status_stat.st_mtim.tv_sec, boot_timestamp) >= 0;
}

static void
show_status_notification ()
{
    g_autofree gchar *event = NULL;
    g_autofree gchar *description = NULL;

    if (!g_file_test (STATUS_PATH, G_FILE_TEST_EXISTS))
        return;

    if (!file_modified_after_boot (STATUS_PATH))
        return;

    get_event_from_file (STATUS_PATH, &event, &description);

    if (g_strcmp0 (event, "applied") == 0) {
        g_autofree gchar *body = NULL;
        gchar *endptr;
        gboolean is_overflow, conversion_failed;

        errno = 0;
        guint64 num_updates = g_ascii_strtoull (description, &endptr, 10);
        is_overflow = (num_updates == G_MAXUINT64 && errno == ERANGE);
        conversion_failed = (num_updates == 0 && description == endptr);

        if (is_overflow || conversion_failed) {
            g_warning ("Failed to parse the status file");
        } else if (num_updates != 0) {
            body = g_strdup_printf (
                ngettext ("%" G_GUINT64_FORMAT " Livepatch update has been successfully applied.",
                          "%" G_GUINT64_FORMAT " Livepatch updates have been successfully applied.",
                          num_updates),
                num_updates);

            show_notification (_("Canonical Livepatch"), body, NULL);
        }
    }
}

int
main (int argc, char **argv)
{
    init_notification ();
    init_gettext ();

    show_status_notification ();

    return EXIT_SUCCESS;
}
