#include <gtk/gtk.h>

#include "teleport-app.h"
#include "teleport-peer.h"
#include "teleport-window.h"
#include "teleport-browser.h"
#include "teleport-publish.h"
#include "teleport-server.h"
#include "teleport-get.h"


static void save_file_callback          (GSimpleAction *simple,
                                         GVariant      *parameter,
                                         gpointer       user_data);

static void do_nothing_callback         (GSimpleAction *simple,
                                         GVariant      *parameter,
                                         gpointer       user_data);

static void open_file_callback          (GSimpleAction *simple,
                                         GVariant      *parameter,
                                         gpointer       user_data);


static void open_folder_callback        (GSimpleAction        *simple,
                                         GVariant             *parameter,
                                         gpointer              user_data);

static void teleport_app_quit           (GSimpleAction        *simple,
                                         GVariant             *parameter,
                                         gpointer              user_data);

enum {
  NOTIFY_USER, NOTIFY_FINISED, N_SIGNALS
};

static GActionEntry app_entries[] =
{
    { "save", save_file_callback, "as", NULL, NULL },
    { "decline", do_nothing_callback, "as", NULL, NULL },
    { "do-nothing", do_nothing_callback, "as", NULL, NULL },
    { "open-file", open_file_callback, "as", NULL, NULL },
    { "open-folder", open_folder_callback, "as", NULL, NULL },
    { "quit",   teleport_app_quit }
};

static TeleportApp *mainApplication;
static gint signalIds [N_SIGNALS];

typedef struct
{
  GtkWidget     *window;
  TeleportPeer  *peerList;
} TeleportAppPrivate;


struct _TeleportApp {
  GtkApplication        parent;

  /*< private >*/
  TeleportAppPrivate    *priv;
};

G_DEFINE_TYPE_WITH_PRIVATE (TeleportApp, teleport_app, GTK_TYPE_APPLICATION);


static void 
save_file_callback (GSimpleAction *simple,
                    GVariant      *parameter,
                    gpointer       user_data) {
  teleport_get_do_downloading(g_variant_get_string (g_variant_get_child_value (parameter, 0), NULL),
                              g_variant_get_string (g_variant_get_child_value (parameter, 1), NULL),
                              g_variant_get_string (g_variant_get_child_value (parameter, 2), NULL));
}

static void 
do_nothing_callback (GSimpleAction *simple,
                     GVariant      *parameter,
                     gpointer       user_data) {
  g_print ("No action\n");
}

static void
open_folder_callback (GSimpleAction *simple,
                      GVariant      *parameter,
                      gpointer       user_data)
{
  GDBusProxy      *proxy;
  GVariant        *retval;
  GVariantBuilder *builder;
  const gchar     *uri;
  GError **error = NULL;
  const gchar     *path;

  path = g_strdup_printf("%s/%s",
                         g_variant_get_string (g_variant_get_child_value (parameter, 3), NULL),
                         g_variant_get_string (g_variant_get_child_value (parameter, 2), NULL));

  uri = g_filename_to_uri (path, NULL, error);
  g_debug ("Show file: %s", uri);

  proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
                                         G_DBUS_PROXY_FLAGS_NONE,
                                         NULL,
                                         "org.freedesktop.FileManager1",
                                         "/org/freedesktop/FileManager1",
                                         "org.freedesktop.FileManager1",
                                         NULL, error);

  if (!proxy) {
    g_prefix_error (error,
                    ("Connecting to org.freedesktop.FileManager1 failed: "));
  }
  else {

    builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
    g_variant_builder_add (builder, "s", uri);

    retval = g_dbus_proxy_call_sync (proxy,
                                     "ShowItems",
                                     g_variant_new ("(ass)",
                                                    builder,
                                                    ""),
                                     G_DBUS_CALL_FLAGS_NONE,
                                     -1, NULL, error);

    g_variant_builder_unref (builder);
    g_object_unref (proxy);

    if (!retval)
      {
        g_prefix_error (error, ("Calling ShowItems failed: "));
      }
    else
      g_variant_unref (retval);
  }
}


static void
open_file_callback (GSimpleAction *simple,
                    GVariant      *parameter,
                    gpointer       user_data)
{
  const gchar *path;
  g_print("Open file\n %s%s",
          g_variant_get_string (g_variant_get_child_value (parameter, 3), NULL),
          g_variant_get_string (g_variant_get_child_value (parameter, 2), NULL));

  path = g_strdup_printf("%s/%s",
                         g_variant_get_string (g_variant_get_child_value (parameter, 3), NULL),
                         g_variant_get_string (g_variant_get_child_value (parameter, 2), NULL));

  gtk_show_uri_on_window (NULL, g_filename_to_uri(path, NULL, NULL), GDK_CURRENT_TIME, NULL);
}

void 
create_user_notification (const char *file_name, const int file_size, const char *origin_device, GVariant *target) {
  GIcon *icon;
  GNotification *notification = g_notification_new ("Teleport");
  TeleportAppPrivate *priv = mainApplication->priv;
  g_notification_set_body (notification,
                           g_strdup_printf("%s is sending %s (%s)",
                                           teleport_peer_get_name_by_addr (priv->peerList, origin_device),
                                           file_name,
                                           g_format_size (file_size)));
  icon = g_themed_icon_new ("dialog-information");
  g_notification_set_icon (notification, icon);
  g_notification_set_default_action_and_target_value (notification, "app.do-nothing", target);
  g_notification_add_button_with_target_value (notification, "Decline", "app.decline", target);
  g_notification_add_button_with_target_value (notification, "Save", "app.save", target);
  g_notification_set_priority (notification, G_NOTIFICATION_PRIORITY_HIGH);
  g_application_send_notification ((GApplication *) mainApplication, NULL, notification);
  g_object_unref (icon);
  g_object_unref (notification);
  //the example says I have to unref it but it gives a critival error
  //https://developer.gnome.org/glib/stable/gvariant-format-strings.html
  //g_variant_unref (target);
}

