/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
 * Copyright (C) 2018 Canonical Ltd.
 *
 * 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 2 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 <http://www.gnu.org/licenses/>.
 */

/* Ubuntu report page {{{1 */

#define PAGE_ID "ubuntu-report"

#include "config.h"
#include "gis-ubuntu-report-page.h"
#include "gis-page-header.h"
#include "ubuntu-report-resources.h"

#include <glib/gi18n.h>
#include <gio/gio.h>
#include <libsysmetrics.h>

struct _GisUbuntuReportPage {
  GisPage parent;

  GtkWidget *opt_in_check;
  GtkWidget *header;
  GtkWidget *cancel_report_b;
  GtkWidget *show_report_b;
  GCancellable *cancellable;
  gchar *report;
};

G_DEFINE_TYPE (GisUbuntuReportPage, gis_ubuntu_report_page, GIS_TYPE_PAGE);

static void
cancel_report (GtkButton *button, GisUbuntuReportPage *page)
{
  gtk_widget_set_visible (GTK_WIDGET (page->cancel_report_b), FALSE);
  gtk_widget_set_visible (GTK_WIDGET (page->show_report_b), TRUE);

  g_cancellable_cancel (page->cancellable);
  g_clear_object (&page->cancellable);
  page->cancellable = g_cancellable_new ();
}

static void
gis_ubuntu_report_page_constructed (GObject *object)
{
  GisUbuntuReportPage *page = GIS_UBUNTU_REPORT_PAGE (object);
  g_autoptr(GtkWidget) image = NULL;

  G_OBJECT_CLASS (gis_ubuntu_report_page_parent_class)->constructed (object);

  page->cancellable = g_cancellable_new ();
  g_signal_connect(page->cancel_report_b, "clicked", G_CALLBACK(cancel_report), page);

  image = g_object_ref_sink (gtk_image_new_from_resource (
    "/org/gnome/initial-setup/ubuntu-report.svg"));
  g_object_set (page->header, "paintable", gtk_image_get_paintable (GTK_IMAGE (image)), NULL);
  g_object_set (page->header, "height-request", 280, NULL);
  gis_page_set_complete (GIS_PAGE (page), TRUE);
}

static void
gis_ubuntu_report_page_finalize (GObject *object)
{
  GisUbuntuReportPage *page = GIS_UBUNTU_REPORT_PAGE (object);

  g_cancellable_cancel (page->cancellable);
  g_clear_object (&page->cancellable);

  g_free (page->report);

  G_OBJECT_CLASS (gis_ubuntu_report_page_parent_class)->finalize (object);
}

static void
on_collect_report_thread (GTask        *task,
                          gpointer      source_object,
                          gpointer      task_data,
                          GCancellable *cancellable)
{
  g_autofree char *report = NULL;
  g_autofree char *error_msg = NULL;

  error_msg = sysmetrics_collect (&report);
  if (error_msg != NULL) {
    g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "%s", error_msg);
    return;
  }

  g_task_return_pointer (task, g_steal_pointer (&report), g_free);
}

