/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: favorites.cpp,v 1.7.2.4 2004/10/18 18:53:31 rggammon Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "favorites.h"
#include "commonapp.h"
#include "hxplayer-i18n.h"

#include <string.h>

enum
{
  COLUMN_TITLE = 0,
  COLUMN_URL,
  COLUMN_EDITABLE,
  COLUMN_FAVORITE,
  NUM_COLUMNS
};

/* Manage HXFavorite structure
 * ===========================
 */

HXFavorite*
hx_favorite_new(void)
{
    return g_new0(HXFavorite, 1);
}

void
hx_favorite_free(HXFavorite* favorite)
{
    g_free(favorite->title);
    g_free(favorite->url);
    g_free(favorite);
}

/* Signal handler prototypes
 * =========================
 */
extern "C"
{
void hfd_new_favorite(GtkWidget* widget);
void hfd_delete_favorite(GtkWidget* widget);
};

/* Import/export functionality
 * =========================
 */
gboolean
favorites_export_to_m3u(const gchar* filename, const GList* favorites_list)
{
    GIOChannel* chan;
    GError *error = NULL;
    const GList* favorites_iter;
    gboolean result = TRUE;
    
    chan = g_io_channel_new_file(filename, "w", &error);
    if(error)
    {
        g_warning(error->message);
        g_free(error);
        error = NULL;
        return FALSE;
    }
    if(!chan)
    {
        /* File does not exist */
        return FALSE;
    }

    favorites_iter = favorites_list;
    while(favorites_iter)
    {
        HXFavorite* favorite = (HXFavorite*)favorites_iter->data;
        gchar* line;
        GError* error = NULL;
        gsize bytes_written;
        gsize len;
        GIOStatus status;

        line = g_strdup_printf("%s\n", favorite->url);
        len = strlen(line);
        status = g_io_channel_write_chars(chan, line, len, &bytes_written, &error);
        g_free(line);
        
        if(error)
        {
            g_warning(error->message);
            g_free(error);
            result = FALSE;
            break;
        }
        if(status != G_IO_STATUS_NORMAL || len != bytes_written)
        {
            g_warning("Error writing config file");
            result = FALSE;
            break;
        }

        favorites_iter = g_list_next(favorites_iter);
    }

    g_io_channel_shutdown(chan, TRUE, &error);

    return TRUE;
}

gboolean
favorites_import_from_m3u(const gchar* filename, GList** favorites_list_ptr)
{
    GIOChannel* chan;
    GError *error = NULL;
    GList* favorites_list = *favorites_list_ptr;
    
    chan = g_io_channel_new_file(filename, "r", &error);
    if(error)
    {
        g_warning(error->message);
        g_free(error);
        error = NULL;
        return FALSE;
    }
    if(!chan)
    {
        /* File does not exist */
        return FALSE;
    }

    for(;;)
    {
        GIOStatus status;
        gchar* line;
        gsize terminator_pos;
        
        status = g_io_channel_read_line(chan, &line, NULL, &terminator_pos, &error);
        if(error)
        {
            g_warning(error->message);
            g_free(error);
            error = NULL;
            break;
        }
        if(status == G_IO_STATUS_EOF)
        {
            break;           
        }
        else if(status == G_IO_STATUS_AGAIN || !line)
        {
            continue;
        }
        else if(status != G_IO_STATUS_NORMAL)
        {
            g_warning("Error reading config file");
            break;
        }

        if(line[terminator_pos] == '\n')
        {
            /* Strip newline */
            line[terminator_pos] = '\0';
        }
        
        HXFavorite* favorite;
        gchar* filename;

        favorite = hx_favorite_new();
        filename = strrchr(line, '/');
        if(filename)
        {
            filename++;
            if(*filename)
            {
                favorite->title = g_strdup(filename);
            }
        }

        if(!favorite->title)
        {
            favorite->title = g_strdup(line);
        }

        favorite->url = line;
                
        favorites_list = g_list_append(favorites_list, favorite);
    }

    g_io_channel_shutdown(chan, TRUE, &error);

    *favorites_list_ptr = favorites_list;

    return TRUE;
}


/* Import/export favorites dialogs
 * ===============================
 */

static void
hxplay_export_favorites_dialog_response(GtkWidget* dialog,
                                        gint response_id,
                                        GList* favorites_list)
{
    if(response_id == GTK_RESPONSE_OK || response_id == GTK_RESPONSE_ACCEPT)
    {
        const gchar* dialog_selection = NULL;
        
#if GTK_CHECK_VERSION(2, 4, 0)
        dialog_selection = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
#else
        dialog_selection = gtk_file_selection_get_filename(GTK_FILE_SELECTION(dialog));
#endif
        favorites_export_to_m3u(dialog_selection, favorites_list);
    }
}

