/*
 This file is part of telegram-purple

 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, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA

 Copyright Ben Wiederhake 2016, Purple developers 2007-2015

 Derived from the nullclient.c example at
 https://github.com/Tasssadar/libpurple/blob/master/example/nullclient.c
 */

#include <assert.h>
#include <stdio.h>
#include <unistd.h>

#include <glib.h>
#include <purple.h>
#include <signal.h>
#include <string.h>

#include "../commit.h"
#include "../telegram-purple.h"

// The following eventloop functions are used in both pidgin and purple-text. If your application uses glib mainloop, you can safely use this verbatim.
#define PURPLE_GLIB_READ_COND  (G_IO_IN | G_IO_HUP | G_IO_ERR)
#define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)

typedef struct TdfGLibIOClosure {
  PurpleInputFunction function;
  guint result;
  gpointer data;
} TdfGLibIOClosure;

typedef struct TdfAccountRequestInfo {
  PurpleAccountRequestType type;
  PurpleAccount *account;
  void *ui_handle;
  char *user;
  gpointer userdata;
  PurpleAccountRequestAuthorizationCb auth_cb;
  PurpleAccountRequestAuthorizationCb deny_cb;
  guint ref;
} TdfAccountRequestInfo;

static void tdf_glib_io_destroy (gpointer data) {
  g_free (data);
}

static gboolean tdf_glib_io_invoke (GIOChannel *source, GIOCondition condition, gpointer data) {
  TdfGLibIOClosure *closure = data;
  PurpleInputCondition purple_cond = 0;

  if (condition & PURPLE_GLIB_READ_COND)
    purple_cond |= PURPLE_INPUT_READ;
  if (condition & PURPLE_GLIB_WRITE_COND)
    purple_cond |= PURPLE_INPUT_WRITE;

  closure->function (closure->data, g_io_channel_unix_get_fd (source), purple_cond);

  return TRUE;
}

static guint tdf_glib_input_add (gint fd, PurpleInputCondition condition, PurpleInputFunction function, gpointer data) {
  TdfGLibIOClosure *closure = g_new0(TdfGLibIOClosure, 1);
  GIOChannel *channel;
  GIOCondition cond = 0;

  closure->function = function;
  closure->data = data;

  if (condition & PURPLE_INPUT_READ)
    cond |= PURPLE_GLIB_READ_COND;
  if (condition & PURPLE_INPUT_WRITE)
    cond |= PURPLE_GLIB_WRITE_COND;

  channel = g_io_channel_unix_new (fd);
  closure->result = g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, cond, tdf_glib_io_invoke, closure, tdf_glib_io_destroy);

  g_io_channel_unref (channel);
  return closure->result;
}

static PurpleEventLoopUiOps tdf_glib_eventloop_ops = {
  g_timeout_add,
  g_source_remove,
  tdf_glib_input_add,
  g_source_remove,
  NULL,
#if GLIB_CHECK_VERSION(2,14,0)
  g_timeout_add_seconds,
#else
  NULL,
#endif
  // padding
  NULL,
  NULL,
  NULL
};
// End of the eventloop functions.

static PurpleConnectionUiOps connection_uiops = {
    NULL,                      // connect_progress
    NULL,                      // connected
    NULL,                      // disconnected
    NULL,                      // notice
    NULL,                      // report_disconnect
    NULL,                      // network_connected
    NULL,                      // network_disconnected
    NULL,                      // report_disconnect_reason
    // padding
    NULL,
    NULL,
    NULL
};

static void tdf_init (void) {
  // This should initialize the UI components for all the modules.
  purple_connections_set_ui_ops (&connection_uiops);
}

static PurpleCoreUiOps tdf_core_uiops = {
  NULL,
  NULL,
  tdf_init,
  NULL,
  // padding
  NULL,
  NULL,
  NULL,
  NULL
};

PurplePlugin *tgp = NULL;

static void display_search_paths (GList *elem) {
  printf ("Search paths:");
  for (; elem; elem = elem->next) {
    printf (" %s", (char *)elem->data);
  }
  printf ("\n");
}

static void tdf_inject_plugin (void) {
  printf ("Injecting our module into purple_plugins_* ...\n");
  purple_plugins_init ();
  GList *search_paths = purple_plugins_get_search_paths ();
  assert (!search_paths->prev && !search_paths->next && search_paths->data);
  GList *new_paths = g_list_append (search_paths, g_strdup ("bin/"));
  assert (new_paths == search_paths);
  // Load "my" path before the default.
  assert (search_paths->next && search_paths->next->data);
  {
    // swap(search_paths->data, search_paths->next->data);
    gchar* tmp = search_paths->data;
    search_paths->data = search_paths->next->data;
    search_paths->next->data = tmp;
  }
}

