/*
 * 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.
 *
 * See the COPYING file for license information.
 *
 * Guillaume Chazarain <guichaz@gmail.com>
 */

/******************************************
 * Make thumbnails and keep track of them *
 ******************************************/

#include "gliv.h"
#include "thumbnails.h"
#include "options.h"
#include "tree.h"
#include "thread.h"
#include "callbacks.h"

extern options_struct *options;

static GHashTable *thumbs_hash_table = NULL;

gchar *get_absolute_filename(const gchar * filename)
{
    static gchar *cwd = NULL;

    if (filename[0] == '/')
        return g_strdup(filename);

    if (cwd == NULL)
        cwd = g_get_current_dir();

    while (filename[0] == '.' && filename[1] == '/') {
        filename += 2;
        while (filename[0] == '/')
            filename++;
    }

    return g_build_filename(cwd, filename, NULL);
}

/*
 * We include the thumbnails wanted dimensions in the hash key, this way we can
 * keep track of many thumbnails with different sizes for a given filename.
 */
static gchar *get_key(const gchar * filename)
{
    gchar *key, *fullpath;

    fullpath = get_absolute_filename(filename);

    key = g_strdup_printf("%s_%d_%d", fullpath,
                          options->thumb_width, options->thumb_height);
    g_free(fullpath);
    return key;
}

static GdkPixbuf *create_mini(GdkPixbuf * pixbuf)
{
    gint w, h;
    gfloat zoom_w, zoom_h, zoom;
    gint mini_w, mini_h;
    GdkPixbuf *res;

    w = gdk_pixbuf_get_width(pixbuf);
    h = gdk_pixbuf_get_height(pixbuf);

    zoom_w = (gfloat) w / options->thumb_width;
    zoom_h = (gfloat) h / options->thumb_height;
    zoom = MAX(zoom_w, zoom_h);

    if (zoom <= 1.0) {
        /* A thumbnail should not be bigger than the original. */
        g_object_ref(pixbuf);
        return pixbuf;
    }

    mini_w = (gint) (w / zoom);
    mini_h = (gint) (h / zoom);

    mini_w = MAX(mini_w, 1);
    mini_h = MAX(mini_h, 1);

    res = gdk_pixbuf_scale_simple(pixbuf, mini_w, mini_h, GDK_INTERP_BILINEAR);

    return res;
}

static void init_hash(void)
{
    if (thumbs_hash_table == NULL)
        /* First time. */
        thumbs_hash_table = g_hash_table_new(g_str_hash, g_str_equal);
}

/* Runs in a separate thread. */
static GdkPixbuf *get_mini(const gchar * filename)
{
    GdkPixbuf *pixbuf, *mini;

    if (canceled_using_tree())
        return NULL;

    pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
    if (pixbuf == NULL)
        return NULL;

    if (canceled_using_tree()) {
        g_object_unref(pixbuf);
        return NULL;
    }

    mini = create_mini(pixbuf);
    g_object_unref(pixbuf);

    return mini;
}

/* Called when we have loaded an image, to add its thumbnail. */
void add_thumbnail(const gchar * filename, GdkPixbuf * pixbuf)
{
    gchar *key;
    GdkPixbuf *mini;

    init_hash();

    key = get_key(filename);
    if (g_hash_table_lookup(thumbs_hash_table, key) != NULL) {
        g_free(key);
        return;
    }

    mini = do_threaded((GThreadFunc) create_mini, pixbuf);
    if (mini == NULL) {
        g_free(key);
        return;
    }

    g_hash_table_insert(thumbs_hash_table, key, mini);
    if (mini == pixbuf)
        g_object_ref(pixbuf);
}

gboolean fill_thumbnail(tree_item * item)
{
    if (item->thumb != NULL) {
        /* Simulate the do_threaded() effect. */
        process_events();
        return TRUE;
    }

    if (item->thumb_key == NULL)
        item->thumb_key = get_key(item->path);

    init_hash();

    item->thumb = g_hash_table_lookup(thumbs_hash_table, item->thumb_key);
    if (item->thumb != NULL)
        return TRUE;

    item->thumb = do_threaded((GThreadFunc) get_mini, item->path);
    if (item->thumb == NULL)
        return FALSE;

    g_hash_table_insert(thumbs_hash_table, g_strdup(item->thumb_key),
                        item->thumb);
    return TRUE;
}

void collection_add_thumbnail(gchar * key, GdkPixbuf * thumb)
{
    init_hash();

    g_hash_table_insert(thumbs_hash_table, key, thumb);
}

GdkPixbuf *get_thumbnail(const gchar * filename, gchar ** thumb_key)
{
    tree_item item;

    item.name = NULL;
    item.path = (gchar *) filename;
    item.thumb_key = NULL;
    item.thumb = NULL;

    fill_thumbnail(&item);

    if (thumb_key)
        *thumb_key = item.thumb_key;

    return item.thumb;
}
