/* ====================================================================
 * 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 "ftd_gui_main.h"
#include "ftd_gui_worksheet.h"
#include "ftd_gui_center.h"
#include "ftd_gui_parse.h"

#include "ftd_irc.h"

#ifdef HAVE_LIBSEXY
#include <libsexy/sexy-spell-entry.h>
#endif

static FTD_IRCObject *irc_data = NULL;

static void on_dblclick_nickname(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data);
static void send_message_func(GtkWidget *caller, gpointer data);
static void scrollbar_changed_func(GtkAdjustment *caller, gpointer data);
static gint sort_users_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data);
static void button_connect_clicked(GtkButton *caller, gpointer data);
static void state_changed_cb (FTD_IRCObject *irc_data, FtdIRCState old_state, const char *errmsg, gpointer data);
static void msg_received_cb (FTD_IRCObject *irc_data, const char *source, const char *channel_name, const char *msg, gboolean is_action, gboolean is_privmsg, gpointer data);
static void join_cb (FTD_IRCObject *irc_data, const char *channel_name, const FtdList *users, const char *topic, gpointer data);
static void user_join_part_cb (FTD_IRCObject *irc_data, const char *username, const char *channel, const char *reason, gboolean has_joined);
static void mode_change_cb (FTD_IRCObject *irc_data, const char *change_performed_by, const char *username, char *mode);
static void nick_change_cb (FTD_IRCObject *irc_data, const char *old_nickname, const char *new_nickname);

struct _channel
{
    GtkWidget *toggleButton;
    const char *name;
    const char *subject;
    const char *contents;
    FtdList *users;
};

static FtdList *channels = NULL;
static struct _channel *active_channel = NULL;

void
ftd_gui_center_irc_new(void)
{
    FtdGuiCenterIRC *irc;
    GtkWidget *hbox1;
    GtkWidget *vbox2;
    GtkWidget *scrolledwindow2;
    GtkWidget *treeview1;
    GtkWidget *hbox2;
    GtkWidget *hbox4;
    GtkWidget *togglebutton1;
    GtkWidget *togglebutton2;
    GtkWidget *togglebutton3;
    GtkWidget *hbuttonbox1;
    GtkWidget *alignment1;
    GtkWidget *hbox5;
    GtkWidget *image1;
    GtkWidget *label2;
    GtkCellRenderer *renderer;
    GtkTreeViewColumn *column;
    GtkAdjustment *adjustment;

    g_assert(gui);
    g_assert(gui->worksheet);
    g_assert(gui->worksheet->center);

    irc = (FtdGuiCenterIRC *) g_malloc0(sizeof(FtdGuiCenterIRC));
    gui->worksheet->center->irc = irc;

    /* Initialiseer de inhoud van het schermpie */
    irc->widget = gtk_vbox_new (FALSE, 0);
    gtk_widget_show (irc->widget);

    irc->lblSubject = gtk_label_new ("");
    gtk_widget_show (irc->lblSubject);
    gtk_box_pack_start (GTK_BOX (irc->widget), irc->lblSubject, FALSE, FALSE, 6);

    hbox1 = gtk_hbox_new (FALSE, 0);
    gtk_widget_show (hbox1);
    gtk_box_pack_start (GTK_BOX (irc->widget), hbox1, TRUE, TRUE, 0);

    vbox2 = gtk_vbox_new (FALSE, 6);
    gtk_widget_show (vbox2);
    gtk_box_pack_start (GTK_BOX (hbox1), vbox2, TRUE, TRUE, 0);
    gtk_container_set_border_width (GTK_CONTAINER (vbox2), 6);

    scrolledwindow2 = gtk_scrolled_window_new (NULL, NULL);
    gtk_widget_show (scrolledwindow2);
    gtk_box_pack_start (GTK_BOX (vbox2), scrolledwindow2, TRUE, TRUE, 0);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_SHADOW_IN);

    // Voeg een extra stukje code toe om de scrollbar automatisch mee te laten
    // bewegen indien er nieuwe tekst binnenkomt
    adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW(scrolledwindow2));
    g_signal_connect(G_OBJECT(adjustment), "changed", G_CALLBACK(scrollbar_changed_func), NULL);
    g_signal_connect(G_OBJECT(adjustment), "value-changed", G_CALLBACK(scrollbar_changed_func), NULL);

    irc->txtChat = gtk_text_view_new ();
    gtk_widget_show (irc->txtChat);
    gtk_container_add (GTK_CONTAINER (scrolledwindow2), irc->txtChat);
    gtk_text_view_set_editable (GTK_TEXT_VIEW (irc->txtChat), FALSE);
    gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (irc->txtChat), GTK_WRAP_WORD);
    gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (irc->txtChat), FALSE);

