
/*
 * Osmo - a handy personal organizer
 *
 * Copyright (C) 2007 Tomasz Maka <pasp@users.sourceforge.net>
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "contacts_export.h"
#include "i18n.h"
#include "utils.h"
#include "utils_date.h"
#include "utils_gui.h"
#include "contacts.h"
#include "calendar_utils.h"
#include "stock_icons.h"
#include "options_prefs.h"
#include "vcf.h"

#ifdef CONTACTS_ENABLED

static gint export_contacts_to_file(gchar *filename, gboolean header, GUI *appGUI);
/*-------------------------------------------------------------------------------------*/
static gboolean
is_export_column(gint column) {
    if(config.export_fields[column] == '+') {
        return TRUE;
    }
    return FALSE;
}
/*-------------------------------------------------------------------------------------*/

static void
export_check (GUI *appGUI) {

gint i, k;
const gchar *text;

    if (appGUI->cnt->output_file_entry != NULL && appGUI->cnt->export_button != NULL) {
        k = 0;

        for(i=0; i < CONTACTS_NUM_COLUMNS; i++) {
            if(is_export_column(i)) k++;
        }

        text = gtk_entry_get_text (GTK_ENTRY(appGUI->cnt->output_file_entry));

        if (text == NULL) {
            gtk_widget_set_sensitive(appGUI->cnt->export_button, FALSE);
        } else if (strlen(text) && k) {
            gtk_widget_set_sensitive(appGUI->cnt->export_button, TRUE);
        } else {
            gtk_widget_set_sensitive(appGUI->cnt->export_button, FALSE);
        }
    }
}

/*-------------------------------------------------------------------------------------*/

static void
export_window_close_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data) {

    GUI *appGUI = (GUI *)user_data;

    gtk_window_get_size (GTK_WINDOW(appGUI->cnt->export_window),
                        &config.contacts_export_win_w, &config.contacts_export_win_h);
    gdk_window_get_root_origin (gtk_widget_get_window(appGUI->cnt->export_window),
                                &config.contacts_export_win_x, &config.contacts_export_win_y);

    gtk_widget_destroy(appGUI->cnt->export_window);
    appGUI->cnt->output_file_entry = NULL;
    appGUI->cnt->export_button = NULL;
}

/*-------------------------------------------------------------------------------------*/

static void
export_cb(GtkWidget *widget, gpointer data) {

    gint n;
    gboolean header;
    gchar tmpbuf[BUFFER_SIZE];

    GUI *appGUI = (GUI *)data;

    if (strlen(gtk_entry_get_text (GTK_ENTRY(appGUI->cnt->output_file_entry)))) {

        header = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(appGUI->cnt->first_row_header_check_button));

        n = export_contacts_to_file((gchar *) gtk_entry_get_text (GTK_ENTRY(appGUI->cnt->output_file_entry)), header, appGUI);

        if (n != -1) {

            g_snprintf(tmpbuf, BUFFER_SIZE, "%s %d %s.\n", _("Done!"), n,
                ngettext ("contact exported", "contacts exported", n));

            utl_gui_create_dialog (GTK_MESSAGE_INFO, tmpbuf, GTK_WINDOW(appGUI->cnt->export_window));
            export_window_close_cb (widget, NULL, appGUI);

        }
    }
}

/*------------------------------------------------------------------------------*/

static void
button_export_window_close_cb (GtkWidget *widget, gpointer data) {

    GUI *appGUI = (GUI *)data;

    export_window_close_cb (widget, NULL, appGUI);

}
/*------------------------------------------------------------------------------*/
struct contact_export_strategy {
    void (*begin_file)(GUI * appGUI, export_writer *writer, void *writer_user_data);
    void (*begin_contact)(gint number, GUI * appGUI, export_writer *writer, void *writer_user_data);
    void (*handle_contact_value)(gint column, gchar const * value, GtkTreeIter *iter, GUI * appGUI, export_writer *writer, void *writer_user_data);
    void (*end_contact)(gint number, GUI * appGUI, export_writer *writer, void *writer_user_data);
    void (*end_file)(GUI * appGUI, export_writer *writer, void *writer_user_data);
};

/*------------------------------------------------------------------------------*/

