/*
 * This file Copyright (C) 2007-2014 Mnemosyne LLC
 *
 * It may be used under the GNU GPL versions 2 or 3
 * or any future license endorsed by Mnemosyne LLC.
 *
 */

#include <glib/gi18n.h>
#include <gtk/gtk.h>

#include <libtransmission/transmission.h>
#include <libtransmission/makemeta.h>
#include <libtransmission/utils.h> /* tr_formatter_mem_B() */

#include "hig.h"
#include "makemeta-ui.h"
#include "tr-core.h"
#include "tr-prefs.h"
#include "util.h"

#define FILE_CHOSEN_KEY "file-is-chosen"

typedef struct
{
    char* target;
    guint progress_tag;
    GtkWidget* file_radio;
    GtkWidget* file_chooser;
    GtkWidget* folder_radio;
    GtkWidget* folder_chooser;
    GtkWidget* pieces_lb;
    GtkWidget* destination_chooser;
    GtkWidget* comment_check;
    GtkWidget* comment_entry;
    GtkWidget* private_check;
    GtkWidget* progress_label;
    GtkWidget* progress_bar;
    GtkWidget* progress_dialog;
    GtkWidget* dialog;
    GtkTextBuffer* announce_text_buffer;
    TrCore* core;
    tr_metainfo_builder* builder;
}
MakeMetaUI;

static void freeMetaUI(gpointer p)
{
    MakeMetaUI* ui = p;

    tr_metaInfoBuilderFree(ui->builder);
    g_free(ui->target);
    memset(ui, ~0, sizeof(MakeMetaUI));
    g_free(ui);
}

static gboolean onProgressDialogRefresh(gpointer data)
{
    char* str = NULL;
    MakeMetaUI* ui = data;
    tr_metainfo_builder const* b = ui->builder;
    GtkDialog* d = GTK_DIALOG(ui->progress_dialog);
    GtkProgressBar* p = GTK_PROGRESS_BAR(ui->progress_bar);
    double const fraction = b->pieceCount != 0 ? (double)b->pieceIndex / b->pieceCount : 0;
    char* base = g_path_get_basename(b->top);

    /* progress label */
    if (!b->isDone)
    {
        str = g_strdup_printf(_("Creating \"%s\""), base);
    }
    else if (b->result == TR_MAKEMETA_OK)
    {
        str = g_strdup_printf(_("Created \"%s\"!"), base);
    }
    else if (b->result == TR_MAKEMETA_URL)
    {
        str = g_strdup_printf(_("Error: invalid announce URL \"%s\""), b->errfile);
    }
    else if (b->result == TR_MAKEMETA_CANCELLED)
    {
        str = g_strdup_printf(_("Cancelled"));
    }
    else if (b->result == TR_MAKEMETA_IO_READ)
    {
        str = g_strdup_printf(_("Error reading \"%s\": %s"), b->errfile, g_strerror(b->my_errno));
    }
    else if (b->result == TR_MAKEMETA_IO_WRITE)
    {
        str = g_strdup_printf(_("Error writing \"%s\": %s"), b->errfile, g_strerror(b->my_errno));
    }
    else
    {
        g_assert_not_reached();
    }

    if (str != NULL)
    {
        gtr_label_set_text(GTK_LABEL(ui->progress_label), str);
        g_free(str);
    }

    /* progress bar */
    if (b->pieceIndex == 0)
    {
        str = g_strdup("");
    }
    else
    {
        char sizebuf[128];
        tr_strlsize(sizebuf, (uint64_t)b->pieceIndex * (uint64_t)b->pieceSize, sizeof(sizebuf));
        /* how much data we've scanned through to generate checksums */
        str = g_strdup_printf(_("Scanned %s"), sizebuf);
    }

    gtk_progress_bar_set_fraction(p, fraction);
    gtk_progress_bar_set_text(p, str);
    g_free(str);

    /* buttons */
    gtk_dialog_set_response_sensitive(d, GTK_RESPONSE_CANCEL, !b->isDone);
    gtk_dialog_set_response_sensitive(d, GTK_RESPONSE_CLOSE, b->isDone);
    gtk_dialog_set_response_sensitive(d, GTK_RESPONSE_ACCEPT, b->isDone && !b->result);

    g_free(base);
    return G_SOURCE_CONTINUE;
}

static void onProgressDialogDestroyed(gpointer data, GObject* dead UNUSED)
{
    MakeMetaUI* ui = data;
    g_source_remove(ui->progress_tag);
}

static void addTorrent(MakeMetaUI* ui)
{
    char* path;
    tr_metainfo_builder const* b = ui->builder;
    tr_ctor* ctor = tr_ctorNew(gtr_core_session(ui->core));

    tr_ctorSetMetainfoFromFile(ctor, ui->target);

    path = g_path_get_dirname(b->top);
    tr_ctorSetDownloadDir(ctor, TR_FORCE, path);
    g_free(path);

    gtr_core_add_ctor(ui->core, ctor);
}

