/* ====================================================================
 * FTD4Linux Software License, Version 1.0
 *
 * Copyright (c) 2000-2004 The FTD4Linux Development Team.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        FTD4Linux Development Team (http://www.ftd4linux.2y.net/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "FTD" and "FTD4Linux" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact alguno@ftd4linux.2y.net.
 *
 * 5. Products derived from this software may not be called "FTD4Linux",
 *    nor may "FTD4Linux" appear in their name, without prior written
 *    permission of the FTD4Linux Development Team.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE FTD4LINUX DEVELOPMENT TEAM OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <stdio.h>
#include "ftd4linux.h"
#include "ftd_app_core.h"
#include "ftd_config.h"
#include "ftd_file.h"
#include "ftd_events.h"
#include "ftd_groups.h"
#include "ftd_filter.h"
#include "ftd_event.h"

#ifdef HAVE_DBUS
#include <dbus/dbus-glib.h>

static gboolean ftd_app_core_dbus_init(const char *frontend_name, gboolean is_gui, char **errmsg);
static void ftd_app_core_dbus_free(void);
#endif

FtdAppCore *core = NULL;

/**
 * Initialiseert de core.
 * Alle benodigde onderdelen van de FTD core worden geladen als deze functie aangeroepen wordt. Vergeet niet argc en argv vanuit de main van je programma door te geven.
 *
 * @arg     data_dir        De map waar de OpenFTD databestanden in opgeslagen staan
 * @arg     cache_dir       De map waar configuratie en cache bestanden in opgeslagen kunnen worden. Indien deze NULL is zal de home-map gebruikt worden
 * @arg     frontend_name   De naam van de frontend die van de core gebruik wilt maken
 * @arg     is_gui          Geeft aan of de core langdurig gebruik gaat worden of maar tijdelijk
 * @arg     errmsg          Pointer voor een eventueel foutbericht (wordt ingesteld indien de return value FALSE is)
 * @arg     dbus_success    Geeft aan of de dbus initialisatie succesvol voltooid is
 *
 * @return  True indien de initialisatie succesvol was.
 */
gboolean
ftd_app_core_init(const char *data_dir, const char *cache_dir, const char *lib_dir, const char *frontend_name, gboolean is_gui, char **errmsg, gboolean *dbus_success)
{
    gboolean is_already_active;

    g_assert(core == NULL);

    // Initialiseer de multi-threaded onderdelen
    if (!g_thread_supported ()) g_thread_init (NULL);

    // Initialiseer GObject
    g_type_init();

    core = g_malloc0(sizeof(FtdAppCore));

#ifdef HAVE_DBUS
    *dbus_success = ftd_dbus_initialize(is_gui, frontend_name, &is_already_active, errmsg);
    if (is_already_active) {
        g_print("OpenFTD is reeds actief\n");
        g_free(core);
        exit(1);
    }
#endif

    ftd_file_set_cache_dir(cache_dir);
    ftd_file_set_data_dir(data_dir);
    ftd_file_set_lib_dir(lib_dir);

    ftd_config_load();

#ifdef HAVE_SQLITE
    core->eventinfo_cache = ftd_event_info_cache_new();
#endif

    core->filterlist = ftd_filterlist_new();
    ftd_filterlist_load(core->filterlist);

    ftd_categories_init();
    ftd_tags_init();
    ftd_group_load();
    ftd_mail_load();

    core->eventlist = ftd_eventlist_new();
    ftd_eventlist_load(core->eventlist, NULL);

    return TRUE;
}

/**
 * Ruimt de volledige core op.
 * Roep deze functie aan het einde van je programma aan.
 */
void
ftd_app_core_cleanup(void)
{
    if (!core) {
        return;
    }

#ifdef HAVE_DBUS
    ftd_dbus_cleanup();
#endif

#ifdef HAVE_SQLITE
    ftd_event_info_cache_destroy(core->eventinfo_cache);
#endif

    ftd_config_save();  // Nodig voor watchlist items op te slaan

    ftd_config_free();
    ftd_tags_free();
    ftd_categories_free();
    ftd_filterlist_destroy(core->filterlist);
    ftd_eventlist_destroy(core->eventlist);
    ftd_file_free_cache_dir();
    ftd_file_free_data_dir();

    ftd_group_free_all();

    g_free(core);
    core = NULL;
}