#ifdef HAVE_LIBSEXY
    irc->entryChat = sexy_spell_entry_new();
    sexy_spell_entry_activate_language(SEXY_SPELL_ENTRY(irc->entryChat), "nl", NULL);
#else
    irc->entryChat = gtk_entry_new ();
#endif
    gtk_widget_show (irc->entryChat);
    gtk_box_pack_start (GTK_BOX (vbox2), irc->entryChat, FALSE, FALSE, 0);
    gtk_entry_set_max_length (GTK_ENTRY (irc->entryChat), 512);

    irc->scrolledwindowUsers = gtk_scrolled_window_new (NULL, NULL);
    gtk_widget_show (irc->scrolledwindowUsers);
    gtk_box_pack_start (GTK_BOX (hbox1), irc->scrolledwindowUsers, FALSE, TRUE, 0);
    //gtk_widget_set_size_request (scrolledwindow1, 332, -1);
    gtk_container_set_border_width (GTK_CONTAINER (irc->scrolledwindowUsers), 6);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (irc->scrolledwindowUsers), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (irc->scrolledwindowUsers), GTK_SHADOW_IN);

    irc->storeUsers = gtk_list_store_new(1, G_TYPE_STRING);

    gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(irc->storeUsers), sort_users_func, NULL, NULL);
    gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(irc->storeUsers), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);

    irc->treeviewUsers = gtk_tree_view_new_with_model(GTK_TREE_MODEL(irc->storeUsers));
    gtk_widget_show (irc->treeviewUsers);
    gtk_container_add (GTK_CONTAINER (irc->scrolledwindowUsers), irc->treeviewUsers);
    GTK_WIDGET_UNSET_FLAGS (irc->treeviewUsers, GTK_CAN_FOCUS);
    g_signal_connect(irc->treeviewUsers, "row-activated", G_CALLBACK(on_dblclick_nickname), irc);

    renderer = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes ("Gebruikers", renderer, "text", 0, NULL);
    gtk_tree_view_column_set_min_width (column, 100);
    gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
    gtk_tree_view_column_set_resizable (column, FALSE);
    gtk_tree_view_append_column (GTK_TREE_VIEW (irc->treeviewUsers), column);

    hbox2 = gtk_hbox_new (FALSE, 0);
    gtk_widget_show (hbox2);
    gtk_box_pack_start (GTK_BOX (irc->widget), hbox2, FALSE, TRUE, 0);
    gtk_container_set_border_width (GTK_CONTAINER (hbox2), 6);

    irc->hboxChannels = gtk_hbox_new (FALSE, 6);
    gtk_widget_show (irc->hboxChannels);
    gtk_box_pack_start (GTK_BOX (hbox2), irc->hboxChannels, TRUE, TRUE, 0);

    hbuttonbox1 = gtk_hbutton_box_new ();
    gtk_widget_show (hbuttonbox1);
    gtk_box_pack_start (GTK_BOX (hbox2), hbuttonbox1, FALSE, FALSE, 0);
    gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_END);

    irc->btnConnect = gtk_button_new ();
    gtk_widget_show (irc->btnConnect);
    gtk_container_add (GTK_CONTAINER (hbuttonbox1), irc->btnConnect);
    GTK_WIDGET_SET_FLAGS (irc->btnConnect, GTK_CAN_DEFAULT);
    g_signal_connect(G_OBJECT(irc->btnConnect), "clicked", G_CALLBACK(button_connect_clicked), NULL);

    alignment1 = gtk_alignment_new (0.5, 0.5, 0, 0);
    gtk_widget_show (alignment1);
    gtk_container_add (GTK_CONTAINER (irc->btnConnect), alignment1);

    hbox5 = gtk_hbox_new (FALSE, 2);
    gtk_widget_show (hbox5);
    gtk_container_add (GTK_CONTAINER (alignment1), hbox5);

    irc->imgConnect = gtk_image_new_from_stock ("gtk-connect", GTK_ICON_SIZE_BUTTON);
    gtk_widget_show (irc->imgConnect);
    gtk_box_pack_start (GTK_BOX (hbox5), irc->imgConnect, FALSE, FALSE, 0);

    irc->lblConnect = gtk_label_new_with_mnemonic ("Verbinden met IRC Server");
    gtk_widget_show (irc->lblConnect);
    gtk_box_pack_start (GTK_BOX (hbox5), irc->lblConnect, FALSE, FALSE, 0);

    g_signal_connect(G_OBJECT(irc->entryChat), "activate", G_CALLBACK(send_message_func), NULL);

    irc_data = ftd_irc_object_new();
}

