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

#include <gio/gdesktopappinfo.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <libnotify/notify.h>
#include <sys/sysinfo.h>

#include "update-notifier.h"
#include "livepatch-utils.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
notify_action_cb (NotifyNotification *notification,
                  char *action,
                  gpointer user_data)
{
    g_autoptr(GDesktopAppInfo) info = NULL;
    g_autoptr(GdkAppLaunchContext) context = NULL;
    g_autoptr(GError) error = NULL;

    info = g_desktop_app_info_new (LIVEPATCH_DESKTOP_FILE);
    if (!info) {
        g_warning ("Could not find application '%s'", LIVEPATCH_DESKTOP_FILE);
        return;
    }

    context = gdk_display_get_app_launch_context (gdk_display_get_default ());
    if (!g_app_info_launch (G_APP_INFO (info), NULL, G_APP_LAUNCH_CONTEXT (context), &error)) {
        g_warning ("Could not launch application '%s'", LIVEPATCH_DESKTOP_FILE);
    }
}

static gboolean
show_notification (const char *summary, const char *body, const char *icon)
{
    NotifyNotification *n;
    g_autoptr(GError) error = NULL;

    n = notify_notification_new (summary, body, icon);
    notify_notification_set_timeout (n, 60000);

    if (livepatch_has_settings_ui()) {
        notify_notification_add_action (n, "settings", _("Show Settings…"),
                                        notify_action_cb, NULL, NULL);
        notify_notification_add_action (n, "default", _("Show Settings…"),
                                        notify_action_cb, NULL, NULL);
    } else {
        g_warning ("There is no graphical application installed to manage "
                   "Livepatch. The notification will not have a "
                   "'Show Settings…' button.");
    }

    g_signal_connect (n, "closed", G_CALLBACK (gtk_main_quit), NULL);

    if (!notify_notification_show (n, &error)) {
        g_warning ("Could not show notification: '%s", error->message);
        return FALSE;
    }

    return TRUE;
}

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 gboolean
show_status_notification ()
{
    g_autofree gchar *state = NULL;
    g_autoptr(GError) error = NULL;

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

    if (!file_modified_after_boot (STATUS_PATH))
        return FALSE;

    state = livepatch_get_state (&error);
    if (state == NULL) {
        g_warning ("Failed to get Livepatch state: %s", error->message);
        return FALSE;
    }

    if (g_strcmp0(state, "applied") != 0)
        return FALSE;

    return show_notification ("Livepatch", _("An update has just been applied."), NULL);
}

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

    if (show_status_notification ())
        gtk_main ();

    return EXIT_SUCCESS;
}