static void onProgressDialogResponse(GtkDialog* d, int response, gpointer data)
{
    MakeMetaUI* ui = data;

    switch (response)
    {
    case GTK_RESPONSE_CANCEL:
        ui->builder->abortFlag = TRUE;
        gtk_widget_destroy(GTK_WIDGET(d));
        break;

    case GTK_RESPONSE_ACCEPT:
        addTorrent(ui);

    /* fall-through */

    case GTK_RESPONSE_CLOSE:
        gtk_widget_destroy(ui->builder->result ? GTK_WIDGET(d) : ui->dialog);
        break;

    default:
        g_assert(0 && "unhandled response");
    }
}

static void makeProgressDialog(GtkWidget* parent, MakeMetaUI* ui)
{
    GtkWidget* d;
    GtkWidget* l;
    GtkWidget* w;
    GtkWidget* v;
    GtkWidget* fr;

    d = gtk_dialog_new_with_buttons(_("New Torrent"), GTK_WINDOW(parent), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, NULL);
    ui->progress_dialog = d;
    g_signal_connect(d, "response", G_CALLBACK(onProgressDialogResponse), ui);

    fr = gtk_frame_new(NULL);
    gtk_container_set_border_width(GTK_CONTAINER(fr), GUI_PAD_BIG);
    gtk_frame_set_shadow_type(GTK_FRAME(fr), GTK_SHADOW_NONE);
    v = gtk_box_new(GTK_ORIENTATION_VERTICAL, GUI_PAD);
    gtk_container_add(GTK_CONTAINER(fr), v);

    l = gtk_label_new(_("Creating torrent…"));
    g_object_set(l, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
    gtk_label_set_justify(GTK_LABEL(l), GTK_JUSTIFY_LEFT);
    ui->progress_label = l;
    gtk_box_pack_start(GTK_BOX(v), l, FALSE, FALSE, 0);

    w = gtk_progress_bar_new();
    ui->progress_bar = w;
    gtk_box_pack_start(GTK_BOX(v), w, FALSE, FALSE, 0);

    ui->progress_tag = gdk_threads_add_timeout_seconds(SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS, onProgressDialogRefresh, ui);
    g_object_weak_ref(G_OBJECT(d), onProgressDialogDestroyed, ui);
    onProgressDialogRefresh(ui);

    gtr_dialog_set_content(GTK_DIALOG(d), fr);
    gtk_widget_show(d);
}

static void onResponse(GtkDialog* d, int response, gpointer user_data)
{
    MakeMetaUI* ui = user_data;

    if (response == GTK_RESPONSE_ACCEPT)
    {
        if (ui->builder != NULL)
        {
            int n;
            int tier;
            GtkTextIter start;
            GtkTextIter end;
            char* dir;
            char* base;
            char* tracker_text;
            char** tracker_strings;
            GtkEntry* c_entry = GTK_ENTRY(ui->comment_entry);
            GtkToggleButton* p_check = GTK_TOGGLE_BUTTON(ui->private_check);
            GtkToggleButton* c_check = GTK_TOGGLE_BUTTON(ui->comment_check);
            char const* comment = gtk_entry_get_text(c_entry);
            gboolean const isPrivate = gtk_toggle_button_get_active(p_check);
            gboolean const useComment = gtk_toggle_button_get_active(c_check);
            tr_tracker_info* trackers;

            /* destination file */
            dir = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(ui->destination_chooser));
            base = g_path_get_basename(ui->builder->top);
            g_free(ui->target);
            ui->target = g_strdup_printf("%s/%s.torrent", dir, base);

            /* build the array of trackers */
            gtk_text_buffer_get_bounds(ui->announce_text_buffer, &start, &end);
            tracker_text = gtk_text_buffer_get_text(ui->announce_text_buffer, &start, &end, FALSE);
            tracker_strings = g_strsplit(tracker_text, "\n", 0);

            trackers = g_new0(tr_tracker_info, g_strv_length(tracker_strings));
            n = 0;
            tier = 0;

            for (int i = 0; tracker_strings[i] != NULL; ++i)
            {
                char* const str = tracker_strings[i];

                if (tr_str_is_empty(str))
                {
                    ++tier;
                }
                else
                {
                    trackers[n].tier = tier;
                    trackers[n].announce = str;
                    ++n;
                }
            }

            /* build the .torrent */
            makeProgressDialog(GTK_WIDGET(d), ui);
            tr_makeMetaInfo(ui->builder, ui->target, trackers, n, useComment ? comment : NULL, isPrivate);

            /* cleanup */
            g_free(trackers);
            g_strfreev(tracker_strings);
            g_free(tracker_text);
            g_free(base);
            g_free(dir);
        }
    }
    else if (response == GTK_RESPONSE_CLOSE)
    {
        gtk_widget_destroy(GTK_WIDGET(d));
    }
}