gboolean tdf_is_in_ci (void) {
  gchar* envvar = getenv ("CONTINUOUS_INTEGRATION");
  return envvar && g_str_equal ("true", envvar);
}

static void init_libpurple (void) {
  // Set a custom user directory (optional)
  purple_util_set_user_dir ("test/tmp/user");

  // We *do* want debugging. However, this is just too much noise.
  // Then again, this doesn't hurt if Travis does it. Query the environment to check whether we're in CI or not.
  purple_debug_set_enabled (tdf_is_in_ci ());

  /* Set the core-uiops, which is used to
   * - initialize the ui specific preferences.
   * - initialize the debug ui.
   * - initialize the ui components for all the modules.
   * - uninitialize the ui components for all the modules when the core terminates.
   */
  purple_core_set_ui_ops (&tdf_core_uiops);

  // Set the uiops for the eventloop. Since our client is glib-based, you can safely copy this verbatim. */
  purple_eventloop_set_ui_ops (&tdf_glib_eventloop_ops);

  tdf_inject_plugin ();
  display_search_paths (purple_plugins_get_search_paths ());

  // Now that all the essential stuff has been set, let's try to init the core.
  printf ("Enter purple_core_init ...\n");
  if (!purple_core_init ("tgp-dummy")) {
    // Initializing the core failed. Terminate.
    fprintf (stderr, "libpurple initialization failed. Abort.\n");
    abort ();
  }
  printf ("Core initialized.\n");

  // Create and load the buddylist.
  purple_set_blist (purple_blist_new ());

  // Find our plugin
  printf ("Checking version:\n");
  tgp = purple_plugins_find_with_id (PLUGIN_ID);
  if (!tgp) {
    printf ("Huh! Can't find plugin! Expected ID \"" PLUGIN_ID "\"");
    abort ();
  }

  // Make sure we loaded the correct plugin
  printf ("This is what we loaded:\n\t\t\tversion: %s\n", tgp->info->version);
  gchar *pos = g_strrstr (tgp->info->version, "commit: " GIT_COMMIT);
  if (pos == NULL) {
    printf ("This is the wrong version, we built for commit %s. Abort!\n", GIT_COMMIT);
    abort ();
  } else {
    printf ("Looks like the right version, because we also expected commit %s.\n", GIT_COMMIT);
  }
}

//static void signed_on (PurpleConnection *gc) {
//  PurpleAccount *account = purple_connection_get_account (gc);
//  printf("Account connected: \"%s\" (%s)\n", purple_account_get_username(account), purple_account_get_protocol_id(account));
//}
//
//static void received_im_msg (PurpleAccount *account, char *sender, char *message, PurpleConversation *conv, PurpleMessageFlags flags) {
//  if (conv==NULL) {
//      conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
//  }
//
//  printf("(%s) %s (%s): %s\n", purple_utf8_strftime("%H:%M:%S", NULL), sender, purple_conversation_get_name(conv), message);
//}
//
//static void connect_to_signals(void)
//{
//
//        static int handle;
//
//        purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle,
//                                PURPLE_CALLBACK(signed_on), NULL);
//
//        purple_signal_connect(purple_conversations_get_handle(), "received-im-msg", &handle,
//                                PURPLE_CALLBACK(received_im_msg), NULL);
//
//}

int main(int argc, char *argv[])
{
  printf ("Running loadtest on %s.\n", argv[1]);
  GMainLoop *loop = g_main_loop_new(NULL, FALSE);

  /* libpurple's built-in DNS resolution forks processes to perform
   * blocking lookups without blocking the main process.  It does not
   * handle SIGCHLD itself, so if the UI does not you quickly get an army
   * of zombie subprocesses marching around.
   */
  signal(SIGCHLD, SIG_IGN);

  printf ("Enter init_libpurple\n");
  init_libpurple ();
  printf ("libpurple %s initialized.\n", purple_core_get_version());

  // Since we do not yet do anything useful, simply terminate after a few seconds.
  purple_timeout_add (2000, purple_core_quit_cb, NULL);
  g_timeout_add (4000, (GSourceFunc)g_main_loop_quit, loop);

//  connect_to_signals();

//  PurpleAccount *account = purple_account_new("YOUR_IM_ACCOUNTS_USERNAME_HERE", "prpl-IM_NETWORK_HERE"); //this could be prpl-aim, prpl-yahoo, prpl-msn, prpl-icq, etc.
//  purple_account_set_password(account, "YOUR_IM_ACCOUNTS_PASSWORD_HERE");
//  purple_accounts_add(account);
//  purple_account_set_enabled(account, UI_ID, TRUE);

  g_main_loop_run (loop);

  return 0;
}
