/*****************************************************************
 * gmerlin - a general purpose multimedia framework and applications
 *
 * Copyright (c) 2001 - 2012 Members of the Gmerlin project
 * gmerlin-general@lists.sourceforge.net
 * http://gmerlin.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, see <http://www.gnu.org/licenses/>.
 * *****************************************************************/

#include <config.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

#include <gmerlin/translation.h>
#include <gmerlin/log.h>

#define LOG_DOMAIN "mdb_thumbnail"

#include <gmerlin/mdb.h>

#include <gmerlin/utils.h>

#include <mdb_private.h>

#include <gavl/metatags.h>

#define META_MIMETYPE_ID "MIMETYPE_ID"

static void create_thumbnail(const bg_mdb_t * mdb, gavl_dictionary_t * m,
                             const char * tag,
                             const char * image,
                             int img_width, int img_height,
                             int width, int height, const char * mimetype)
  {
  int done = 0;
  struct stat st;
  char * tn_path = NULL;
  char * tn_file = NULL;
  char * real_path = NULL;
  
  gavl_video_frame_t * in_frame = NULL;
  gavl_video_format_t in_format;
  int64_t image_id = -1;
  int64_t thumbnail_id = -1;
  int64_t mimetype_id = -1;
  char * sql;
  
  /* This doesn't check if the thumbnail exists already */
  
  if((img_width <= width) && (img_height <= height))
    goto end;

  if(!strncasecmp(image, "file://", 7))
    image += 7;

  if(strstr(image, "://"))
    goto end;
  
  /* Create directory if necessary */
  tn_path = bg_sprintf("%s/thumbnails/%dx%d", mdb->path, width, height);
  bg_ensure_directory(tn_path, 0);
  free(tn_path);
  tn_path = NULL;
  
  image_id = bg_sqlite_string_to_id(mdb->thumbnail_db,
                                    "images",
                                    "ID",
                                    GAVL_META_URI,
                                    image);

  /* Check if the thumbnail exists already */
  if(image_id >= 0)
    {
    tn_file = bg_sprintf("%dx%d/%"PRId64".%s",
                         width, height, image_id,
                         bg_mimetype_to_ext(mimetype));
  
    tn_path = bg_sprintf("%s/thumbnails/%s",
                         mdb->path, tn_file);
    

    if(!stat(tn_path, &st))
      {
      time_t t;
      done = 1;

      t = st.st_mtime;

      if(stat(image, &st) || (st.st_mtime > t))
        {
        remove(tn_path);
        done = 0;
        }
      }
    }
  
  if(done)
    goto end;
  
  if(!(in_frame = bg_plugin_registry_load_image(bg_plugin_reg,
                                                image,
                                                &in_format, NULL)))
    goto end;

  /* Check if we have the right size already */
  if(((in_format.image_width == width) && (in_format.image_height <= height)) ||
     ((in_format.image_width <= width) && (in_format.image_height == height)))
    goto end;

  if(image_id < 0)
    {
    image_id = bg_sqlite_get_max_int(mdb->thumbnail_db, "images", "ID") + 1;

    sql = sqlite3_mprintf("INSERT INTO images (ID, "GAVL_META_URI") VALUES "
                          "(%"PRId64", %Q);", image_id, image);
    bg_sqlite_exec(mdb->thumbnail_db, sql, NULL, NULL);
    sqlite3_free(sql);

    tn_file = bg_sprintf("%dx%d/%"PRId64".%s",
                         width, height, image_id,
                         bg_mimetype_to_ext(mimetype));
    
    tn_path = bg_sprintf("%s/thumbnails/%s",
                         mdb->path, tn_file);
    }
  
  real_path = bg_make_thumbnail(bg_plugin_reg,
                                in_frame,
                                &in_format,
                                &width, &height,
                                tn_path,
                                mimetype);

  gavl_log(GAVL_LOG_INFO, LOG_DOMAIN, "Created thumbnail %dx%d for %s", width, height, image);
  
  if(strcmp(real_path, tn_path))
    goto end;
  
  mimetype_id = bg_sqlite_string_to_id_add(mdb->thumbnail_db,
                                           "mimetypes",
                                           "ID",
                                           GAVL_META_MIMETYPE,
                                           mimetype);

  thumbnail_id = bg_sqlite_get_max_int(mdb->thumbnail_db, "thumbnails", "ID")+1;
  
  sql = sqlite3_mprintf("INSERT INTO thumbnails "
                        "(ID, PARENT, "GAVL_META_WIDTH", "GAVL_META_HEIGHT", "META_MIMETYPE_ID", "GAVL_META_URI") VALUES "
                        "(%"PRId64", %"PRId64", %d, %d, %"PRId64", %Q);", thumbnail_id, image_id, width, height, mimetype_id, tn_file);
  
  bg_sqlite_exec(mdb->thumbnail_db, sql, NULL, NULL);
  sqlite3_free(sql);
  
  end:

  if(in_frame)
    gavl_video_frame_destroy(in_frame);
  
  if(tn_file)
    free(tn_file);
  if(tn_path)
    free(tn_path);
  if(real_path)
    free(real_path);
  
  }