static void
xhtml_begin_file(GUI * appGUI, export_writer *writer, void *writer_user_data) {
    gchar tmpbuf[BUFFER_SIZE];
    gint i, a, b;
    writer(writer_user_data, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
    writer(writer_user_data, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
    writer(writer_user_data, "<head>\n");
    writer(writer_user_data, "\t<title>Contact List</title>\n");
    writer(writer_user_data, "\t<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n");
    writer(writer_user_data, "\t<meta name=\"generator\" content=\"OSMO - http://clay.ll.pl/osmo\" />\n");
    writer(writer_user_data, "\t<style type=\"text/css\">\n");
    writer(writer_user_data, "\t\tbody { color:black; background-color:white; }\n");
    writer(writer_user_data, "\t\ta { color:#0000ff; }\n");
    writer(writer_user_data, "\t\ta:visited { color:#000055; }\n");
    writer(writer_user_data, "\t\ttable { border-collapse:collapse; }\n");
    writer(writer_user_data, "\t\ttr.header { background-color:#c0c0c0; }\n");
    writer(writer_user_data, "\t\ttr.evenrow { background-color:#f0f0f0; }\n");
    writer(writer_user_data, "\t\ttr.oddrow { background-color:#fafafa; }\n");
    writer(writer_user_data, "\t\tth, td { border:1px solid #555555; padding:3px; }\n");
    writer(writer_user_data, "\t</style>\n");
    writer(writer_user_data, "</head>\n\n");
    writer(writer_user_data, "<body>\n\n");
    writer(writer_user_data, "<h1>Contact List</h1>\n\n");

    writer(writer_user_data, "<table>\n");

    writer(writer_user_data, "<tr class=\"header\">\n");

    for (i = 0; i < CONTACTS_NUM_COLUMNS; i++) {
        if (is_export_column(i)) {
            writer(writer_user_data, "\t<th>");

            if (appGUI->cnt->contact_fields_tags_name[i * 2] != NULL) {
                for (a = b = 0; a < strlen(appGUI->cnt->contact_fields_tags_name[i * 2]); a++) {
                    if (appGUI->cnt->contact_fields_tags_name[i * 2][a] == ' ') {
                        tmpbuf[b] = '\0';
                        g_strlcat(tmpbuf, "&nbsp;", BUFFER_SIZE);
                        b += 6;
                    } else {
                        tmpbuf[b++] = appGUI->cnt->contact_fields_tags_name[i * 2][a];
                    }
                }
                tmpbuf[b] = '\0';
                writer(writer_user_data, "%s", tmpbuf);
            }

            writer(writer_user_data, "</th>\n");
        }
    }

    writer(writer_user_data, "</tr>\n\n");
}

static void
xhtml_begin_contact(gint number, GUI * appGUI, export_writer *writer, void *writer_user_data) {
    if (number & 1) {
        writer(writer_user_data, "<tr class=\"evenrow\">\n");
    } else {
        writer(writer_user_data, "<tr class=\"oddrow\">\n");
    }
}

static void
xhtml_end_contact(gint number, GUI * appGUI, export_writer *writer, void *writer_user_data) {
    writer(writer_user_data, "</tr>\n\n");
}

static void
xhtml_handle_contact_value(gint column, gchar const * value, GtkTreeIter *iter, GUI * appGUI, export_writer *writer, void *writer_user_data) {
    gint a, b;
    gchar tmpbuf[BUFFER_SIZE];

    writer(writer_user_data, "\t<td>");

    if (value != NULL) {

        for (a = b = 0; a < strlen(value); a++) {
            if (value[a] == ' ') {
                tmpbuf[b] = '\0';
                g_strlcat(tmpbuf, "&nbsp;", BUFFER_SIZE);
                b += 6;
            } else {
                tmpbuf[b++] = value[a];
            }
        }
        tmpbuf[b] = '\0';

        switch (column) {
            case COLUMN_EMAIL_1:
            case COLUMN_EMAIL_2:
            case COLUMN_EMAIL_3:
            case COLUMN_EMAIL_4:
                writer(writer_user_data, "<a href=\"mailto:%s\">%s</a>", tmpbuf, tmpbuf);
                break;
            case COLUMN_WWW_1:
            case COLUMN_WWW_2:
            case COLUMN_WWW_3:
            case COLUMN_WWW_4:
            case COLUMN_BLOG:
                writer(writer_user_data, "<a href=\"%s\">%s</a>", tmpbuf, tmpbuf);
                break;
            default:
                writer(writer_user_data, "%s", tmpbuf);
        }
    }

    writer(writer_user_data, "</td>\n");
}

static void
xhtml_end_file(GUI * appGUI, export_writer *writer, void *writer_user_data) {
    writer(writer_user_data, "</table>\n");
    writer(writer_user_data, "</body>\n");
    writer(writer_user_data, "</html>\n");
}

/*------------------------------------------------------------------------------*/

static void
csv_begin_file(GUI * appGUI, export_writer *writer, void *writer_user_data) {
}

static void
csv_no_header_begin_contact(gint number, GUI * appGUI, export_writer *writer, void *writer_user_data) {
}

static gboolean csv_add_coma;

static void
csv_header_begin_contact(gint number, GUI * appGUI, export_writer *writer, void *writer_user_data) {
    if (number == 0) {
        gint i;
        gboolean add_coma = FALSE;

        for (i = 0; i < CONTACTS_NUM_COLUMNS; i++) {
            if (is_export_column(i)) {
                if (add_coma) {
                    writer(writer_user_data, ",");
                }
                add_coma = TRUE;
                writer(writer_user_data, "%s", appGUI->cnt->contact_fields_tags_name[i * 2]);
            }
        }
        writer(writer_user_data, "\r\n");
    }
    csv_add_coma = FALSE;
}

static void
csv_end_contact(gint number, GUI * appGUI, export_writer *writer, void *writer_user_data) {
    writer(writer_user_data, "\r\n");
}

static void
csv_handle_contact_value(gint column, gchar const * value, GtkTreeIter *iter, GUI * appGUI, export_writer *writer, void *writer_user_data) {
    if (csv_add_coma) {
        writer(writer_user_data, ",");
    }
    csv_add_coma = TRUE;
    if (value != NULL) {
        gint i;
        gboolean must_quote, leading_space, trailing_space;
        gsize len;
        len = strlen(value);
        leading_space = TRUE;
        trailing_space = FALSE;
        for (i = 0, must_quote = FALSE; i < len; i++) {
            if (value[i] == '"' || value[i] == ',' || value[i] == '\n') {
                must_quote = TRUE;
                break;
            } else if (value[i] == ' ' || value[i] == '\t') {
                trailing_space = TRUE;
            } else {
                trailing_space = FALSE;
                if (i == 0) {
                    leading_space = FALSE;
                } else if (leading_space) {
                    must_quote = TRUE;
                    break;
                }
            }
        }
        if (!must_quote) {
            /* do not trim whitespaces, RFC 4180 */
            must_quote = (leading_space || trailing_space) ? TRUE : FALSE;
        }

        if (must_quote) {
            gchar *buffer = g_new(gchar, len * 2 + 1);
            gint j;
            for (i = 0, j = 0; i < len; i++) {
                if (value[i] == '"') {
                    buffer[j++] = '"';
                } else if (value[i] == '\n') {
                    buffer[j++] = '\r';
                }
                buffer[j++] = value[i];
            }
            buffer[j] = '\0';
            writer(writer_user_data, "\"%s\"", buffer);
            g_free(buffer);
        } else {
            writer(writer_user_data, "%s", value);
        }
    }
}

static void
csv_end_file(GUI * appGUI, export_writer *writer, void *writer_user_data) {

}

/*------------------------------------------------------------------------------*/
static vcf_writer *vcf;

static struct {
    export_writer *writer;
    void *data;
} vcf_writer_data;

static void
vcf_write_cb(gchar const *buffer, gsize buffer_size, void *user_data) {
    export_writer *writer = vcf_writer_data.writer;
    void *data = vcf_writer_data.data;
    gchar *tmp = g_strndup(buffer, buffer_size); /* NULL terminated for writer */

    writer(data, tmp);
    g_free(tmp);
}

static void
vcf_begin_file(GUI * appGUI, export_writer *writer, void *writer_user_data) {
    vcf_writer_data.writer = writer;
    vcf_writer_data.data = writer_user_data;
    vcf = vcf_create_writer(vcf_write_cb, NULL);
}

static gboolean vcf_write_required_properties;
static gboolean vcf_write_home_address;
static gboolean vcf_write_work_address;
static gboolean vcf_write_org;

static void
vcf_begin_contact(gint number, GUI * appGUI, export_writer *writer, void *writer_user_data) {
    vcf_write_begin(vcf);
    vcf_write_required_properties = TRUE;
    vcf_write_home_address = TRUE;
    vcf_write_work_address = TRUE;
    vcf_write_org = TRUE;
}

static void
vcf_export_address(gchar const* type, gint address_column, gint city_column, gint state_column, gint post_code_column, gint country_column,
        GtkTreeIter *iter, GUI * appGUI) {
    gchar *address, *city, *state, *post_code, *country;
    gtk_tree_model_get(GTK_TREE_MODEL(appGUI->cnt->contacts_list_store), iter,
            address_column, &address,
            city_column, &city,
            state_column, &state,
            post_code_column, &post_code,
            country_column, &country,
            -1);
    vcf_write_ADR(vcf, type,
            is_export_column(address_column) ? address : NULL,
            is_export_column(city_column) ? city : NULL,
            is_export_column(state_column) ? state : NULL,
            is_export_column(post_code_column) ? post_code : NULL,
            is_export_column(country_column) ? country : NULL
            );

    g_free(address);
    g_free(city);
    g_free(state);
    g_free(post_code);
    g_free(country);
}

static void
vcf_handle_contact_value(gint column, gchar const * value, GtkTreeIter *iter, GUI * appGUI, export_writer *writer, void *writer_user_data) {
    if (vcf_write_required_properties == TRUE) {
        gchar *first_name, *last_name, *second_name;
        gtk_tree_model_get(GTK_TREE_MODEL(appGUI->cnt->contacts_list_store), iter,
                COLUMN_FIRST_NAME, &first_name,
                COLUMN_LAST_NAME, &last_name,
                COLUMN_SECOND_NAME, &second_name,
                -1);
        vcf_write_FN(vcf, first_name, last_name);
        vcf_write_N(vcf, last_name, first_name, second_name);
        vcf_write_required_properties = FALSE;
        g_free(first_name);
        g_free(last_name);
        g_free(second_name);
        vcf_write_required_properties = FALSE;
    }
    if (value) {
        switch (column) {
            case COLUMN_NICK_NAME:
                vcf_write_NICKNAME(vcf, value);
                break;
            case COLUMN_BIRTH_DAY_DATE:
            {
                guint32 julian_day;
                gtk_tree_model_get(GTK_TREE_MODEL(appGUI->cnt->contacts_list_store), iter,
                        COLUMN_BIRTH_DAY_DATE, &julian_day,
                        -1);
                vcf_write_BDAY(vcf, julian_day);
                break;
            }
            case COLUMN_HOME_ADDRESS:
            case COLUMN_HOME_CITY:
            case COLUMN_HOME_STATE:
            case COLUMN_HOME_POST_CODE:
            case COLUMN_HOME_COUNTRY:
                if (vcf_write_home_address) {
                    vcf_export_address(VCF_TYPE_HOME, COLUMN_HOME_ADDRESS, COLUMN_HOME_CITY, COLUMN_HOME_STATE, COLUMN_HOME_POST_CODE, COLUMN_HOME_COUNTRY, iter, appGUI);
                    vcf_write_home_address = FALSE;
                }
                break;
            case COLUMN_WORK_ADDRESS:
            case COLUMN_WORK_CITY:
            case COLUMN_WORK_STATE:
            case COLUMN_WORK_POST_CODE:
            case COLUMN_WORK_COUNTRY:
                if (vcf_write_work_address) {
                    vcf_export_address(VCF_TYPE_WORK, COLUMN_WORK_ADDRESS, COLUMN_WORK_CITY, COLUMN_WORK_STATE, COLUMN_WORK_POST_CODE, COLUMN_WORK_COUNTRY, iter, appGUI);
                    vcf_write_work_address = FALSE;
                }
                break;
            case COLUMN_WORK_ORGANIZATION:
            case COLUMN_WORK_DEPARTMENT:
                if (vcf_write_org) {
                    gchar *organization, *department;
                    gtk_tree_model_get(GTK_TREE_MODEL(appGUI->cnt->contacts_list_store), iter,
                            COLUMN_WORK_ORGANIZATION, &organization,
                            COLUMN_WORK_DEPARTMENT, &department,
                            -1);
                    vcf_write_ORG(vcf,
                            is_export_column(COLUMN_WORK_ORGANIZATION) ? organization : NULL,
                            is_export_column(COLUMN_WORK_DEPARTMENT) ? department : NULL);

                    g_free(organization);
                    g_free(department);
                    vcf_write_org = FALSE;
                }
                break;
            case COLUMN_WORK_FAX:
                vcf_write_TEL(vcf, value, 1, VCF_TYPE_TEL_FAX, VCF_TYPE_WORK);
                break;
            case COLUMN_WORK_PHONE_1:
            case COLUMN_WORK_PHONE_2:
            case COLUMN_WORK_PHONE_3:
            case COLUMN_WORK_PHONE_4:
            {
                gint pref = column - COLUMN_WORK_PHONE_1 + 1;
                vcf_write_TEL(vcf, value, pref, VCF_TYPE_TEL_VOICE, VCF_TYPE_WORK);
                break;
            }
            case COLUMN_HOME_PHONE_1:
            case COLUMN_HOME_PHONE_2:
            case COLUMN_HOME_PHONE_3:
            case COLUMN_HOME_PHONE_4:
            {
                gint pref = column - COLUMN_HOME_PHONE_1 + 1;
                vcf_write_TEL(vcf, value, pref, VCF_TYPE_TEL_VOICE, VCF_TYPE_HOME);
                break;
            }
            case COLUMN_CELL_PHONE_1:
            case COLUMN_CELL_PHONE_2:
            case COLUMN_CELL_PHONE_3:
            case COLUMN_CELL_PHONE_4:
            {
                gint pref = column - COLUMN_CELL_PHONE_1 + 1;
                vcf_write_TEL(vcf, value, pref, VCF_TYPE_TEL_CELL);
                break;
            }
            case COLUMN_EMAIL_1:
            case COLUMN_EMAIL_2:
            case COLUMN_EMAIL_3:
            case COLUMN_EMAIL_4:
            {
                gint pref = column - COLUMN_EMAIL_1 + 1;
                vcf_write_EMAIL(vcf, value, pref);
                break;
            }
            case COLUMN_WWW_1:
            case COLUMN_WWW_2:
            case COLUMN_WWW_3:
            case COLUMN_WWW_4:
            {
                gint pref = column - COLUMN_WWW_1 + 1;
                vcf_write_URL_pref(vcf, value, pref);
                break;
            }
            case COLUMN_IM_AOL:
                vcf_write_IMPP(vcf, value, "aol");
                break;
            case COLUMN_IM_GG:
                vcf_write_IMPP(vcf, value, "gg");
                break;
            case COLUMN_IM_ICQ:
                vcf_write_IMPP(vcf, value, "icq");
                break;
            case COLUMN_IM_JABBER:
                vcf_write_IMPP(vcf, value, "xmpp");
                break;
            case COLUMN_IM_MSN:
                vcf_write_IMPP(vcf, value, "msn");
                break;
            case COLUMN_IM_SKYPE:
                vcf_write_IMPP(vcf, value, "skype");
                break;
            case COLUMN_IM_TLEN:
                vcf_write_IMPP(vcf, value, "tlen");
                break;
            case COLUMN_IM_YAHOO:
                vcf_write_IMPP(vcf, value, "ymsgr");
                break;
            case COLUMN_BLOG:
                vcf_write_URL(vcf, value);
                break;
            case COLUMN_INFO:
                vcf_write_NOTE(vcf, value);
                break;
            default:
                break;
        }
    }
}

static void
vcf_end_contact(gint number, GUI * appGUI, export_writer *writer, void *writer_user_data) {
    vcf_write_end(vcf);
}

static void
vcf_end_file(GUI * appGUI, export_writer *writer, void *writer_user_data) {
    vcf_close_writer(vcf);
    vcf = NULL;
    vcf_writer_data.writer = NULL;
    vcf_writer_data.data = NULL;
}

/*------------------------------------------------------------------------------*/
static void
export_to_file(void *user_data, gchar const *format, ...) {
    FILE *filehandle = user_data;
    va_list arguments;

    va_start(arguments, format);
    vfprintf(filehandle, format, arguments);
    va_end(arguments);
}

static struct contact_export_strategy *
choose_strategy(gint type, gboolean header) {
    if (type == EXPORT_TO_XHTML) {
        struct contact_export_strategy *strategy = g_new0(struct contact_export_strategy, 1);
        strategy->begin_file = xhtml_begin_file;
        strategy->begin_contact = xhtml_begin_contact;
        strategy->handle_contact_value = xhtml_handle_contact_value;
        strategy->end_contact = xhtml_end_contact;
        strategy->end_file = xhtml_end_file;
        return strategy;
    } else if (type == EXPORT_TO_CSV) {
        struct contact_export_strategy *strategy = g_new0(struct contact_export_strategy, 1);
        strategy->begin_file = csv_begin_file;
        strategy->begin_contact = header ? csv_header_begin_contact : csv_no_header_begin_contact;
        strategy->handle_contact_value = csv_handle_contact_value;
        strategy->end_contact = csv_end_contact;
        strategy->end_file = csv_end_file;
        return strategy;
    } else if (type == EXPORT_TO_VCF) {
        struct contact_export_strategy *strategy = g_new0(struct contact_export_strategy, 1);
        strategy->begin_file = vcf_begin_file;
        strategy->begin_contact = vcf_begin_contact;
        strategy->handle_contact_value = vcf_handle_contact_value;
        strategy->end_contact = vcf_end_contact;
        strategy->end_file = vcf_end_file;
        return strategy;
    }
    return NULL;
}

gint
export_contacts(gint type, gboolean header, GUI *appGUI, export_writer *writer, void *writer_user_data) {
    GtkTreeIter sort_iter;
    gboolean valid;
    gint i, exported;
    gchar *text;
    guint32 date;
    struct contact_export_strategy *strategy;

    exported = 0;

    strategy = choose_strategy(type, header);

    if (strategy) {
        strategy->begin_file(appGUI, writer, writer_user_data);

        valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(appGUI->cnt->contacts_sort), &sort_iter);
        while (valid) {
            GtkTreeIter filter_iter, iter;
            gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT(appGUI->cnt->contacts_sort), &filter_iter, &sort_iter);
            gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(appGUI->cnt->contacts_filter), &iter, &filter_iter);
            strategy->begin_contact(exported, appGUI, writer, writer_user_data);

            for (i = 0; i < CONTACTS_NUM_COLUMNS; i++) {
                if (is_export_column(i)) {

                    if (i == COLUMN_BIRTH_DAY_DATE || i == COLUMN_NAME_DAY_DATE) {
                        gtk_tree_model_get(GTK_TREE_MODEL(appGUI->cnt->contacts_list_store), &iter, i, &date, -1);
                        if (date == 0) {
                            text = NULL;
                        } else {
                            if (i == COLUMN_BIRTH_DAY_DATE) {
                                text = g_strdup((const gchar *) julian_to_str(date, DATE_DD_MM_YYYY, TRUE));
                            } else {
                                text = g_strdup((const gchar *) julian_to_str(date, DATE_DD_MM_YYYY, TRUE));
                            }
                        }
                    } else {
                        gtk_tree_model_get(GTK_TREE_MODEL(appGUI->cnt->contacts_list_store), &iter, i, &text, -1);
                    }

                    strategy->handle_contact_value(i, text, &iter, appGUI, writer, writer_user_data);
                    g_free(text);
                }
            }
            strategy->end_contact(exported, appGUI, writer, writer_user_data);
            exported++;
            valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(appGUI->cnt->contacts_sort), &sort_iter);
        }

        strategy->end_file(appGUI, writer, writer_user_data);

        g_free(strategy);
        return exported;
    } else {
        utl_gui_create_dialog(GTK_MESSAGE_ERROR, _("Cannot create file."), GTK_WINDOW(appGUI->cnt->export_window));
        return -1;
    }
}