static void
send_message_func(GtkWidget *caller, gpointer data)
{
    GtkTextBuffer *buffer;
    GtkTextIter iter;
    const char *msg = gtk_entry_get_text(GTK_ENTRY(caller));
    char *total_msg;
    FtdGuiCenterIRC *irc;
    gboolean is_action;
    gboolean is_join;
    char *tmp;

    g_assert(gui);
    g_assert(gui->worksheet);
    g_assert(gui->worksheet->center);
    g_assert(gui->worksheet->center->irc);

    if (!active_channel) {
        return;
    }

    irc = gui->worksheet->center->irc;

    if (!irc_data && !ftd_irc_object_get_active_nickname(irc_data)) {
        return;
    }

    is_join = !strncmp(msg, "/join", 5);

    if (is_join) {
        char *channel = (char *) msg + 5;
        ftd_irc_object_join(irc_data, channel);

        gtk_entry_set_text(GTK_ENTRY(caller), "");

        return;
    }

    is_action = !strncmp(msg, "/me", 3);
    ftd_irc_object_send_msg(irc_data, active_channel->name, (is_action) ? msg + 4 : msg, is_action);

    if (is_action) {
        total_msg = g_strdup_printf("* %s %s\n", ftd_irc_object_get_active_nickname(irc_data), msg + 4);
    } else {
        total_msg = g_strdup_printf("<%s> %s\n", ftd_irc_object_get_active_nickname(irc_data), msg);
    }
    buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(irc->txtChat));
    gtk_text_buffer_get_end_iter(buffer, &iter);
    gtk_text_buffer_insert(buffer, &iter, total_msg, -1);

    tmp = (char *) active_channel->contents;
    active_channel->contents = g_strdup_printf("%s%s", tmp, total_msg);
    g_free(tmp);

    g_free(total_msg);

    gtk_entry_set_text(GTK_ENTRY(caller), "");
}

static void
scrollbar_changed_func(GtkAdjustment *caller, gpointer data)
{
    static gdouble prev_upper = -1;

    gdouble value, upper, page_size;

    g_object_get(G_OBJECT(caller), "value", &value,
                                   "upper", &upper,
                                   "page_size", &page_size,
                                   NULL);

    if (prev_upper == -1) {
        prev_upper = upper;
    }

    if (upper > prev_upper) {
        // Er is meer tekst op het scherm verschenen dan past
        // Scroll alleen automatisch mee indien we reeds helemaal onderaan stonden
        if (value + page_size == prev_upper) {
            gtk_adjustment_set_value(caller, upper - page_size);
        }
    }

    prev_upper = upper;
}

static gint
sort_users_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
{
    char *user1, *user2;

    gtk_tree_model_get (model, a, 0, &user1, -1);
    gtk_tree_model_get (model, b, 0, &user2, -1);

    if (user1 == NULL) {
        return -1;
    }

    if (user2 == NULL) {
        return 1;
    }

    // Zorg ervoor dat mensen met operator status (*) hoger staan de de mensen met voice (+)
    if (user1[0] == '@' && user2[0] == '+') {
        return -1;
    }
    if (user1[0] == '+' && user2[0] == '@') {
        return 1;
    }

    return g_strcasecmp(user1, user2);
}