/***
****
***/

static void onSourceToggled(GtkToggleButton* tb, gpointer user_data)
{
    gtk_widget_set_sensitive(GTK_WIDGET(user_data), gtk_toggle_button_get_active(tb));
}

static void updatePiecesLabel(MakeMetaUI* ui)
{
    tr_metainfo_builder const* builder = ui->builder;
    char const* filename = builder != NULL ? builder->top : NULL;
    GString* gstr = g_string_new(NULL);

    g_string_append(gstr, "<i>");

    if (filename == NULL)
    {
        g_string_append(gstr, _("No source selected"));
    }
    else
    {
        char buf[128];
        tr_strlsize(buf, builder->totalSize, sizeof(buf));
        g_string_append_printf(gstr, ngettext("%1$s; %2$'d File", "%1$s; %2$'d Files", builder->fileCount), buf,
            builder->fileCount);
        g_string_append(gstr, "; ");

        tr_formatter_mem_B(buf, builder->pieceSize, sizeof(buf));
        g_string_append_printf(gstr, ngettext("%1$'d Piece @ %2$s", "%1$'d Pieces @ %2$s", builder->pieceCount),
            builder->pieceCount, buf);
    }

    g_string_append(gstr, "</i>");
    gtk_label_set_markup(GTK_LABEL(ui->pieces_lb), gstr->str);
    g_string_free(gstr, TRUE);
}

static void setFilename(MakeMetaUI* ui, char const* filename)
{
    if (ui->builder != NULL)
    {
        tr_metaInfoBuilderFree(ui->builder);
        ui->builder = NULL;
    }

    if (filename)
    {
        ui->builder = tr_metaInfoBuilderCreate(filename);
    }

    updatePiecesLabel(ui);
}

static void onChooserChosen(GtkFileChooser* chooser, gpointer user_data)
{
    char* filename;
    MakeMetaUI* ui = user_data;

    g_object_set_data(G_OBJECT(chooser), FILE_CHOSEN_KEY, GINT_TO_POINTER(TRUE));

    filename = gtk_file_chooser_get_filename(chooser);
    setFilename(ui, filename);
    g_free(filename);
}

static void onSourceToggled2(GtkToggleButton* tb, GtkWidget* chooser, MakeMetaUI* ui)
{
    if (gtk_toggle_button_get_active(tb))
    {
        if (g_object_get_data(G_OBJECT(chooser), FILE_CHOSEN_KEY) != NULL)
        {
            onChooserChosen(GTK_FILE_CHOOSER(chooser), ui);
        }
        else
        {
            setFilename(ui, NULL);
        }
    }
}

static void onFolderToggled(GtkToggleButton* tb, gpointer data)
{
    MakeMetaUI* ui = data;
    onSourceToggled2(tb, ui->folder_chooser, ui);
}

static void onFileToggled(GtkToggleButton* tb, gpointer data)
{
    MakeMetaUI* ui = data;
    onSourceToggled2(tb, ui->file_chooser, ui);
}

static char const* getDefaultSavePath(void)
{
    return g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP);
}

static void on_drag_data_received(GtkWidget* widget UNUSED, GdkDragContext* drag_context, gint x UNUSED, gint y UNUSED,
    GtkSelectionData* selection_data, guint info UNUSED, guint time_, gpointer user_data)
{
    gboolean success = FALSE;
    MakeMetaUI* ui = user_data;
    char** uris = gtk_selection_data_get_uris(selection_data);

    if (uris != NULL && uris[0] != NULL)
    {
        char const* uri = uris[0];
        gchar* filename = g_filename_from_uri(uri, NULL, NULL);

        if (g_file_test(filename, G_FILE_TEST_IS_DIR))
        {
            /* a directory was dragged onto the dialog... */
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->folder_radio), TRUE);
            gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(ui->folder_chooser), filename);
            success = TRUE;
        }
        else if (g_file_test(filename, G_FILE_TEST_IS_REGULAR))
        {
            /* a file was dragged on to the dialog... */
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->file_radio), TRUE);
            gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(ui->file_chooser), filename);
            success = TRUE;
        }

        g_free(filename);
    }

    g_strfreev(uris);
    gtk_drag_finish(drag_context, success, FALSE, time_);
}