/**
 * Geeft aan of dit de eerste keer is dat FTD4linux gestart wordt.
 *
 * @return  True indien dit de eerste keer is.
 */
gboolean
ftd_app_core_is_first_time(void)
{
    g_assert(core);

    return core->config->firstTime;
}

/**
 * Geeft een vertaling van ::FtdErrCode naar een leesbare foutmelding.
 *
 * @arg     errcode     De error-code.
 * @arg     errmsg      Een ontvangen error-bericht bij de error-code.
 *
 * @return  Een leesbare foutmelding.
 */
gchar *
ftd_error_msg(int errcode, const char *errmsg)
{
    gchar *msg;

    switch (errcode) {
        case ERROR_NONE:
            msg = g_strdup("Geen fout opgetreden");
            break;
        case ERROR_UNKNOWN:
            msg = g_strdup("Onbekende fout opgetreden");
            break;
        case ERROR_UNAUTH:
            msg = g_strdup("Ongeldige username/wachtwoord opgegeven");
            break;
        case ERROR_BAN:
            msg = g_strdup("Je bent verbannen van de FTD server");
            break;
        case ERROR_DB:
            msg = g_strdup("Database-storing op de FTD server");
            break;
        case ERROR_NEW:
            msg = g_strdup("Geen nieuwe FTD postings sinds laatste update");
            break;
        case ERROR_PLUGIN_UNSUPPORTED:
            msg = g_strdup("Plugins worden niet ondersteund op dit platform");
            break;
        case ERROR_PLUGIN_LOCATION:
            msg = g_strdup("Locatie van http auth plugin onbekend");
            break;
        case ERROR_PLUGIN_LOAD:
            msg = g_strdup("Laden van http auth plugin mislukt");
            break;
        case ERROR_PLUGIN_EXECUTE:
            msg = g_strdup("Uitvoeren van http auth plugin mislukt");
            break;
        case ERROR_RES:
            msg = g_strdup("Onbekende uitvoer van FTD server ontvangen");
            break;
        case ERROR_PARSE:
            msg = g_strdup("Er is een fout opgetreden tijdens het parsen van de ontvangen gegevens");
            break;
        case ERROR_ORIGEVENT:
            msg = g_strdup("Kan originele FTD event niet vinden in de lokale cache");
            break;
        case ERROR_NOT_ON_SERVER:
            msg = g_strdup("Gegevens van de opgevraagde FTD posting zijn niet meer beschikbaar op de FTD server");
            break;
        case ERROR_NODATA:
            msg = g_strdup("Geen data van de FTD server ontvangen");
            break;
        case ERROR_DNS:
            msg = g_strdup("Kan www.ftd.nu niet resolven");
            break;
        case ERROR_DNS_PROXY:
            msg = g_strdup("Kan dns naam van de proxy server niet resolven");
            break;
        case ERROR_CONNECT:
            msg = g_strdup("Connection refused");
            break;
        case ERROR_READ:
            msg = g_strdup("Fout tijdens het lezen van data van de FTD server");
            break;
        case ERROR_SEND:
            msg = g_strdup("Fout tijdens het versturen van data naar de FTD server");
            break;
        case ERROR_TRANSFER_TIMEOUT:
            msg = g_strdup("Timeout opgetreden in de communicatie tussen OpenFTD en de FTD server");
            break;
        case ERROR_SIGNUP_E1:
            msg = g_strdup("Er is reeds iemand geregistreerd met de opgegeven gebruikersnaam");
            break;
        case ERROR_SIGNUP_E2:
            msg = g_strdup("Het huidige IP adres is verbannen van de FTD server");
            break;
        case ERROR_SIGNUP_E3:
            msg = g_strdup("Er is in de laatste 48 uur iemand ingelogd met een geldig FTD account vanaf je huidige IP adres");
            break;
        case ERROR_UPDATE:
            msg = g_strdup("Er is een nieuwe versie van FTD beschikbaar. Gelieve te updaten");
            break;
        case ERROR_UPDATE_LIMIT:
            msg = g_strdup("Er is in de afgelopen 10 minuten reeds een update uitgevoerd");
            break;
        case ERROR_MSG:
            msg = g_strdup_printf("Servermededeling ontvangen: %s", errmsg);
            break;
        default:
            msg = g_strdup("Onbekende errcode!\n");
            break;
    }

    return msg;
}