static void
on_show_report_callback (GObject      *source_object,
                         GAsyncResult *res,
                         gpointer      user_data)
{
  GtkWidget *dialog, *scroll, *text_view;
  GTask *task = G_TASK (res);
  g_autoptr (GError) error = NULL;
  GisUbuntuReportPage *page = GIS_UBUNTU_REPORT_PAGE (user_data);

  gtk_widget_set_visible (GTK_WIDGET (page->show_report_b), TRUE);
  gtk_widget_set_visible (GTK_WIDGET (page->cancel_report_b), FALSE);

  page->report = g_task_propagate_pointer (task, &error);

  if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
    return;

  if (error != NULL) {
    dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (page))),
                                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_CLOSE,
                                     _("Failed to get report information: %s"),
                                     error->message);
    g_signal_connect_swapped (dialog,
                              "response",
                              G_CALLBACK (gtk_window_destroy),
                              dialog);
    gtk_window_present (GTK_WINDOW (dialog));
    return;
  }

  dialog = gtk_dialog_new_with_buttons (_("Ubuntu Report Contents"),
                                        GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (page))),
                                        GTK_DIALOG_MODAL |
                                        GTK_DIALOG_DESTROY_WITH_PARENT |
                                        GTK_DIALOG_USE_HEADER_BAR,
                                        NULL, NULL);
  gtk_widget_set_size_request (dialog, 800, 600);

  scroll = gtk_scrolled_window_new ();

  text_view = gtk_text_view_new ();
  gtk_text_view_set_monospace (GTK_TEXT_VIEW (text_view), TRUE);
  gtk_widget_set_hexpand (scroll, TRUE);
  gtk_widget_set_vexpand (scroll, TRUE);
  gtk_widget_set_margin_top (scroll, 6);
  gtk_widget_set_margin_bottom (scroll, 6);
  gtk_widget_set_margin_start (scroll, 6);
  gtk_widget_set_margin_end (scroll, 6);
  gtk_box_append (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), scroll);
  gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)), page->report, -1);
  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scroll), text_view);

  g_signal_connect_swapped (dialog,
                            "response",
                            G_CALLBACK (gtk_window_destroy),
                            dialog);
  gtk_window_present (GTK_WINDOW (dialog));
}

static void
show_report (GtkButton *button, GisUbuntuReportPage *page)
{
  g_autofree char *error = NULL;
  g_autoptr(GTask) task = NULL;

  gtk_widget_set_visible (GTK_WIDGET (page->show_report_b), FALSE);
  gtk_widget_set_visible (GTK_WIDGET (page->cancel_report_b), TRUE);

  task = g_task_new (page, page->cancellable, on_show_report_callback, page);

  if (page->report) {
    g_task_return_pointer (task, g_steal_pointer (&page->report), NULL);
    return;
  }

  g_task_set_return_on_cancel (task, TRUE);
  g_task_run_in_thread (task, on_collect_report_thread);
}

static void
on_show_uri_cb (GObject *source_object,
                GAsyncResult *result,
                gpointer user_data)
{
  g_autoptr(GError) error = NULL;
  GisUbuntuReportPage *page = user_data;

  if (!gtk_show_uri_full_finish (GTK_WINDOW (source_object), result, &error)) {
    GtkWidget *dialog;

    if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
      return;

    dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (page))),
                                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_CLOSE,
                                     _("Failed to show privacy policy: %s"), error->message);
    g_signal_connect_swapped (dialog,
                              "response",
                              G_CALLBACK (gtk_window_destroy),
                              dialog);
    gtk_window_present (GTK_WINDOW (dialog));
  }
}

static void
show_legal (GtkButton *button, GisUbuntuReportPage *page)
{
  gtk_show_uri_full (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (page))),
                     "https://www.ubuntu.com/legal/terms-and-policies/systems-information-notice",
                     GDK_CURRENT_TIME, page->cancellable, on_show_uri_cb, page);
}

typedef struct {
  gboolean send_allowed;
  gchar *report;
} SendReportThreadData;

static void
send_report_thread_data_free (SendReportThreadData *data)
{
  g_free (data->report);
  g_free (data);
}

static void
on_collect_send_report_thread (GTask *task,
                               gpointer source_object,
                               gpointer task_data,
                               GCancellable *cancellable)
{
  SendReportThreadData *data = task_data;
  g_autofree char *error = NULL;

  if (!data->send_allowed) {
    error = sysmetrics_send_decline (FALSE, "");

    if (error != NULL) {
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
                               _("Failed to send report decline: %s"), error);
      return;
    }

    g_task_return_boolean (task, TRUE);
    return;
  }

  if (!data->report) {
    error = sysmetrics_collect (&data->report);

    if (error != NULL) {
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
                               _("Failed to get report information: %s"), error);
      return;
    }
  }

  g_assert (data->report);
  error = sysmetrics_send_report (data->report, FALSE, "");

  if (error != NULL) {
    g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
                             _("Failed to send report information: %s"), error);
    return;
  }

  g_task_return_boolean (task, TRUE);
}