/* Create thumbs when the object is added to the db */
void bg_mdb_create_thumbnails(bg_mdb_t * mdb, gavl_dictionary_t * track)
  {
  int width, height;
  const char * path;
  const char * mimetype;

  gavl_dictionary_t * m = gavl_track_get_metadata_nc(track);

  pthread_mutex_lock(&mdb->thumbnail_mutex);

  bg_sqlite_start_transaction(mdb->thumbnail_db);
  
  /* Album cover */  
  if((path = gavl_dictionary_get_string_image_uri(m, GAVL_META_COVER_URL, 0, &width, &height, &mimetype)))
    {
    create_thumbnail(mdb, m, GAVL_META_COVER_URL, path, width, height, 600, 600, "image/jpeg"); // "large"
    create_thumbnail(mdb, m, GAVL_META_COVER_URL, path, width, height, 248, 248, "image/jpeg"); // NMJ
    create_thumbnail(mdb, m, GAVL_META_COVER_URL, path, width, height, 160, 160, "image/jpeg"); // Web, DLNA JPEG_TN
    create_thumbnail(mdb, m, GAVL_META_COVER_URL, path, width, height,  48,  48, "image/jpeg"); // Gmerlin GUI
    }
  
  /* Video poster */  
  if((path = gavl_dictionary_get_string_image_uri(m, GAVL_META_POSTER_URL, 0, &width, &height, &mimetype)))
    {
    create_thumbnail(mdb, m, GAVL_META_POSTER_URL, path, width, height, 124, 185, "image/jpeg"); // NMT Thumbnail
    create_thumbnail(mdb, m, GAVL_META_POSTER_URL, path, width, height, 220, 330, "image/jpeg"); // NMT Poster
    create_thumbnail(mdb, m, GAVL_META_COVER_URL, path, width, height,   48,  72, "image/jpeg"); // Gmerlin GUI
    }
  
  /* Wallpaper */  
  if((path = gavl_dictionary_get_string_image_uri(m, GAVL_META_WALLPAPER_URL, 0, &width, &height, &mimetype)))
    {
    create_thumbnail(mdb, m, GAVL_META_POSTER_URL, path, width, height, 1280, 720, "image/jpeg"); // NMT Wallpaper
    }

  bg_sqlite_end_transaction(mdb->thumbnail_db);
  
  pthread_mutex_unlock(&mdb->thumbnail_mutex);
  }

/* Delete thumbnails for a particular file */
static int delete_callback(void * data, int argc, char **argv, char **azColName)
  {
  bg_mdb_t * mdb = data;

  if(argv[0])
    {
    char * filename;
    filename = bg_sprintf("%s/thumbnails/%s", mdb->path, argv[0]);
    gavl_log(GAVL_LOG_INFO, LOG_DOMAIN, "Removing thumbnail %s", filename);
    remove(filename);
    free(filename);
    }
  return 0;
  }