GtkDialog*
hxplay_export_favorites_dialog_new(const GList* favorites_list)
{
    GtkWidget* fs;
    const gchar* export_favorites = _("Export Favorites");

#if GTK_CHECK_VERSION(2, 4, 0)
    fs = gtk_file_chooser_dialog_new(export_favorites,
                                     NULL,
                                     GTK_FILE_CHOOSER_ACTION_SAVE,
                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
                                     NULL);

    gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), TRUE);
    
#else	
    fs = gtk_file_selection_new (export_favorites);
#endif	

    g_signal_connect (G_OBJECT (fs), "response",
                      G_CALLBACK (hxplay_export_favorites_dialog_response),
                      (gpointer)favorites_list);
    
    return GTK_DIALOG(fs);
}


static void
hxplay_import_favorites_dialog_response(GtkWidget* dialog,
                                        gint response_id,
                                        GList** favorites_list_ptr)    
{
    if(response_id == GTK_RESPONSE_OK || response_id == GTK_RESPONSE_ACCEPT)
    {
        const gchar* dialog_selection = NULL;

#if GTK_CHECK_VERSION(2, 4, 0)
        dialog_selection = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
#else
        dialog_selection = gtk_file_selection_get_filename(GTK_FILE_SELECTION(dialog));
#endif
        favorites_import_from_m3u(dialog_selection, favorites_list_ptr);
    }
}

GtkDialog*
hxplay_import_favorites_dialog_new(GList** favorites_list_ptr)
{
    GtkWidget* fs;
    const gchar* import_favorites = _("Import Favorites");

#if GTK_CHECK_VERSION(2, 4, 0)
    fs = gtk_file_chooser_dialog_new(import_favorites,
                                     NULL,
                                     GTK_FILE_CHOOSER_ACTION_OPEN,
                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
                                     NULL);

    gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(fs), TRUE);

#else		
    fs = gtk_file_selection_new (import_favorites);

    gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(fs));
#endif 	
        
    g_signal_connect (G_OBJECT (fs), "response",
                      G_CALLBACK (hxplay_import_favorites_dialog_response),
                      favorites_list_ptr);

    return GTK_DIALOG(fs);
}


/* Favorites management dialog callbacks
 * =====================================
 */
static void
hfd_cell_edited(GtkCellRendererText *cell,
                const gchar         *path_string,
                const gchar         *new_text,
                gpointer            /* data */)
{
    GtkTreeModel* model;
    GtkTreePath* path = gtk_tree_path_new_from_string (path_string);
    GtkTreeIter iter;
    gint column;
    HXFavorite* favorite = NULL;
    
    column = (gint)g_object_get_data (G_OBJECT (cell), "column");
    model = (GtkTreeModel*)g_object_get_data (G_OBJECT (cell), "model");

    g_return_if_fail(model != NULL);
    
    gtk_tree_model_get_iter(model, &iter, path);
    
    gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
                       COLUMN_FAVORITE, &favorite,
                       -1);

    g_return_if_fail(favorite != NULL);
    
    switch(column)
    {
        case COLUMN_TITLE:
            g_free(favorite->title);
            favorite->title = g_strdup(new_text);
            gtk_list_store_set(GTK_LIST_STORE(model), &iter,
                               COLUMN_TITLE, favorite->title,
                               -1);
            break;

        case COLUMN_URL:
            g_free(favorite->url);
            favorite->url = g_strdup(new_text);
            gtk_list_store_set(GTK_LIST_STORE(model), &iter,
                               COLUMN_URL, favorite->url,
                               -1);
            break;
    }

    gtk_tree_path_free(path);
}

static void
count_selections(GtkTreeModel* /* model */,
                 GtkTreePath*  /* path */,
                 GtkTreeIter*  /* iter */,
                 gpointer data)
{
    gint* count = (gint*)data;

    (*count)++;
}

void 
hfd_selection_changed(GtkTreeSelection* selection,
                      GtkWidget* delete_button)
{
    gint count = 0;
        
    gtk_tree_selection_selected_foreach(selection, count_selections, &count);

    if(count > 0)
    {
        /* Enable delete button */
        gtk_widget_set_sensitive(GTK_WIDGET(delete_button), TRUE);
    }
    else
    {
        /* Disable delete button */
        gtk_widget_set_sensitive(GTK_WIDGET(delete_button), FALSE);
    }
}