static gint
export_contacts_to_file(gchar *filename, gboolean header, GUI *appGUI) {
    FILE *filehandle;
    if (utl_gui_check_overwrite_file(filename, appGUI->cnt->export_window, appGUI) != 0) {
        return -1;
    }


    filehandle = g_fopen(filename, "w");
    if (filehandle) {
        gint exported = export_contacts(config.export_format, header, appGUI, export_to_file, filehandle);
        fclose(filehandle);
        return exported;
    } else {
        utl_gui_create_dialog(GTK_MESSAGE_ERROR, _("Cannot create file."), GTK_WINDOW(appGUI->cnt->export_window));
        return -1;
    }
}

/*------------------------------------------------------------------------------*/

static void
browse_dir_cb (GtkWidget *widget, gpointer data) {

    GtkWidget *dialog;
    gchar f_filename[PATH_MAX];

        GUI *appGUI = (GUI *)data;

    dialog = utl_gui_create_save_file_dialog(_("Select output file"),
            GTK_WINDOW(appGUI->cnt->export_window));

    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {

            g_strlcpy (f_filename, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)), PATH_MAX-1);

        if (config.export_format == EXPORT_TO_XHTML) {
                if (g_str_has_suffix (f_filename, ".html") == FALSE &&
                    g_str_has_suffix (f_filename, ".HTML") == FALSE) {
                    g_strlcat(f_filename, ".html", PATH_MAX-1);
            }
            } else if(config.export_format == EXPORT_TO_CSV) {
                if (g_str_has_suffix (f_filename, ".csv") == FALSE &&
                    g_str_has_suffix (f_filename, ".CSV") == FALSE) {
                    g_strlcat(f_filename, ".csv", PATH_MAX-1);
            }
            } else if(config.export_format == EXPORT_TO_VCF) {
                if (g_str_has_suffix (f_filename, ".vcf") == FALSE &&
                    g_str_has_suffix (f_filename, ".VCF") == FALSE &&
                    g_str_has_suffix (f_filename, ".vcard") == FALSE &&
                    g_str_has_suffix (f_filename, ".VCARD") == FALSE) {
                    g_strlcat(f_filename, ".vcf", PATH_MAX-1);
            }
        }
            gtk_entry_set_text (GTK_ENTRY(appGUI->cnt->output_file_entry), f_filename);
    }

    export_check(appGUI);
    gtk_widget_destroy(dialog);
}