void
create_finished_notification (const char *origin, const int filesize, const char *filename, GVariant *target) {
  GIcon *icon;
  GNotification *notification = g_notification_new ("Teleport");
  TeleportAppPrivate *priv = mainApplication->priv;

  g_notification_set_body (notification,
                           g_strdup_printf("Transfer of %s from %s is complete", 
                                           filename,
                                           teleport_peer_get_name_by_addr (priv->peerList, origin)));
  icon = g_themed_icon_new ("dialog-information");
  g_notification_set_icon (notification, icon);
  g_notification_set_default_action_and_target_value (notification, "app.do-nothing", target);
  g_notification_add_button_with_target_value (notification, "Show in folder", "app.open-folder", target);
  g_notification_add_button_with_target_value (notification, "Open", "app.open-file", target);
  g_notification_set_priority (notification, G_NOTIFICATION_PRIORITY_HIGH);
  g_application_send_notification ((GApplication *) mainApplication, NULL, notification);
  g_object_unref (icon);
  g_object_unref (notification);
}


gboolean
mainLoopAddPeerCallback (gpointer peer) {
  TeleportAppPrivate *priv = mainApplication->priv;
  GtkWidget *window = priv->window;

  update_remote_device_list((TeleportWindow *) window, (Peer *) peer);
  return G_SOURCE_REMOVE;
}

gboolean
mainLoopRemovePeerCallback (gpointer peer)  {
  TeleportAppPrivate *priv = mainApplication->priv;
  GtkWidget *window = priv->window;

  update_remote_device_list_remove((TeleportWindow *) window, (Peer *) peer);
  return G_SOURCE_REMOVE;
}

void
callback_add_peer (GObject *instance, Peer *peer, gpointer window) {
  g_idle_add(mainLoopAddPeerCallback, peer);
}

void
callback_remove_peer (GObject *instance, Peer *peer, gpointer window) {
  g_idle_add(mainLoopRemovePeerCallback, peer);
}

void
callback_notify_user (GObject *instance, gchar *name, gpointer window) {
  //create_user_notification("icon.png", 2000, "Mark's laptop");
}

static void
teleport_app_startup (GApplication *app) {
  TeleportAppPrivate *priv;
  GtkStyleProvider *provider;

  mainApplication = TELEPORT_APP (app);
  priv = mainApplication->priv;

  g_action_map_add_action_entries (G_ACTION_MAP (app),
                                   app_entries,
                                   G_N_ELEMENTS (app_entries),
                                   app);

  G_APPLICATION_CLASS (teleport_app_parent_class)->startup (app);

  /* window */
  priv->window = GTK_WIDGET (teleport_window_new (TELEPORT_APP (app)));

  /* CSS style */
  provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ());
  gtk_css_provider_load_from_resource (GTK_CSS_PROVIDER (provider),
                                       "com/frac_tion/teleport/style.css");
  gtk_style_context_add_provider_for_screen (gdk_screen_get_default(),
                                             provider,
                                             GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  g_object_unref (provider);

  /* PeerList */
  priv->peerList = g_object_new (TELEPORT_TYPE_PEER, NULL);

  /* Add signals */
  g_signal_connect (priv->peerList, "addpeer", (GCallback)callback_add_peer, priv->window);
  g_signal_connect (priv->peerList, "removepeer", (GCallback)callback_remove_peer, priv->window);
  g_signal_connect (app, "notify_user", (GCallback)callback_notify_user, priv->window);

  teleport_server_run();
  teleport_publish_run (teleport_get_device_name());
  teleport_browser_run_avahi_service(priv->peerList);
}

static void
teleport_app_activate (GApplication *app) {
  TeleportAppPrivate *priv;
  priv = mainApplication->priv;

  gtk_widget_show (priv->window);
  gtk_window_present (GTK_WINDOW (priv->window));
}

static void
teleport_app_finalize (GObject *object)
{
  G_OBJECT_CLASS (teleport_app_parent_class)->finalize (object);
}

static void
teleport_app_class_init (TeleportAppClass *class)
{
  G_APPLICATION_CLASS (class)->startup = teleport_app_startup;
  G_APPLICATION_CLASS (class)->activate = teleport_app_activate;

  G_OBJECT_CLASS (class)->finalize = teleport_app_finalize;

  signalIds[NOTIFY_USER] = g_signal_new ("notify_user",
                                         G_TYPE_OBJECT,
                                         G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                                         0,
                                         NULL /* accumulator */,
                                         NULL /* accumulator data */,
                                         NULL /* C marshaller */,
                                         G_TYPE_NONE /* return_type */,
                                         1,
                                         G_TYPE_STRING);
}

static void
teleport_app_quit (GSimpleAction *simple,
                   GVariant      *parameter,
                   gpointer       user_data)
{
  TeleportAppPrivate *priv = TELEPORT_APP (user_data)->priv;

  gtk_widget_destroy (priv->window);
}

TeleportApp *
teleport_app_new (void)
{
  return g_object_new (TELEPORT_APP_TYPE,
                       "application-id", "com.frac_tion.teleport",
                       "flags", G_APPLICATION_HANDLES_OPEN,
                       NULL);
}

static void
teleport_app_init (TeleportApp *app) {
  TeleportAppPrivate *priv = teleport_app_get_instance_private (app);
  app->priv = priv;
}