static void
button_connect_clicked(GtkButton *caller, gpointer data)
{
    FtdGuiCenterIRC *irc;

    g_assert(gui);
    g_assert(gui->worksheet);
    g_assert(gui->worksheet->center);
    g_assert(gui->worksheet->center->irc);

    irc = gui->worksheet->center->irc;

    switch (ftd_irc_object_get_state(irc_data)) {
        case IRC_STATE_DISCONNECTED:
            ftd_irc_object_destroy(irc_data);
            irc_data = ftd_irc_object_new();

            if (ftd_config_get()->ircNickname && ftd_string_length(ftd_config_get()->ircNickname) > 0) {
                ftd_irc_object_set_nickname(irc_data, ftd_string_value(ftd_config_get()->ircNickname));
            } else {
                ftd_irc_object_set_nickname(irc_data, ftd_string_value(ftd_config_get()->username));
            }

            ftd_irc_object_set_alt_nickname(irc_data, ftd_string_value(ftd_config_get()->ircAltNickname));

            ftd_irc_object_set_state_changed_cb(irc_data, state_changed_cb);
            ftd_irc_object_set_msg_received_cb(irc_data, msg_received_cb);
            ftd_irc_object_set_join_cb(irc_data, join_cb);
            ftd_irc_object_set_user_join_part_cb(irc_data, user_join_part_cb);
            ftd_irc_object_set_mode_change_cb(irc_data, mode_change_cb);
            ftd_irc_object_set_nick_change_cb(irc_data, nick_change_cb);

            ftd_irc_object_connect(irc_data);
            break;

        case IRC_STATE_CONNECTED:
            ftd_irc_object_disconnect(irc_data);
            break;
    }
}

static void
on_switch_channel(GtkWidget *caller, gpointer data)
{
    struct _channel *channel = (struct _channel *) data;
    FtdList *list;
    FtdGuiCenterIRC *irc;
    GtkTextBuffer *buffer;
    static gboolean leave_me_alone = FALSE;

    g_assert(gui);
    g_assert(gui->worksheet);
    g_assert(gui->worksheet->center);
    g_assert(gui->worksheet->center->irc);

    irc = gui->worksheet->center->irc;

    if (leave_me_alone) {
        return;
    }

    leave_me_alone = TRUE;

    // Zet alle knopjes UIT behalve degene waar de gebruiker net op geklikt heeft
    list = channels;
    while (list) {
        struct _channel *channel = ftd_list_value(list);

        if (channel->toggleButton != caller) {
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(channel->toggleButton), FALSE);
        }

        list = ftd_list_next(list);
    }

    // Zet ons knopje (indien nodig) terug aan
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(caller), TRUE);

    while (gtk_events_pending ()) {
        gtk_main_iteration ();
    }

    leave_me_alone = FALSE;

    // We moeten de gegevens van een ander channel tonen
    active_channel = channel;

    // Bij het eerste kanaal dat we joinen is de buffer standaard leeg
    if (channel->contents) {
#if 0
        static GtkTextTag *tag = NULL;
        GtkTextIter start, end;
        char *begin_url = (char *) channel->contents;
#endif

        // Toon de buffer MET opmaak, dat houdt in zowel klikbare links als kleurtjes
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(irc->txtChat));
        gtk_text_buffer_set_text(buffer, channel->contents, -1);

        // FIXME: het tonen van kleurtjes en URL's moet nog goed geimplementeerd worden
#if 0
        if (!tag) {
            tag = gtk_text_buffer_create_tag (buffer, "url", "foreground", "blue", NULL);
        }

        begin_url = strstr(begin_url, "[url]");
        while (begin_url) {
            char *eind_url;

            gtk_text_buffer_get_iter_at_offset (buffer, &start, begin_url - channel->contents);

            eind_url = strstr(begin_url, "[/url]");
            if (!eind_url) {
                // Geen sluit-tag gevonden, negeren
                break;
            }

            gtk_text_buffer_get_iter_at_offset (buffer, &end, eind_url - channel->contents);
            gtk_text_buffer_apply_tag (buffer, tag, &start, &end);

            begin_url = strstr(begin_url + 1, "[url]");
        }
#endif
    }

    gtk_widget_modify_bg(channel->toggleButton, GTK_STATE_NORMAL, NULL);

    if (channel->users) {
        gtk_widget_show(irc->scrolledwindowUsers);
    } else {
        gtk_widget_hide(irc->scrolledwindowUsers);
    }

    list = channel->users;
    gtk_list_store_clear(irc->storeUsers);
    gtk_label_set_text(GTK_LABEL(irc->lblSubject), channel->subject);

    while (list) {
        char *nickname = ftd_list_value(list);

        GtkTreeIter iter;
        gtk_list_store_append(irc->storeUsers, &iter);
        gtk_list_store_set(irc->storeUsers, &iter, 0, nickname, -1);

        list = ftd_list_next(list);
    }

    gtk_widget_grab_focus(irc->entryChat);
}