typedef struct
{
    GladeXML* xml;
} HXManageFavoritesDialog;

static void
hxplay_manage_favorites_dialog_destroy (GtkWidget* /* widget */,
                                        HXManageFavoritesDialog* info)
{
    glade_xml_destroy (info->xml);

    g_free(info);
}

GtkDialog*
hxplay_manage_favorites_dialog_new(GList** favorites_list_ptr)
{
    GladeXML* xml;
    GtkWidget* dialog;
    gchar* filename;
    GList* favorites_list = *favorites_list_ptr;
    GList* favorites_list_iter;
    GtkWidget* tree_view;
    GtkWidget* delete_button;
    GtkListStore *store;
    GtkCellRenderer *renderer;
    GtkTreeViewColumn *column;
    GtkTreeIter tree_iter;
    GtkTreeSelection* selection;
    HXManageFavoritesDialog* info;
    
    filename = hxcommon_locate_file("favorites.glade");
    xml = glade_xml_new (filename, NULL, NULL);
    g_free(filename);
    g_return_val_if_fail(xml != NULL, NULL);

    dialog = glade_xml_get_widget(xml, "hxplayer_favorites_dialog");    
    g_return_val_if_fail(dialog != NULL, NULL);

    delete_button = glade_xml_get_widget(xml, "hfd_delete_button");    
    g_return_val_if_fail(delete_button != NULL, NULL);
    
    tree_view = glade_xml_get_widget (xml, "hfd_tree_view");
    g_return_val_if_fail(tree_view != NULL, NULL);

    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));

    gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
    
    g_signal_connect(G_OBJECT(selection), "changed",
                     G_CALLBACK(hfd_selection_changed), delete_button);

    /* Populate the store */
    store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER);
    
    favorites_list_iter = favorites_list;
    while(favorites_list_iter)
    {
        HXFavorite* favorite = (HXFavorite*)favorites_list_iter->data;
        
        gtk_list_store_append(store, &tree_iter);
	gtk_list_store_set (store, &tree_iter,
			    COLUMN_TITLE, favorite->title,
                            COLUMN_URL, favorite->url,
                            COLUMN_EDITABLE, TRUE,
                            COLUMN_FAVORITE, favorite,
			    -1);

        favorites_list_iter = g_list_next(favorites_list_iter);
    }
    
    /* Configure the tree view */
    gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view),
			    GTK_TREE_MODEL(store));

    /* Title column */
    renderer = gtk_cell_renderer_text_new();
    
    g_signal_connect(G_OBJECT(renderer), "edited",
                     G_CALLBACK(hfd_cell_edited), NULL);

    g_object_set_data(G_OBJECT(renderer), "model", store);
    g_object_set_data(G_OBJECT(renderer), "column", (gint *)COLUMN_TITLE);
    
    column = gtk_tree_view_column_new_with_attributes(_("Title"),
                                                      renderer,
                                                      "text", COLUMN_TITLE,
                                                      "editable", COLUMN_EDITABLE,
                                                      NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);

    // XXXRGG: How do I set the default (vs minimum) width of a column?
    gtk_tree_view_column_set_min_width (column, 80);
    
    /* Url column */
    renderer = gtk_cell_renderer_text_new();

    g_signal_connect (G_OBJECT (renderer), "edited",
                      G_CALLBACK (hfd_cell_edited), NULL);
    g_object_set_data(G_OBJECT(renderer), "model", store);
    g_object_set_data(G_OBJECT(renderer), "column", (gint *)COLUMN_URL);
    
    column = gtk_tree_view_column_new_with_attributes(_("URI"),
                                                      renderer,
                                                      "text", COLUMN_URL,
                                                      "editable", COLUMN_EDITABLE,
                                                      NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
    
    /* Set data for new/delete button handlers */
    g_object_set_data(G_OBJECT(dialog), "favorites_list_ptr", (gpointer)favorites_list_ptr);
    g_object_set_data(G_OBJECT(dialog), "model",              (gpointer)store);
    g_object_set_data(G_OBJECT(dialog), "tree_view",          (gpointer)tree_view);

    info = g_new0(HXManageFavoritesDialog, 1);
    info->xml = xml;
    
    g_signal_connect (G_OBJECT (dialog), "destroy",
                      G_CALLBACK (hxplay_manage_favorites_dialog_destroy),
                      info);

    return GTK_DIALOG(dialog);
}


