/*
 * This file Copyright (C) Mnemosyne LLC
 *
 * This file is licensed by the GPL version 2. Works owned by the
 * Transmission project are granted a special exemption to clause 2(b)
 * so that the bulk of its code can remain under the MIT license.
 * This exemption does not extend to derived works not owned by
 * the Transmission project.
 *
 * $Id: favicon.c 12655 2011-08-08 17:10:55Z jordan $
 */

#include <glib/gstdio.h> /* g_remove() */
#include <gtk/gtk.h>

#include <libtransmission/transmission.h>
#include <libtransmission/web.h> /* tr_webRun() */

#include "favicon.h"
#include "util.h" /* gtr_get_host_from_url() */

#define IMAGE_TYPES 4
static const char * image_types[IMAGE_TYPES] = { "ico", "png", "gif", "jpg" };

struct favicon_data
{
    tr_session * session;
    GFunc func;
    gpointer data;
    char * host;
    char * contents;
    size_t len;
    int type;
};

static char*
get_url( const char * host, int image_type )
{
    return g_strdup_printf( "http://%s/favicon.%s", host, image_types[image_type] );
}

static char*
favicon_get_cache_dir( void )
{
    static char * dir = NULL;

    if( dir == NULL )
    {
        dir = g_build_filename( g_get_user_cache_dir(),
                                "transmission",
                                "favicons",
                                NULL );
        g_mkdir_with_parents( dir, 0777 );
    }

    return dir;
}

static char*
favicon_get_cache_filename( const char * host )
{
    return g_build_filename( favicon_get_cache_dir(), host, NULL );
}

static void
favicon_save_to_cache( const char * host, const void * data, size_t len )
{
    char * filename = favicon_get_cache_filename( host );
    g_file_set_contents( filename, data, len, NULL );
    g_free( filename );
}

static GdkPixbuf*
favicon_load_from_cache( const char * host )
{
    char * filename = favicon_get_cache_filename( host );
    GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_size( filename, 16, 16, NULL );
    if( pixbuf == NULL ) /* bad file */
        g_remove( filename );
    g_free( filename );
    return pixbuf;
}

static void favicon_web_done_cb( tr_session*, bool, bool, long, const void*, size_t, void* );

static gboolean
favicon_web_done_idle_cb( gpointer vfav )
{
    GdkPixbuf * pixbuf = NULL;
    gboolean finished = FALSE;
    struct favicon_data * fav = vfav;

    if( fav->len > 0 ) /* we got something... try to make a pixbuf from it */
    {
        favicon_save_to_cache( fav->host, fav->contents, fav->len );
        pixbuf = favicon_load_from_cache( fav->host );
        finished = pixbuf != NULL;
    }

    if( !finished ) /* no pixbuf yet... */
    {
        if( ++fav->type == IMAGE_TYPES ) /* failure */
        {
            finished = TRUE;
        }
        else /* keep trying */
        {
            char * url = get_url( fav->host, fav->type );

            g_free( fav->contents );
            fav->contents = NULL;
            fav->len = 0;

            tr_webRun( fav->session, url, NULL, NULL, favicon_web_done_cb, fav );
            g_free( url );
        }
    }

    if( finished )
    {
        fav->func( pixbuf, fav->data );
        g_free( fav->host );
        g_free( fav->contents );
        g_free( fav );
    }

    return FALSE;
}

static void
favicon_web_done_cb( tr_session    * session UNUSED,
                     bool            did_connect UNUSED,
                     bool            did_timeout UNUSED,
                     long            code UNUSED,
                     const void    * data,
                     size_t          len,
                     void          * vfav )
{
    struct favicon_data * fav = vfav;
    fav->contents = g_memdup( data, len );
    fav->len = len;

    gdk_threads_add_idle( favicon_web_done_idle_cb, fav );
}

void
gtr_get_favicon( tr_session  * session,
                 const char  * host,
                 GFunc         pixbuf_ready_func,
                 gpointer      pixbuf_ready_func_data )
{
    GdkPixbuf * pixbuf = favicon_load_from_cache( host );

    if( pixbuf != NULL )
    {
        pixbuf_ready_func( pixbuf, pixbuf_ready_func_data );
    }
    else
    {
        struct favicon_data * data;
        char * url = get_url( host, 0 );

        data = g_new( struct favicon_data, 1 );
        data->session = session;
        data->func = pixbuf_ready_func;
        data->data = pixbuf_ready_func_data;
        data->host = g_strdup( host );
        data->type = 0;

        tr_webRun( session, url, NULL, NULL, favicon_web_done_cb, data );
        g_free( url );
    }
}

void
gtr_get_favicon_from_url( tr_session  * session,
                          const char  * url,
                          GFunc         pixbuf_ready_func,
                          gpointer      pixbuf_ready_func_data )
{
    char host[1024];
    gtr_get_host_from_url( host, sizeof( host ), url );
    gtr_get_favicon( session, host, pixbuf_ready_func, pixbuf_ready_func_data );
}