/*------------------------------------------------------------------------------*/

static gint
export_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data) {

    GUI *appGUI = (GUI *)data;

    switch(event->keyval) {
        case GDK_KEY_Escape:
            export_window_close_cb (widget, NULL, appGUI);
            return TRUE;
    }

    return FALSE;
}

/*------------------------------------------------------------------------------*/

static void
export_field_selected_cb (GtkToggleButton *button, gpointer user_data) {

    gint i;

    MESSAGE *msg = (MESSAGE *)user_data;

    i = (size_t) msg->data;

    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)) == TRUE) {
        config.export_fields[i] = '+';
    } else {
        config.export_fields[i] = '-';
    }

    export_check(msg->appGUI);
}

/*------------------------------------------------------------------------------*/

static void
format_changed_cb(GtkToggleButton *button, gpointer user_data) {
    gint format = GPOINTER_TO_INT(user_data);
    if (gtk_toggle_button_get_active(button)) {
        config.export_format = format;
    }
}

/*------------------------------------------------------------------------------*/

static void
select_action_cb (GtkWidget *widget, gpointer user_data) {

    gint i;
    gboolean state;

    MESSAGE *msg = (MESSAGE *)user_data;

    for(i=0; i < CONTACTS_NUM_COLUMNS; i++) {

        if (i != COLUMN_PHOTO && i != COLUMN_ID) {
                switch((size_t) msg->data) {

                case SELECT_ALL:
                        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(msg->appGUI->cnt->check_buttons[i]), TRUE);
                    break;

                case SELECT_NONE:
                        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(msg->appGUI->cnt->check_buttons[i]), FALSE);
                    break;

                case SELECT_INVERT:
                        state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(msg->appGUI->cnt->check_buttons[i]));
                        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(msg->appGUI->cnt->check_buttons[i]), !state);
                    break;

                default:
                    break;
            };
        }
    }
}