void hfd_new_favorite(GtkWidget* widget)
{
    GList* favorites_list;
    GList** favorites_list_ptr;
    GtkListStore* list_store;
    gpointer model_data;
    HXFavorite* favorite;
    GtkTreeIter tree_iter;
    GtkWidget* tree_view;
    GtkTreePath* path;
    
    GtkWidget* dialog = gtk_widget_get_toplevel(widget);
    
    favorites_list_ptr = (GList**)g_object_get_data(G_OBJECT(dialog), "favorites_list_ptr");
    favorites_list = *favorites_list_ptr;
    model_data = g_object_get_data(G_OBJECT(dialog), "model");
    list_store = GTK_LIST_STORE(model_data);
    tree_view = (GtkWidget*)g_object_get_data(G_OBJECT(dialog), "tree_view");

    favorite = hx_favorite_new();

    favorite->title = g_strdup(_("Untitled"));
    favorite->url = g_strdup("http://");

    favorites_list = g_list_append(favorites_list, favorite);
    
    gtk_list_store_append(list_store, &tree_iter);
    gtk_list_store_set (list_store, &tree_iter,
                        COLUMN_TITLE, favorite->title,
                        COLUMN_URL, favorite->url,
                        COLUMN_EDITABLE, TRUE,
                        COLUMN_FAVORITE, favorite,
                        -1);
    
    path = gtk_tree_model_get_path(GTK_TREE_MODEL(list_store),
                                   &tree_iter);
    
    gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree_view), path, NULL, FALSE);
    gtk_tree_path_free(path);
    
    *favorites_list_ptr = favorites_list;
}

void hfd_delete_favorite(GtkWidget* widget)
{
    GList* favorites_list;
    GList** favorites_list_ptr;

    GList* favorites_link;
    GtkListStore* list_store;
    gpointer model_data;
    HXFavorite* favorite;
    GtkTreeIter tree_iter;
    GtkTreeIter* select_iter = NULL;
    GtkTreeIter* last_iter = NULL;
    GtkTreeSelection* selection;
    GtkWidget* tree_view;
    gboolean got_row;
    gboolean deleted_row = FALSE;    
    
    GtkWidget* dialog = gtk_widget_get_toplevel(widget);
    
    favorites_list_ptr = (GList**)g_object_get_data(G_OBJECT(dialog), "favorites_list_ptr");
    favorites_list = *favorites_list_ptr;
    tree_view = (GtkWidget*)g_object_get_data(G_OBJECT(dialog), "tree_view");
    model_data = g_object_get_data(G_OBJECT(dialog), "model");
    list_store = GTK_LIST_STORE(model_data);

    g_return_if_fail(list_store != NULL &&
                     favorites_list != NULL &&
                     model_data != NULL);
    
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
    
    got_row = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &tree_iter);
    while(got_row)
    {
        if(gtk_tree_selection_iter_is_selected(selection, &tree_iter))
        {
            gtk_tree_model_get(GTK_TREE_MODEL(list_store), &tree_iter,
                               COLUMN_FAVORITE, &favorite,
                               -1);
            favorites_link = g_list_find(favorites_list, favorite);
            favorites_list = g_list_remove_link(favorites_list, favorites_link);

            hx_favorite_free(favorite);

            // Gtk 2.2:
            // got_row = gtk_list_store_remove(list_store, &tree_iter);

            gtk_list_store_remove(list_store, &tree_iter);
            got_row = tree_iter.stamp != 0;

            if(select_iter)
            {
                gtk_tree_iter_free(select_iter);
                select_iter = NULL;
            }

            if(got_row)
            {
                select_iter = gtk_tree_iter_copy(&tree_iter);
            }

            deleted_row = TRUE;
        }
        else
        {
            if(last_iter)
            {
                gtk_tree_iter_free(last_iter);
            }

            last_iter = gtk_tree_iter_copy(&tree_iter);

            got_row = gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), &tree_iter);
        }
    }

    if(deleted_row)
    {
        GtkTreePath* path;

        if(!select_iter)
        {
            select_iter = last_iter;
        }
        else
        {
            gtk_tree_iter_free(last_iter);            
        }
        
        if(select_iter)
        {
            path = gtk_tree_model_get_path(GTK_TREE_MODEL(list_store),
                                           select_iter);
    
            gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree_view), path, NULL, FALSE);
            gtk_tree_path_free(path);
            gtk_tree_iter_free(select_iter);
            select_iter = NULL;
        }
    }

    *favorites_list_ptr = favorites_list;
}