void bg_mdb_delete_thumbnails(bg_mdb_t * mdb, const char * image)
  {
  char * sql;
  int64_t image_id;
  pthread_mutex_lock(&mdb->thumbnail_mutex);

  image_id = bg_sqlite_string_to_id(mdb->thumbnail_db, "images", "ID", GAVL_META_URI, image);
  
  if(image_id < 0)
    {
    pthread_mutex_unlock(&mdb->thumbnail_mutex);
    return;
    }
  
  sql = bg_sprintf("DELETE FROM images WHERE ID = %"PRId64";", image_id);
  bg_sqlite_exec(mdb->thumbnail_db, sql, NULL, NULL);
  free(sql);

  /* Delete files */
  sql = bg_sprintf("SELECT "GAVL_META_URI" FROM thumbnails WHERE PARENT = %"PRId64";", image_id);
  bg_sqlite_exec(mdb->thumbnail_db, sql, delete_callback, mdb);
  free(sql);

  
  sql = bg_sprintf("DELETE FROM thumbnails WHERE PARENT = %"PRId64";", image_id);
  bg_sqlite_exec(mdb->thumbnail_db, sql, NULL, NULL);
  free(sql);
  pthread_mutex_unlock(&mdb->thumbnail_mutex);
  
  }

typedef struct
  {
  const bg_mdb_t * mdb;
  gavl_dictionary_t * m;
  const char * tag;

  //  int64_t parent;
  
  } get_tn_t;

static int
get_thumbnail_callback(void * data, int argc, char **argv, char **azColName)
  {
  int mimetype_id;
  int width  = -1;
  int height = -1;
  int i = 0;
  char * mimetype = NULL;
  char * uri = NULL;
  

  get_tn_t * tn = data;
  
  for(i = 0; i < argc; i++)
    {
    if(!strcmp(azColName[i], GAVL_META_WIDTH))
      width = atoi(argv[i]);
    if(!strcmp(azColName[i], GAVL_META_HEIGHT))
      height = atoi(argv[i]);
    if(!strcmp(azColName[i], META_MIMETYPE_ID))
      {
      mimetype_id = atoi(argv[i]);
      mimetype = bg_sqlite_id_to_string(tn->mdb->thumbnail_db,
                                        "mimetypes",
                                        GAVL_META_MIMETYPE,
                                        "ID", mimetype_id);
      }
    if(!strcmp(azColName[i], GAVL_META_URI))
      uri = bg_sprintf("%s/thumbnails/%s", tn->mdb->path, argv[i]);
    }
  
  gavl_metadata_add_image_uri(tn->m, tn->tag, width, height, mimetype, uri);
  
  if(uri)
    free(uri);
  if(mimetype)
    free(mimetype);
  return 0;
  }

/* Get thumbnails for a particular track */
void bg_mdb_get_thumbnails(bg_mdb_t * mdb, gavl_dictionary_t * track)
  {
  int64_t id;
  char * sql;
  const char * path;
  get_tn_t tn;
  
  memset(&tn, 0, sizeof(tn));
  tn.m = gavl_track_get_metadata_nc(track);
  tn.mdb = mdb;
  
  if(!tn.m)
    return;
  
  pthread_mutex_lock(&mdb->thumbnail_mutex);
  
  /* Album cover */  
  if((path = gavl_dictionary_get_string_image_uri(tn.m, GAVL_META_COVER_URL, 0, NULL, NULL, NULL)) &&
     ((id = bg_sqlite_string_to_id(mdb->thumbnail_db, "images", "ID", GAVL_META_URI, path)) > 0))
    {
    sql = bg_sprintf("SELECT * FROM thumbnails WHERE PARENT = %"PRId64";", id);
    tn.tag = GAVL_META_COVER_URL;
    bg_sqlite_exec(mdb->thumbnail_db, sql, get_thumbnail_callback, &tn);
    free(sql);
    }
  
  /* Video poster */  
  if((path = gavl_dictionary_get_string_image_uri(tn.m, GAVL_META_POSTER_URL, 0, NULL, NULL, NULL)) &&
     ((id = bg_sqlite_string_to_id(mdb->thumbnail_db, "images", "ID", GAVL_META_URI, path)) > 0))
    {
    sql = bg_sprintf("SELECT * FROM thumbnails WHERE PARENT = %"PRId64";", id);
    tn.tag = GAVL_META_POSTER_URL;
    bg_sqlite_exec(mdb->thumbnail_db, sql, get_thumbnail_callback, &tn);
    free(sql);
    }
  
  /* Wallpaper */  
  if((path = gavl_dictionary_get_string_image_uri(tn.m, GAVL_META_WALLPAPER_URL, 0, NULL, NULL, NULL)) &&
     ((id = bg_sqlite_string_to_id(mdb->thumbnail_db, "images", "ID", GAVL_META_URI, path)) > 0))
    {
    sql = bg_sprintf("SELECT * FROM thumbnails WHERE PARENT = %"PRId64";", id);
    tn.tag = GAVL_META_WALLPAPER_URL;
    bg_sqlite_exec(mdb->thumbnail_db, sql, get_thumbnail_callback, &tn);
    free(sql);
    }
  pthread_mutex_unlock(&mdb->thumbnail_mutex);
  }