GtkWidget* gtr_torrent_creation_dialog_new(GtkWindow* parent, TrCore* core)
{
    char const* str;
    GtkWidget* d;
    GtkWidget* t;
    GtkWidget* w;
    GtkWidget* l;
    GtkWidget* fr;
    GtkWidget* sw;
    GtkWidget* v;
    GSList* slist;
    guint row = 0;
    MakeMetaUI* ui = g_new0(MakeMetaUI, 1);

    ui->core = core;

    d = gtk_dialog_new_with_buttons(_("New Torrent"), parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE,
        GTK_RESPONSE_CLOSE, GTK_STOCK_NEW, GTK_RESPONSE_ACCEPT, NULL);
    ui->dialog = d;
    g_signal_connect(d, "response", G_CALLBACK(onResponse), ui);
    g_object_set_data_full(G_OBJECT(d), "ui", ui, freeMetaUI);

    t = hig_workarea_create();

    hig_workarea_add_section_title(t, &row, _("Files"));

    str = _("Sa_ve to:");
    w = gtk_file_chooser_button_new(NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(w), getDefaultSavePath());
    ui->destination_chooser = w;
    hig_workarea_add_row(t, &row, str, w, NULL);

    l = gtk_radio_button_new_with_mnemonic(NULL, _("Source F_older:"));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(l), FALSE);
    w = gtk_file_chooser_button_new(NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
    g_signal_connect(l, "toggled", G_CALLBACK(onFolderToggled), ui);
    g_signal_connect(l, "toggled", G_CALLBACK(onSourceToggled), w);
    g_signal_connect(w, "selection-changed", G_CALLBACK(onChooserChosen), ui);
    ui->folder_radio = l;
    ui->folder_chooser = w;
    gtk_widget_set_sensitive(GTK_WIDGET(w), FALSE);
    hig_workarea_add_row_w(t, &row, l, w, NULL);

    slist = gtk_radio_button_get_group(GTK_RADIO_BUTTON(l)),
    l = gtk_radio_button_new_with_mnemonic(slist, _("Source _File:"));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(l), TRUE);
    w = gtk_file_chooser_button_new(NULL, GTK_FILE_CHOOSER_ACTION_OPEN);
    g_signal_connect(l, "toggled", G_CALLBACK(onFileToggled), ui);
    g_signal_connect(l, "toggled", G_CALLBACK(onSourceToggled), w);
    g_signal_connect(w, "selection-changed", G_CALLBACK(onChooserChosen), ui);
    ui->file_radio = l;
    ui->file_chooser = w;
    hig_workarea_add_row_w(t, &row, l, w, NULL);

    w = gtk_label_new(NULL);
    ui->pieces_lb = w;
    gtk_label_set_markup(GTK_LABEL(w), _("<i>No source selected</i>"));
    hig_workarea_add_row(t, &row, NULL, w, NULL);

    hig_workarea_add_section_divider(t, &row);
    hig_workarea_add_section_title(t, &row, _("Properties"));

    str = _("_Trackers:");
    v = gtk_box_new(GTK_ORIENTATION_VERTICAL, GUI_PAD_SMALL);
    ui->announce_text_buffer = gtk_text_buffer_new(NULL);
    w = gtk_text_view_new_with_buffer(ui->announce_text_buffer);
    gtk_widget_set_size_request(w, -1, 80);
    sw = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_container_add(GTK_CONTAINER(sw), w);
    fr = gtk_frame_new(NULL);
    gtk_frame_set_shadow_type(GTK_FRAME(fr), GTK_SHADOW_IN);
    gtk_container_add(GTK_CONTAINER(fr), sw);
    gtk_box_pack_start(GTK_BOX(v), fr, TRUE, TRUE, 0);
    l = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(l), _("To add a backup URL, add it on the line after the primary URL.\n"
        "To add another primary URL, add it after a blank line."));
    gtk_label_set_justify(GTK_LABEL(l), GTK_JUSTIFY_LEFT);
    g_object_set(l, "halign", GTK_ALIGN_START, "valign", GTK_ALIGN_CENTER, NULL);
    gtk_box_pack_start(GTK_BOX(v), l, FALSE, FALSE, 0);
    hig_workarea_add_tall_row(t, &row, str, v, NULL);

    l = gtk_check_button_new_with_mnemonic(_("Co_mment:"));
    ui->comment_check = l;
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(l), FALSE);
    w = gtk_entry_new();
    ui->comment_entry = w;
    gtk_widget_set_sensitive(GTK_WIDGET(w), FALSE);
    g_signal_connect(l, "toggled", G_CALLBACK(onSourceToggled), w);
    hig_workarea_add_row_w(t, &row, l, w, NULL);

    w = hig_workarea_add_wide_checkbutton(t, &row, _("_Private torrent"), FALSE);
    ui->private_check = w;

    gtr_dialog_set_content(GTK_DIALOG(d), t);

    gtk_drag_dest_set(d, GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
    gtk_drag_dest_add_uri_targets(d);
    g_signal_connect(d, "drag-data-received", G_CALLBACK(on_drag_data_received), ui);

    return d;
}