static void
on_collect_send_report_callback (GObject *source_object,
                                 GAsyncResult *res,
                                 gpointer user_data)
{
  GTask *task = G_TASK (res);
  g_autoptr (GError) error = NULL;

  if (!g_task_propagate_boolean (task, &error)) {
    g_warning ("%s", error->message);
  }
}

/* Retrieve and send the report (or decline thereof).
 * Don't wait on the server to respond though, go to next page immediately.
 * Ubuntu Report will take care of sending it via a Systemd service if the
 * thread doesn't manage to do it anyway.
 */
static gboolean
gis_ubuntu_report_page_apply (GisPage *gis_page, GCancellable *cancellable)
{
  GisUbuntuReportPage *page = GIS_UBUNTU_REPORT_PAGE (gis_page);
  g_autofree char *error = NULL;
  g_autoptr(GTask) task = NULL;
  SendReportThreadData *data;

  data = g_new0 (SendReportThreadData, 1);
  data->send_allowed = gtk_check_button_get_active (GTK_CHECK_BUTTON (page->opt_in_check));
  data->report = g_steal_pointer (&page->report);

  task = g_task_new (page, NULL, on_collect_send_report_callback, NULL);
  g_task_set_task_data (task, g_steal_pointer (&data),
                        (GDestroyNotify) send_report_thread_data_free);

  g_task_run_in_thread (task, on_collect_send_report_thread);

  gis_page_apply_complete (GIS_PAGE (page), TRUE);
  return TRUE;
}

static void
gis_ubuntu_report_page_locale_changed (GisPage *gis_page)
{
  GisUbuntuReportPage *page = GIS_UBUNTU_REPORT_PAGE (gis_page);
  g_autofree gchar *timezone_text = NULL;
  g_autofree gchar *description_text = NULL;
  g_autoptr(GError) error = NULL;

  gis_page_set_title (GIS_PAGE (page), _("Help improve Ubuntu"));

  if (!g_file_get_contents ("/etc/timezone", &timezone_text, NULL, &error)) {
    g_warning ("Failed to get timezone from /etc/timezone: %s", error->message);
    return;
  }

  description_text = g_strdup_printf (_("Help us improve Ubuntu by sharing your system data with us. "
                                        "This includes things like your machine model, "
					                              "installed software and the location you chose for your timezone."));
  g_object_set (page->header, "subtitle", description_text, NULL);
}

static void
gis_ubuntu_report_page_class_init (GisUbuntuReportPageClass *klass)
{
  GisPageClass *page_class = GIS_PAGE_CLASS (klass);
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-ubuntu-report-page.ui");
  gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisUbuntuReportPage, header);
  gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisUbuntuReportPage, show_report_b);
  gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisUbuntuReportPage, cancel_report_b);
  gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisUbuntuReportPage, opt_in_check);
  gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), show_report);
  gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), show_legal);

  page_class->page_id = PAGE_ID;
  page_class->apply = gis_ubuntu_report_page_apply;
  page_class->locale_changed = gis_ubuntu_report_page_locale_changed;
  object_class->constructed = gis_ubuntu_report_page_constructed;
  object_class->finalize = gis_ubuntu_report_page_finalize;
}

static void
gis_ubuntu_report_page_init (GisUbuntuReportPage *page)
{
  g_resources_register (ubuntu_report_get_resource ());
  g_type_ensure (GIS_TYPE_PAGE_HEADER);

  gtk_widget_init_template (GTK_WIDGET (page));
}

GisPage *
gis_prepare_ubuntu_report_page (GisDriver *driver)
{
  return g_object_new (GIS_TYPE_UBUNTU_REPORT_PAGE,
                       "driver", driver,
                       NULL);
}