void bg_mdb_clear_thumbnail_uris(gavl_dictionary_t * track)
  {
  gavl_array_t * arr;
  
  gavl_dictionary_t * m = gavl_track_get_metadata_nc(track);

  if(!m)
    return;

  if((arr = gavl_dictionary_get_array_nc(m, GAVL_META_COVER_URL)))
    gavl_array_splice_val(arr, 1, -1, NULL);
  if((arr = gavl_dictionary_get_array_nc(m, GAVL_META_POSTER_URL)))
    gavl_array_splice_val(arr, 1, -1, NULL);
  if((arr = gavl_dictionary_get_array_nc(m, GAVL_META_WALLPAPER_URL)))
    gavl_array_splice_val(arr, 1, -1, NULL);
  }

/* Global funcs */

/* Delete thumbnails for a particular file */
void bg_mdb_cleanup_thumbnails(bg_mdb_t * mdb)
  {
  pthread_mutex_destroy(&mdb->thumbnail_mutex);

  sqlite3_close(mdb->thumbnail_db);
  }

static int create_tables(bg_mdb_t * mdb)
  {
  /* Object table */  
  if(!bg_sqlite_exec(mdb->thumbnail_db,
                     "CREATE TABLE IF NOT EXISTS thumbnails(ID INTEGER PRIMARY KEY, "
                     "PARENT INTEGER, "GAVL_META_WIDTH" INTEGER, "GAVL_META_HEIGHT" INTEGER, "META_MIMETYPE_ID" INTEGER, "GAVL_META_URI" TEXT);",
                     NULL, NULL) ||
     !bg_sqlite_exec(mdb->thumbnail_db,
                     "CREATE TABLE IF NOT EXISTS mimetypes(ID INTEGER PRIMARY KEY, "GAVL_META_MIMETYPE" TEXT);", NULL, NULL) ||
     !bg_sqlite_exec(mdb->thumbnail_db,
                     "CREATE TABLE IF NOT EXISTS images(ID INTEGER PRIMARY KEY, "GAVL_META_URI" TEXT);", NULL, NULL))
    return 0;
  return 1;
  }


/* Delete thumbnails for a particular file */
void bg_mdb_init_thumbnails(bg_mdb_t * mdb)
  {
  char * filename;
  int result;
  pthread_mutex_init(&mdb->thumbnail_mutex, NULL);

  filename = bg_sprintf("%s/thumbnails", mdb->path);
  bg_ensure_directory(filename, 0);
  free(filename);
  
  filename = bg_sprintf("%s/thumbnails/thumbs.sqlite", mdb->path);
  result = sqlite3_open(filename, &mdb->thumbnail_db);

  if(result)
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Cannot open database %s: %s", filename,
           sqlite3_errmsg(mdb->thumbnail_db));
    sqlite3_close(mdb->thumbnail_db);
    mdb->thumbnail_db = NULL;
    free(filename);
    return;
    }
  free(filename);

  if(!create_tables(mdb))
    return; // Should not happen if the path is writeable
  
  }