static void
on_dblclick_nickname(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
{
    // De gebruiker wilt een persoonlijk gesprek beginnen met iemand
    GtkTreeIter iter;
    char *nickname = NULL;
    char *nickname2;
    struct _channel *channel;
    FtdGuiCenterIRC *irc;
    GtkTreeModel *model;

    g_assert(gui);
    g_assert(gui->worksheet);
    g_assert(gui->worksheet->center);
    g_assert(gui->worksheet->center->irc);

    irc = gui->worksheet->center->irc;

    model = gtk_tree_view_get_model(tree_view);
    g_return_if_fail(gtk_tree_model_get_iter(model, &iter, path));

    gtk_tree_model_get(model, &iter, 0, &nickname, -1);

    if (nickname[0] == '@' || nickname[0] == '+') {
        nickname2 = nickname + 1;
    } else {
        nickname2 = nickname;
    }

    channel = g_malloc0(sizeof(struct _channel));
    channels = ftd_list_append(channels, channel);

    channel->name = g_strdup(nickname2);
    channel->contents = g_strdup("");
    channel->toggleButton = gtk_toggle_button_new_with_label (nickname2);
    gtk_widget_show (channel->toggleButton);
    gtk_box_pack_start (GTK_BOX (irc->hboxChannels), channel->toggleButton, FALSE, FALSE, 0);

    g_signal_connect(G_OBJECT(channel->toggleButton), "toggled", G_CALLBACK(on_switch_channel), channel);

    // Grijp gelijk de focus
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(channel->toggleButton), TRUE);

    g_free(nickname);
}

static void
state_changed_cb (FTD_IRCObject *irc_data, FtdIRCState old_state, const char *errmsg, gpointer data)
{
    FtdGuiCenterIRC *irc;
    GtkTextBuffer *buffer;
    GtkTextIter iter;
    char *total_msg;
    FtdList *list;

    g_assert(gui);
    g_assert(gui->worksheet);
    g_assert(gui->worksheet->center);
    g_assert(gui->worksheet->center->irc);

    irc = gui->worksheet->center->irc;

    buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(irc->txtChat));
    gtk_text_buffer_get_end_iter(buffer, &iter);

    switch (ftd_irc_object_get_state(irc_data)) {
        case IRC_STATE_DISCONNECTED:
            gtk_widget_set_sensitive(irc->btnConnect, TRUE);
            gtk_label_set_text(GTK_LABEL(irc->lblConnect), "Verbinden met IRC Server");
            gtk_image_set_from_stock(GTK_IMAGE(irc->imgConnect), "gtk-connect", GTK_ICON_SIZE_BUTTON);
            if (errmsg) {
                total_msg = g_strdup_printf("\n*** Verbinding verbroken met IRC Server: %s\n\n", errmsg);
                gtk_text_buffer_insert(buffer, &iter, total_msg, -1);
                g_free(total_msg);
            } else {
                gtk_text_buffer_insert(buffer, &iter, "\n*** Verbinding verbroken met IRC Server\n\n", -1);
            }

            gtk_list_store_clear(irc->storeUsers);

            list = channels;
            while (list) {
                FtdList *list2;
                struct _channel *channel = ftd_list_value(list);

                g_free((gpointer) channel->name);
                g_free((gpointer) channel->subject);
                g_free((gpointer) channel->contents);

                gtk_widget_destroy(channel->toggleButton);

                list2 = channel->users;
                while (list2) {
                    g_free(ftd_list_value(list2));
                    list2 = ftd_list_next(list2);
                }

                list = ftd_list_next(list);
            }

            active_channel = NULL;

            break;

        case IRC_STATE_CONNECTING:
            gtk_widget_set_sensitive(irc->btnConnect, FALSE);
            gtk_text_buffer_insert(buffer, &iter, "*** Bezig met verbinden...\n", -1);
            break;

        case IRC_STATE_CONNECTED:
            gtk_widget_set_sensitive(irc->btnConnect, TRUE);
            gtk_label_set_text(GTK_LABEL(irc->lblConnect), "Verbinding verbreken");
            gtk_image_set_from_stock(GTK_IMAGE(irc->imgConnect), "gtk-disconnect", GTK_ICON_SIZE_BUTTON);
            gtk_text_buffer_insert(buffer, &iter, "*** Verbonden met IRC Server!\n\n", -1);
            break;
    }
}