/*--------------------------------------------------------------------*/

static GSList *
create_format_radiobutton(gchar * const label, gint format, GtkBox *parent, GSList *group) {
    GtkWidget* radiobutton;
    radiobutton = gtk_radio_button_new_with_mnemonic(NULL, label);
    gtk_widget_show(radiobutton);
    g_signal_connect(G_OBJECT(radiobutton), "toggled",
            G_CALLBACK(format_changed_cb), GINT_TO_POINTER(format));
    gtk_widget_set_can_focus(radiobutton, FALSE);
    gtk_box_pack_start(GTK_BOX(parent), radiobutton, FALSE, FALSE, 0);
    gtk_radio_button_set_group(GTK_RADIO_BUTTON(radiobutton), group);
    if (config.export_format == format) {
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radiobutton), TRUE);
    }
    return gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobutton));
}
/*--------------------------------------------------------------------*/

void
contacts_create_export_window(GUI *appGUI) {

    GtkWidget *vbox1;
    GtkWidget *hbox1;
    GtkWidget *vbox2;
    GtkWidget *hbox2;
    GtkWidget *vbox3;
    GtkWidget *frame;
    GtkWidget *vbox4;
    GtkWidget *vbox5;
GSList    *format_radiobutton_group = NULL;
    GtkWidget *label;
    GtkWidget *vseparator;
    GtkWidget *scrolledwindow;
    GtkWidget *viewport;
    GtkWidget *fields_table;
    GtkWidget *hbox3;
    GtkWidget *browse_dir_button;
    GtkWidget *hseparator;
    GtkWidget *hbuttonbox;
    GtkWidget *hbuttonbox_s;
    GtkWidget *cancel_button;
    GtkWidget *select_all_button;
    GtkWidget *select_none_button;
    GtkWidget *invert_selection_button;
    gint i;
    gchar tmpbuf[BUFFER_SIZE];
    static MESSAGE msg_export_field[CONTACTS_NUM_COLUMNS];
    static MESSAGE msg_select[3]; /* select all, select none, select invert */

    appGUI->cnt->export_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (appGUI->cnt->export_window), _("Export contacts"));
    gtk_container_set_border_width (GTK_CONTAINER (appGUI->cnt->export_window), 6);

    gtk_window_move (GTK_WINDOW (appGUI->cnt->export_window), 
            config.contacts_export_win_x, config.contacts_export_win_y);
    gtk_window_set_default_size (GTK_WINDOW(appGUI->cnt->export_window), 
            config.contacts_export_win_w, config.contacts_export_win_h);

    if (config.fullscreen == FALSE) {
        gtk_window_set_transient_for(GTK_WINDOW(appGUI->cnt->export_window), GTK_WINDOW(appGUI->main_window));
    }
    gtk_window_set_modal(GTK_WINDOW(appGUI->cnt->export_window), TRUE);

    g_signal_connect (G_OBJECT (appGUI->cnt->export_window), "key_press_event", 
                      G_CALLBACK (export_key_press_cb), appGUI);

    g_signal_connect (G_OBJECT (appGUI->cnt->export_window), "delete_event", 
            G_CALLBACK(export_window_close_cb), appGUI);

    vbox1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_widget_show (vbox1);
    gtk_container_add (GTK_CONTAINER (appGUI->cnt->export_window), vbox1);

    hbox1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_widget_show (hbox1);
    gtk_box_pack_start (GTK_BOX (vbox1), hbox1, TRUE, TRUE, 0);

    vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_widget_show (vbox2);
    gtk_box_pack_start (GTK_BOX (hbox1), vbox2, TRUE, TRUE, 0);

    hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_widget_show (hbox2);
    gtk_box_pack_start (GTK_BOX (vbox2), hbox2, TRUE, TRUE, 0);

    vbox3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_widget_show (vbox3);
    gtk_box_pack_start (GTK_BOX (hbox2), vbox3, FALSE, TRUE, 0);

    frame = gtk_frame_new (NULL);
    gtk_widget_show (frame);
    gtk_box_pack_start (GTK_BOX (vbox3), frame, FALSE, FALSE, 0);
    gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);

    vbox4 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_widget_show (vbox4);
    gtk_widget_set_margin_left(vbox4, 12);
    gtk_container_add (GTK_CONTAINER (frame), vbox4);
    gtk_container_set_border_width (GTK_CONTAINER (vbox4), 8);

    format_radiobutton_group = create_format_radiobutton("CSV", EXPORT_TO_CSV, GTK_BOX(vbox4), format_radiobutton_group);
    format_radiobutton_group = create_format_radiobutton("XHTML", EXPORT_TO_XHTML, GTK_BOX(vbox4), format_radiobutton_group);
    create_format_radiobutton("VCF", EXPORT_TO_VCF, GTK_BOX(vbox4), format_radiobutton_group);

    g_snprintf(tmpbuf, BUFFER_SIZE, "<b>%s:</b>", _("Output format"));
    label = gtk_label_new (tmpbuf);
    gtk_widget_show (label);
    gtk_frame_set_label_widget (GTK_FRAME (frame), label);
    gtk_label_set_use_markup (GTK_LABEL (label), TRUE);

    frame = gtk_frame_new (NULL);
    gtk_widget_show (frame);
    gtk_box_pack_start (GTK_BOX (vbox3), frame, FALSE, FALSE, 0);
    gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);

    vbox4 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_widget_show (vbox4);
    gtk_widget_set_margin_left(vbox4, 12);
    gtk_container_add (GTK_CONTAINER (frame), vbox4);
    gtk_container_set_border_width (GTK_CONTAINER (vbox4), 8);

    g_snprintf(tmpbuf, BUFFER_SIZE, "<b>%s:</b>", _("Options"));
    label = gtk_label_new (tmpbuf);
    gtk_widget_show (label);
    gtk_frame_set_label_widget (GTK_FRAME (frame), label);
    gtk_label_set_use_markup (GTK_LABEL (label), TRUE);

    g_snprintf(tmpbuf, BUFFER_SIZE, "%s", _("Add header"));
    appGUI->cnt->first_row_header_check_button = gtk_check_button_new_with_mnemonic (tmpbuf);
    gtk_widget_show (appGUI->cnt->first_row_header_check_button);
    gtk_box_pack_start (GTK_BOX (vbox4), appGUI->cnt->first_row_header_check_button, TRUE, TRUE, 0);
    gtk_widget_set_can_focus(appGUI->cnt->first_row_header_check_button, FALSE);
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(appGUI->cnt->first_row_header_check_button), TRUE);

    vseparator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
    gtk_widget_show (vseparator);
    gtk_box_pack_start (GTK_BOX (hbox2), vseparator, FALSE, TRUE, 4);

    vbox5 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_widget_show (vbox5);
    gtk_box_pack_start (GTK_BOX (hbox2), vbox5, TRUE, TRUE, 0);
    gtk_container_set_border_width (GTK_CONTAINER (vbox4), 8);

    frame = gtk_frame_new (NULL);
    gtk_widget_show (frame);
    gtk_box_pack_start (GTK_BOX (vbox5), frame, TRUE, TRUE, 0);
    gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);

    scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
    gtk_widget_show (scrolledwindow);
    gtk_widget_set_margin_left(scrolledwindow, 12);
    gtk_container_add (GTK_CONTAINER (frame), scrolledwindow);
    gtk_container_set_border_width (GTK_CONTAINER (scrolledwindow), 8);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

    viewport = gtk_viewport_new (NULL, NULL);
    gtk_widget_show (viewport);
    gtk_container_add (GTK_CONTAINER (scrolledwindow), viewport);

    fields_table = gtk_grid_new ();
    gtk_widget_show (fields_table);
    gtk_container_add (GTK_CONTAINER (viewport), fields_table);

    g_snprintf(tmpbuf, BUFFER_SIZE, "<b>%s:</b>", _("Fields to export"));
    label = gtk_label_new (tmpbuf);
    gtk_widget_show (label);
    gtk_frame_set_label_widget (GTK_FRAME (frame), label);
    gtk_label_set_use_markup (GTK_LABEL (label), TRUE);

    frame = gtk_frame_new (NULL);
    gtk_widget_show (frame);
    gtk_box_pack_start (GTK_BOX (vbox5), frame, FALSE, TRUE, 0);
    gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);

    hbuttonbox_s = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
    gtk_widget_show (hbuttonbox_s);
    gtk_box_pack_start (GTK_BOX (vbox5), hbuttonbox_s, FALSE, TRUE, 0);
    gtk_box_set_spacing (GTK_BOX (hbuttonbox_s), 4);
    gtk_button_box_set_layout(GTK_BUTTON_BOX(hbuttonbox_s), GTK_BUTTONBOX_END);

    select_all_button = gtk_button_new_with_label (_("All"));
    gtk_widget_set_can_focus(select_all_button, FALSE);
    gtk_widget_show (select_all_button);
    gtk_container_add (GTK_CONTAINER (hbuttonbox_s), select_all_button);
    msg_select[0].appGUI = appGUI;
    msg_select[0].data = (gpointer) SELECT_ALL;
    g_signal_connect (G_OBJECT (select_all_button), "clicked",
                        G_CALLBACK (select_action_cb), &msg_select[0]);

    select_none_button = gtk_button_new_with_label (_("None"));
    gtk_widget_set_can_focus(select_none_button, FALSE);
    gtk_widget_show (select_none_button);
    gtk_container_add (GTK_CONTAINER (hbuttonbox_s), select_none_button);
    msg_select[1].appGUI = appGUI;
    msg_select[1].data = (gpointer) SELECT_NONE;
    g_signal_connect (G_OBJECT (select_none_button), "clicked",
                        G_CALLBACK (select_action_cb), &msg_select[1]);

    invert_selection_button = gtk_button_new_with_label (_("Invert"));
    gtk_widget_set_can_focus(invert_selection_button, FALSE);
    gtk_widget_show (select_none_button);
    gtk_widget_show (invert_selection_button);
    gtk_container_add (GTK_CONTAINER (hbuttonbox_s), invert_selection_button);
    msg_select[2].appGUI = appGUI;
    msg_select[2].data = (gpointer) SELECT_INVERT;
    g_signal_connect (G_OBJECT (invert_selection_button), "clicked",
                        G_CALLBACK (select_action_cb), &msg_select[2]);

    g_snprintf(tmpbuf, BUFFER_SIZE, "<b>%s:</b>", _("Select fields"));
    label = gtk_label_new (tmpbuf);
    gtk_widget_show (label);
    gtk_frame_set_label_widget (GTK_FRAME (frame), label);
    gtk_label_set_use_markup (GTK_LABEL (label), TRUE);

    frame = gtk_frame_new (NULL);
    gtk_widget_show (frame);
    gtk_box_pack_start (GTK_BOX (vbox1), frame, FALSE, TRUE, 0);
    gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);

    hbox3 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
    gtk_widget_show (hbox3);
    gtk_widget_set_margin_left(hbox3, 12);
    gtk_container_add (GTK_CONTAINER (frame), hbox3);

    appGUI->cnt->output_file_entry = gtk_entry_new ();
    gtk_widget_show (appGUI->cnt->output_file_entry);
    gtk_box_pack_start (GTK_BOX (hbox3), appGUI->cnt->output_file_entry, TRUE, TRUE, 0);
    gtk_widget_set_can_focus(appGUI->cnt->output_file_entry, FALSE);
    gtk_editable_set_editable (GTK_EDITABLE(appGUI->cnt->output_file_entry), FALSE);

    for(i=0; i < CONTACTS_NUM_COLUMNS; i++) {

        if (i != COLUMN_PHOTO && i != COLUMN_ID) {

            appGUI->cnt->check_buttons[i] = gtk_check_button_new_with_mnemonic (appGUI->cnt->contact_fields_tags_name[i*2]);

            gtk_widget_show (appGUI->cnt->check_buttons[i]);
            msg_export_field[i].appGUI = appGUI;
            msg_export_field[i].data = (gpointer) ((size_t)i);
            g_signal_connect (G_OBJECT (appGUI->cnt->check_buttons[i]), "toggled",
                              G_CALLBACK (export_field_selected_cb), &msg_export_field[i]);
            gtk_widget_set_can_focus(appGUI->cnt->check_buttons[i], FALSE);
            gtk_widget_set_hexpand(appGUI->cnt->check_buttons[i], TRUE);
            gtk_widget_set_vexpand(appGUI->cnt->check_buttons[i], TRUE);
            gtk_grid_attach (GTK_GRID (fields_table), appGUI->cnt->check_buttons[i], 0, i, 1, 1);

            if(is_export_column(i)) {
                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(appGUI->cnt->check_buttons[i]), TRUE);
            }
        }
    }

	browse_dir_button = gtk_button_new_with_mnemonic (_("_Open"));
    gtk_widget_show (browse_dir_button);
    gtk_widget_set_can_focus(browse_dir_button, FALSE);
    g_signal_connect(browse_dir_button, "clicked", G_CALLBACK(browse_dir_cb), appGUI);
    gtk_box_pack_start (GTK_BOX (hbox3), browse_dir_button, FALSE, FALSE, 0);

    g_snprintf(tmpbuf, BUFFER_SIZE, "<b>%s:</b>", _("Output filename"));
    label = gtk_label_new (tmpbuf);
    gtk_widget_show (label);
    gtk_frame_set_label_widget (GTK_FRAME (frame), label);
    gtk_label_set_use_markup (GTK_LABEL (label), TRUE);

    hseparator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
    gtk_widget_show (hseparator);
    gtk_box_pack_start (GTK_BOX (vbox1), hseparator, FALSE, TRUE, 4);

    hbuttonbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
    gtk_widget_show (hbuttonbox);
    gtk_box_pack_start (GTK_BOX (vbox1), hbuttonbox, FALSE, TRUE, 0);
    gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END);
    gtk_box_set_spacing (GTK_BOX (hbuttonbox), 8);

	cancel_button = gtk_button_new_with_mnemonic (_("_Cancel"));
    gtk_widget_show (cancel_button);
    gtk_container_add (GTK_CONTAINER (hbuttonbox), cancel_button);
    gtk_widget_set_can_default (cancel_button, TRUE);
    g_signal_connect(cancel_button, "clicked", G_CALLBACK(button_export_window_close_cb), appGUI);

    appGUI->cnt->export_button = gtk_button_new_with_mnemonic (_("Export"));
    gtk_widget_show (appGUI->cnt->export_button);
    gtk_container_add (GTK_CONTAINER (hbuttonbox), appGUI->cnt->export_button);
    gtk_widget_set_can_default (appGUI->cnt->export_button, TRUE);
    g_signal_connect(appGUI->cnt->export_button, "clicked", G_CALLBACK(export_cb), appGUI);
    gtk_widget_set_sensitive(appGUI->cnt->export_button, FALSE);

    gtk_widget_show(appGUI->cnt->export_window);

}

/*-------------------------------------------------------------------------------------*/

#endif  /* CONTACTS_ENABLED */