/**
 * Geeft een verwijzing terug naar de totale eventlist.
 * Deze verwijzing dient na gebruik weer vrijgegeven te worden
 * met g_object_unref()
 *
 * @return  Een verwijzing naar het eventlist object
 */
FTDEventlist *
ftd_eventlist_get(void)
{
    g_object_ref(core->eventlist);
    return core->eventlist;
}

/**
 * Geeft een verwijzing terug naar de totale lijst met filters
 * Deze verwijzing dient na gebruik weer vrijgegeven te worden
 * met g_object_unref()
 *
 * @return  Een verwijzing naar het filterlist object
 */
FTDFilterlist *
ftd_filterlist_get(void)
{
    g_object_ref(core->filterlist);
    return core->filterlist;
}


#ifdef HAVE_DBUS
struct _ftd_dbus_settings {
    const char *frontend_name;
    gboolean is_gui;
};

static struct _ftd_dbus_settings ftd_dbus_settings;
static DBusGConnection *connection = NULL;
static DBusGProxy *proxy = NULL;

static void
bring_to_front_signal_handler(GObject *obj, gpointer data)
{
    g_print("bring_to_front_signal_handler\n");
}

static void
open_posting_signal_handler(GObject *obj, int postID, gpointer data)
{
    g_print("open_posting_signal_handler, postID = %i\n", postID);
}

static gboolean
ftd_app_core_dbus_init(const char *frontend_name, gboolean is_gui, char **errmsg)
{
    GError *error;
    char **name_list;
    char **name_list_ptr;

    memset(&ftd_dbus_settings, 0, sizeof(ftd_dbus_settings));

    g_assert(connection == NULL);
    g_assert(proxy == NULL);

    g_assert(frontend_name);

    error = NULL;
    connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
    if (connection == NULL)
    {
        if (errmsg) {
            *errmsg = g_strdup_printf("Maken van verbinding met DBus mislukt:\n%s\n", error->message);
        }
        g_error_free (error);

        return FALSE;
    }

    /* Create a proxy object for the "bus driver" (name "org.freedesktop.DBus") */
    proxy = dbus_g_proxy_new_for_name (connection,
                                       "org.FTD4Linux.FTD4Linux",
                                       "/org/FTD4Linux/FTD4Linux/object",
                                       "org.FTD4Linux.FTD4Linux");

    g_assert(proxy);

    dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, G_TYPE_NONE, G_TYPE_INVALID);
    dbus_g_proxy_add_signal (proxy, "bring_to_front", G_TYPE_NONE, G_TYPE_INVALID);

    dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__INT, G_TYPE_NONE, G_TYPE_INT, G_TYPE_INVALID);
    dbus_g_proxy_add_signal (proxy, "open_posting", G_TYPE_INT, G_TYPE_INVALID);

    dbus_g_proxy_connect_signal (proxy, "bring_to_front", G_CALLBACK (bring_to_front_signal_handler), NULL, NULL);
    dbus_g_proxy_connect_signal (proxy, "open_posting", G_CALLBACK (open_posting_signal_handler), NULL, NULL);

    error = NULL;
    if (!dbus_g_proxy_call (proxy, "openPosting", &error, G_TYPE_INT, 123, G_TYPE_INVALID, G_TYPE_INVALID)) {
        /* Just do demonstrate remote exceptions versus regular GError */
        if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION)
            g_printerr ("Caught remote method exception %s: %s", dbus_g_error_get_name (error), error->message);
        else
            g_printerr ("Error: %s\n", error->message);

        g_error_free (error);
        exit (1);
    }

    return TRUE;
}

static void
ftd_app_core_dbus_free(void)
{
    g_assert(proxy);

    g_object_unref (proxy);
    proxy = NULL;
}
#endif