static void
msg_received_cb (FTD_IRCObject *irc_data, const char *source, const char *channel_name, const char *msg, gboolean is_action, gboolean is_privmsg, gpointer data)
{
    GtkTextBuffer *buffer;
    GtkTextIter iter;
    FtdGuiCenterIRC *irc;
    char *total_msg;
    FtdList *list;
#if 0
    FtdString *orig_msg;
    FtdString *new_msg;
#endif

    g_assert(gui);
    g_assert(gui->worksheet);
    g_assert(gui->worksheet->center);
    g_assert(gui->worksheet->center->irc);
    g_return_if_fail(active_channel);

    irc = gui->worksheet->center->irc;

    // FIXME: eventueel url's omdopen tot [url]http://www.blaat.nl[/url]

    if (is_action) {
        if (is_privmsg) {
            total_msg = g_strdup_printf("* %s %s\n", channel_name, msg);
        } else {
            total_msg = g_strdup_printf("* %s %s\n", source, msg);
        }
    } else {
        if (is_privmsg) {
            total_msg = g_strdup_printf("<%s> %s\n", channel_name, msg);
        } else {
            total_msg = g_strdup_printf("<%s> %s\n", source, msg);
        }
    }

    // Het tonen van url's is nog broken op dit moment. Moet een betere
    // implementatie van geschreven worden.
#if 0
    orig_msg = ftd_string_new(total_msg);
    g_free(total_msg);

    // Voer eerst een smerige hack uit om een oneindige loop in de regexp_replace functie te voorkomen
    new_msg = ftd_string_replace(orig_msg, "http://", "httP://");
    ftd_string_free(orig_msg);
    orig_msg = new_msg;
    new_msg = ftd_gui_parse_regexp_replace(orig_msg, "(.*)httP://(.*)([ \n].*)", "%s[url]http://%s[/url]%s", FALSE);
    ftd_string_free(orig_msg);
#endif

    if (!strcmp(channel_name, active_channel->name)) {
        char *tmp;

        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(irc->txtChat));
        gtk_text_buffer_get_end_iter(buffer, &iter);
        gtk_text_buffer_insert(buffer, &iter, total_msg, -1);

        tmp = (char *) active_channel->contents;
        active_channel->contents = g_strdup_printf("%s%s", tmp, total_msg);
        g_free(tmp);

        g_free(total_msg);

        return;
    } else {
        list = channels;
        while (list) {
            struct _channel *channel = ftd_list_value(list);

            if (!strcmp(channel_name, channel->name)) {
                char *tmp = (char *) channel->contents;
                GdkColor rood = { 0, 65535, 0, 0 };

                gtk_widget_modify_bg(channel->toggleButton, GTK_STATE_NORMAL, &rood);

                channel->contents = g_strdup_printf("%s%s", tmp, total_msg);
                g_free(tmp);

                g_free(total_msg);

                return;
            }

            list = ftd_list_next(list);
        }

        if (is_privmsg) {
            // Maak een nieuwe channel aan voor pm's
            GdkColor rood = { 0, 65535, 0, 0 };
            struct _channel *channel = g_malloc0(sizeof(struct _channel));
            channels = ftd_list_append(channels, channel);

            channel->name = g_strdup(channel_name);
            channel->contents = g_strdup(total_msg);
            channel->toggleButton = gtk_toggle_button_new_with_label (channel_name);
            gtk_widget_show (channel->toggleButton);
            gtk_box_pack_start (GTK_BOX (irc->hboxChannels), channel->toggleButton, FALSE, FALSE, 0);

            g_signal_connect(G_OBJECT(channel->toggleButton), "toggled", G_CALLBACK(on_switch_channel), channel);

            gtk_widget_modify_bg(channel->toggleButton, GTK_STATE_NORMAL, &rood);
        } else {
            // Huh? we hebben een bericht ontvangen uit een channel waar we niet in gejoined zijn...
            g_print("Bericht ontvangen van '%s' met de inhoud '%s', maar we zijn niet in dit kanaal gejoined\n", source, msg);
        }

        g_free(total_msg);
    }
}

static void
join_cb (FTD_IRCObject *irc_data, const char *channel_name, const FtdList *users, const char *topic, gpointer data)
{
    FtdList *list;
    FtdGuiCenterIRC *irc;

    g_assert(gui);
    g_assert(gui->worksheet);
    g_assert(gui->worksheet->center);
    g_assert(gui->worksheet->center->irc);
    g_return_if_fail(active_channel);

    irc = gui->worksheet->center->irc;

    gtk_widget_show(irc->scrolledwindowUsers);

    list = channels;
    while (list) {
        struct _channel *channel = ftd_list_value(list);

        if (!strcmp(channel->name, channel_name)) {
            FtdList *list2 = (FtdList *) users;

            channel->subject = g_strdup(topic);

            if (channel == active_channel) {
                gtk_list_store_clear(irc->storeUsers);
                gtk_label_set_text(GTK_LABEL(irc->lblSubject), topic);
            }

            while (list2) {
                char *nickname = ftd_list_value(list2);

                if (channel == active_channel) {
                    GtkTreeIter iter;

                    gtk_list_store_append(irc->storeUsers, &iter);
                    gtk_list_store_set(irc->storeUsers, &iter, 0, nickname, -1);
                }

                channel->users = ftd_list_append(channel->users, g_strdup(nickname));

                list2 = ftd_list_next(list2);
            }
        }

        list = ftd_list_next(list);
    }
}

static void
user_join_part_cb (FTD_IRCObject *irc_data, const char *username, const char *channel_name, const char *reason, gboolean has_joined)
{
    GtkTextBuffer *buffer;
    GtkTextIter iter;
    FtdGuiCenterIRC *irc;
    char *total_msg;
    char *tmp;
    FtdList *list;
    struct _channel *channel = NULL;

    g_assert(gui);
    g_assert(gui->worksheet);
    g_assert(gui->worksheet->center);
    g_assert(gui->worksheet->center->irc);

    irc = gui->worksheet->center->irc;

    list = channels;
    while (list) {
        channel = ftd_list_value(list);

        if (!strcmp(channel->name, channel_name)) {
            break;
        }

        channel = NULL;

        list = ftd_list_next(list);
    }

    if (!channel) {
        channel = g_malloc0(sizeof(struct _channel));
        channels = ftd_list_append(channels, channel);

        channel->name = g_strdup(channel_name);
        channel->toggleButton = gtk_toggle_button_new_with_label (channel_name);
        gtk_widget_show (channel->toggleButton);
        gtk_box_pack_start (GTK_BOX (irc->hboxChannels), channel->toggleButton, FALSE, FALSE, 0);

        g_signal_connect(G_OBJECT(channel->toggleButton), "toggled", G_CALLBACK(on_switch_channel), channel);

        if (!active_channel) {
            active_channel = channel;
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(channel->toggleButton), TRUE);
        }
    }

    g_return_if_fail(channel);

    if (has_joined) {
        total_msg = g_strdup_printf("*** %s is binnengekomen in %s\n", username, channel_name);
    } else {
        if (reason) {
            total_msg = g_strdup_printf("*** %s heeft %s verlaten (%s)\n", username, channel_name, reason);
        } else {
            total_msg = g_strdup_printf("*** %s heeft %s verlaten\n", username, channel_name);
        }
    }

    if (!strcmp(active_channel->name, channel_name)) {
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(irc->txtChat));
        gtk_text_buffer_get_end_iter(buffer, &iter);
        gtk_text_buffer_insert(buffer, &iter, total_msg, -1);
    }

    if (channel->contents) {
        tmp = (char *) channel->contents;
        channel->contents = g_strdup_printf("%s%s", tmp, total_msg);
        g_free(tmp);
    } else {
        channel->contents = g_strdup(total_msg);
    }

    g_free(total_msg);

    if (has_joined) {
        // Zijn we deze gebruiker niet al eens eerder tegengekomen ?
        FtdList *list;
        list = channel->users;

        while (list) {
            char *nickname = ftd_list_value(list);

            if (!strcmp(nickname, username)) {
                if (!strcmp(active_channel->name, channel_name)) {
                    GtkTreeIter iter;

                    gtk_list_store_append(irc->storeUsers, &iter);
                    gtk_list_store_set(irc->storeUsers, &iter, 0, username, -1);
                }

                channel->users = ftd_list_append(channel->users, g_strdup(username));

                break;
            }

            list = ftd_list_next(list);
        }
    } else {
        if (!strcmp(active_channel->name, channel_name)) {
            GtkTreeIter iter;
            char *user = NULL;

            if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(irc->storeUsers), &iter)) {
                return;
            }

            do {
                gtk_tree_model_get(GTK_TREE_MODEL(irc->storeUsers), &iter, 0, &user, -1);
                if (!strcmp(user, username) || !strcmp(user + 1, username)) {
                    gtk_list_store_remove(GTK_LIST_STORE(irc->storeUsers), &iter);
                    g_free(user);
                    return;
                }
                g_free(user);
            } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(irc->storeUsers), &iter));
        }

        list = channel->users;
        while (list) {
            char *nickname = ftd_list_value(list);

            if (!strcmp(nickname, username)) {
                channel->users = ftd_list_remove(channel->users, nickname);
                break;
            }

            list = ftd_list_next(list);
        }
   }
}

static void
mode_change_cb (FTD_IRCObject *irc_data, const char *change_performed_by, const char *username, char *mode)
{
    GtkTreeIter iter;
    char *user = NULL;
    GtkTextBuffer *buffer;
    GtkTextIter text_iter;
    FtdGuiCenterIRC *irc;
    char *total_msg;

    g_assert(gui);
    g_assert(gui->worksheet);
    g_assert(gui->worksheet->center);
    g_assert(gui->worksheet->center->irc);

    irc = gui->worksheet->center->irc;

    if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(irc->storeUsers), &iter)) {
        return;
    }

    do {
        gtk_tree_model_get(GTK_TREE_MODEL(irc->storeUsers), &iter, 0, &user, -1);

        if (!strcmp(user, username) || !strcmp(user + 1, username)) {
            g_free(user);
            user = NULL;
            total_msg = NULL;

            // We hebben de juiste user gevonden in de lijst. Pas de modus nu aan
            if (!strcmp(mode, "+o")) {
                user = g_strdup_printf("@%s", username);
                total_msg = g_strdup_printf("*** %s geeft kanaal-operator status aan %s\n", change_performed_by, username);
            }

            if (!strcmp(mode, "-o")) {
                user = g_strdup(username);
                total_msg = g_strdup_printf("*** %s verwijderd kanaal-operator status van %s\n", change_performed_by, username);
            }

            if (!strcmp(mode, "+v")) {
                user = g_strdup_printf("+%s", username);
                total_msg = g_strdup_printf("*** %s geeft voice status aan %s\n", change_performed_by, username);
            }

            if (!strcmp(mode, "-v")) {
                user = g_strdup(username);
                total_msg = g_strdup_printf("*** %s verwijderd voice status van %s\n", change_performed_by, username);
            }

            if (user) {
                gtk_list_store_set(GTK_LIST_STORE(irc->storeUsers), &iter, 0, user, -1);
                g_free(user);

                buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(irc->txtChat));
                gtk_text_buffer_get_end_iter(buffer, &text_iter);
                gtk_text_buffer_insert(buffer, &text_iter, total_msg, -1);
                g_free(total_msg);
            }

            return;
        }

        g_free(user);
    } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(irc->storeUsers), &iter));
}

static void
nick_change_cb (FTD_IRCObject *irc_data, const char *old_nickname, const char *new_nickname)
{
    GtkTextBuffer *buffer;
    GtkTextIter text_iter;
    FtdGuiCenterIRC *irc;
    char *total_msg;
    GtkTreeIter iter;

    g_assert(gui);
    g_assert(gui->worksheet);
    g_assert(gui->worksheet->center);
    g_assert(gui->worksheet->center->irc);

    irc = gui->worksheet->center->irc;

    total_msg = g_strdup_printf("*** %s is nu bekend als %s\n", old_nickname, new_nickname);

    buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(irc->txtChat));
    gtk_text_buffer_get_end_iter(buffer, &text_iter);
    gtk_text_buffer_insert(buffer, &text_iter, total_msg, -1);

    g_free(total_msg);

    if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(irc->storeUsers), &iter)) {
        return;
    }

    do {
        char *user = NULL;

        gtk_tree_model_get(GTK_TREE_MODEL(irc->storeUsers), &iter, 0, &user, -1);

        if (!strcmp(user, old_nickname) || !strcmp(user + 1, old_nickname)) {
            // Bingo! pas de naam nu aan, maar behoud wel een eventuele status vlag
            char vlag = '\0';
            char *nick;

            if (user[0] == '@' || user[0] == '+') {
                nick = g_strdup_printf("%c%s", user[0], new_nickname);
            } else {
                nick = g_strdup(new_nickname);
            }

            gtk_list_store_set(irc->storeUsers, &iter, 0, nick, -1);

            g_free(user);
            g_free(nick);

            return;
        }

        g_free(user);
    } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(irc->storeUsers), &iter));
}
