/*   Cataract - Static web photo gallery generator
 *   Copyright (C) 2008 Tomas Bzatek <tbzatek@users.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 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>

#include <glib.h>

#include <config.h>

#include "setup.h"
#include "items.h"
#include "jpeg-utils.h"
#include "gallery-utils.h"
#include "replace-table.h"
#include "block-parser.h"
#include "stats.h"



/*  Returns newly allocated string  */
static gchar *
make_thumbnail_string (TGallerySetup *setup, unsigned int item_index, const gchar *imgname)
{
  gchar *s;
  gchar *num;

  s = g_strdup (setup->thumbnail_name_format);
  str_replace (&s, "%s", imgname);
  num = g_strdup_printf ("%.3d", item_index);
  str_replace (&s, "%d", num);
  g_free (num);

  return s;
}

/*  Get full image source path  */
/*  Returns newly allocated string  */
static gchar *
item_get_img_src (TGallerySetup *setup, TAlbum *items, TIndexItem *item)
{
  if (items->type == GALLERY_TYPE_INDEX) {
    if (item->thumbnail == NULL || strlen (item->thumbnail) == 0)
      return NULL;
    return g_path_get_basename (item->thumbnail);
  }
  else
  if (items->type == GALLERY_TYPE_ALBUM) {
    return g_path_get_basename ((item->path == NULL && item->preview) ? item->preview : item->path);
  }
  g_assert_not_reached ();
  return NULL;
}

/*  Get full thumbnail source path  */
/*  Returns newly allocated string  */
static gchar *
item_get_thumbnail_src (TGallerySetup *setup, TAlbum *items, TIndexItem *item, unsigned int item_index)
{
  gchar *s1, *ret;
  const gchar *gen_img_src;
  
  if (items->type == GALLERY_TYPE_INDEX) {
    if (item->thumbnail == NULL || strlen (item->thumbnail) == 0)
      return NULL;
    s1 = g_path_get_basename (item->thumbnail);
    ret = make_thumbnail_string (setup, item_index, s1);
    g_free (s1);
    return ret;
  }
  else
  if (items->type == GALLERY_TYPE_ALBUM) {
    gen_img_src = (item->path == NULL && item->preview) ? item->preview : item->path;
    s1 = g_path_get_basename ((item->thumbnail) ? item->thumbnail : gen_img_src);
    ret = make_thumbnail_string (setup, item_index, s1);
    g_free (s1);
    return ret;
  }
  g_assert_not_reached ();
  return NULL;
}



/*
 *     generate_image: generate needed image sizes
 */
gboolean
generate_image (TGallerySetup *setup,
                TAlbum *items,
                TIndexItem *item,
                unsigned int item_index,
                TPathInfo *path_info,
                gboolean update_when_necessary)
{
  unsigned long new_w, new_h;
  unsigned long thumb_w, thumb_h;
  gchar *thumb_dst;
  gchar *big_dst;
  gchar *big_src;
  gchar *orig_dst;
  gchar *img_src_full;
  gchar *thumb_src_full;
  gchar *s1;
  int bigq;
  gboolean res;
  gboolean source_img_portrait;


  thumb_src_full = NULL;
  img_src_full = NULL;
  orig_dst = NULL;

  if (items->type == GALLERY_TYPE_INDEX) {
    if (item->thumbnail == NULL || strlen (item->thumbnail) == 0)
      return FALSE;
    img_src_full = g_build_filename (path_info->src_dir, item->thumbnail, NULL);
    thumb_src_full = g_build_filename (path_info->src_dir, item->thumbnail, NULL);
  }
  else
  if (items->type == GALLERY_TYPE_ALBUM) {
    s1 = (item->path == NULL && item->preview) ? item->preview : item->path;
    thumb_src_full = (item->thumbnail) ? item->thumbnail : s1;
    img_src_full = g_build_filename (path_info->src_dir, s1, NULL);
    thumb_src_full = g_build_filename (path_info->src_dir, thumb_src_full, NULL);
  }

  /*  Make paths  */
  s1 = item_get_thumbnail_src (setup, items, item, item_index);
  thumb_dst = g_build_filename (path_info->dest_dir, setup->thumbnail_dir, s1, NULL);
  g_free (s1);
  s1 = item_get_img_src (setup, items, item);
  big_dst = g_build_filename (path_info->dest_dir, setup->img_big_dir, s1, NULL);
  if (item->force_fullsize || setup->override_nofullsize || (items->fullsize && ! item->force_nofullsize) ||
      (! item->force_nofullsize && ! items->nofullsize && ! setup->nofullsize))
     orig_dst = g_build_filename (path_info->dest_dir, setup->img_orig_dir, s1, NULL);
  res = (! update_when_necessary) || (! item->hidden && needs_update (thumb_src_full, thumb_dst)) ||
        (items->type == GALLERY_TYPE_ALBUM && (needs_update (img_src_full, big_dst) || (orig_dst && needs_update (img_src_full, orig_dst))));
  g_free (s1);

  /*  Do something when necessary  */
  if (res) {
    get_image_sizes (img_src_full, &new_w, &new_h);

    if (new_w > 0 && new_h > 0) {
      stats_images_inc ();
      source_img_portrait = new_h > new_w;

      /*  Generate thumbnail  */
      g_assert (thumb_src_full != NULL);
      get_image_sizes (thumb_src_full, &thumb_w, &thumb_h);

      if (thumb_w > 0 && thumb_h > 0) {
        if (! item->hidden) {
          /*  Squared thumbnails  */
          if (setup->squared_thumbnail_type != THUMBNAIL_SQUARE_TYPE_NONE) {
            thumb_w = setup->thumbnail_square_size;
            thumb_h = setup->thumbnail_square_size;
          } else
          /*  Portrait / Landscape thumbnails  */
          if (thumb_h > thumb_w)
            calculate_sizes (setup->thumbnail_portrait_width, setup->thumbnail_portrait_height, &thumb_w, &thumb_h);
          else
            calculate_sizes (setup->thumbnail_landscape_width, setup->thumbnail_landscape_height, &thumb_w, &thumb_h);
          if (! resize_image (thumb_src_full, thumb_dst, thumb_w, thumb_h, setup->thumbnail_quality, TRUE, setup->squared_thumbnail_type))
            log_error ("generate_image: error resizing thumbnail %s\n", thumb_src_full);
        }
      } else {
        log_error ("generate_image: thumbnail %s sizes are %lux%lu\n", thumb_src_full, thumb_w, thumb_h);
      }


      /*  Generate/copy preview image  */
      if (items->type == GALLERY_TYPE_ALBUM) {
        if (item->preview == NULL) {
          /*  No preview image supplied, generate it from original  */
          bigq = setup->preview_quality;
          if (items->quality > 0 && items->quality <= 100)
            bigq = items->quality;
          if (item->quality > 0 && item->quality <= 100)
            bigq = item->quality;

          if (item->width > 0 && item->height > 0) {
            calculate_sizes (item->width, item->height, &new_w, &new_h);
          } else {
            if (! source_img_portrait) {
              if (items->landscape_width > 0 && items->landscape_height > 0)
                calculate_sizes (items->landscape_width, items->landscape_height, &new_w, &new_h);
              else
                calculate_sizes (setup->preview_landscape_width, setup->preview_landscape_height, &new_w, &new_h);
            } else {
              if (items->portrait_width > 0 && items->portrait_height > 0)
                calculate_sizes (items->portrait_width, items->portrait_height, &new_w, &new_h);
              else
                calculate_sizes (setup->preview_portrait_width, setup->preview_portrait_height, &new_w, &new_h);
            }
          }

          g_assert (img_src_full != NULL);
          if (! resize_image (img_src_full, big_dst, new_w, new_h, bigq, FALSE, 0))
            log_error ("generate_image: error resizing big image %s\n", img_src_full);
        }
        else {
          /*  Copy the preview (big) image provided  */
          big_src = g_build_filename (path_info->src_dir, item->preview, NULL);
          if (! copy_file (big_src, big_dst))
            log_error ("generate_image: error copying preview image %s\n", big_src);
          g_free (big_src);
        }
        modify_exif (big_dst, setup->erase_exif_thumbnail, setup->add_copyright);


        /*  Generate/copy original image  */
        if (orig_dst) {
          if (! copy_file (img_src_full, orig_dst))
            log_error ("generate_image: error copying original image %s\n", img_src_full);
          else
            modify_exif (orig_dst, setup->erase_exif_thumbnail, setup->add_copyright);
        }
      }
    }
  }
  g_free (img_src_full);
  g_free (thumb_src_full);
  g_free (big_dst);
  g_free (thumb_dst);
  g_free (orig_dst);

  return res;
}



/*
 *   write_html_album: process album and index template files
 *
 *      template_src  = template file of the album/index
 *      dst           = save generated file as
 *      items         = array of items in the album/index
 *
 */
gboolean
write_html_album (TGallerySetup *setup,
                  TPathInfo *path_info,
                  const gchar *template_src,
                  const gchar *dst,
                  TAlbum *items)
{
  FILE *fin;
  FILE *fout;
  gchar *line;
  gchar *block;
  gchar *s1, *s2, *s3, *s4, *s5;
  TAlbum *parent;
  TIndexItem *item;
  TIndexItem *tmp_item;
  int level, old_parent_item_index;
  gboolean res;
  int bb;
  int i;
  unsigned int real_total_items;
  unsigned long img_thumb_w, img_thumb_h;
  ReplaceTable *global_replace_table;
  ReplaceTable *local_replace_table;
  BlockParser *block_parser;


  fin = fopen (template_src, "r");
  if (fin == NULL) {
    log_error ("write_html_index: error reading file \"%s\": %s\n", template_src, strerror (errno));
    return FALSE;
  }
  fout = fopen (dst, "w");
  if (fout == NULL) {
    log_error ("write_html_index: error writing to file \"%s\": %s\n", dst, strerror (errno));
    fclose (fin);
    return FALSE;
  }

  res = TRUE;

  global_replace_table = replace_table_new ();
  block_parser = block_parser_new ();

  /*  Get number of real pictures in the list  */
  real_total_items = 0;
  for (i = 0; i < items->items->len; i++) {
    tmp_item = g_ptr_array_index (items->items, i);
    if (tmp_item->type == INDEX_ITEM_TYPE_PICTURE && (! tmp_item->hidden))
      real_total_items++;
  }
  replace_table_add_key_int (global_replace_table, "TOTAL_ITEMS", real_total_items);

  /*  Page title  */
  if (items->parent_index == NULL || setup->site_title == NULL)
    s1 = g_strdup (setup->site_title ? setup->site_title : items->ID);
  else
    s1 = g_strdup_printf ("%s | %s", items->title, setup->site_title);
  replace_table_add_key (global_replace_table, "PAGE_TITLE", s1);
  g_free (s1);

  /*  Simple placeholders  */
  replace_table_add_key (global_replace_table, "ID", items->ID);
  replace_table_add_key (global_replace_table, "TITLE", items->title);
  replace_table_add_key (global_replace_table, "DESCRIPTION", items->desc);
  replace_table_add_key (global_replace_table, "FOOTNOTE", items->footnote);
  replace_table_add_key (global_replace_table, "FOOTER", setup->footer);

  /*  Navigation bar  (NOTE: 'int level' is used below + favicon)  */
  s1 = g_strdup (items->ID);
  old_parent_item_index = items->parent_item_index + 1;
  parent = items->parent_index;
  level = 1;
  if (parent) {
    tmp_item = g_ptr_array_index (parent->items, old_parent_item_index - 1);
    if (tmp_item && tmp_item->path && strlen (tmp_item->path) > 0)
      level += count_dir_levels (tmp_item->path) - 1;
  }
  /*  Go Up string  */
  s3 = make_string ("../", level);
  s2 = setup->use_inpage_links ? g_strdup_printf ("#i%d", old_parent_item_index) : g_strdup ("");
  replace_table_add_key_printf (global_replace_table, "GO_UP_LINK", "%s%s%s", s3, setup->index_file_name, s2);
  g_free (s2);
  g_free (s3);

  while (parent) {
    s3 = make_string ("../", level);
    s4 = g_strdup (parent->ID);
    s5 = setup->use_inpage_links ? g_strdup_printf ("#i%d", old_parent_item_index) : g_strdup ("");
    s2 = g_strdup_printf ("<a href=\"%s%s%s\">%s</a> &gt; %s", s3, setup->index_file_name, s5, s4, s1);
    g_free (s3);
    g_free (s1);
    g_free (s4);
    g_free (s5);
    s1 = s2;
    old_parent_item_index = parent->parent_item_index + 1;
    parent = parent->parent_index;
    level++;
    if (parent) {
      tmp_item = g_ptr_array_index (parent->items, old_parent_item_index - 1);
      if (tmp_item && tmp_item->path && strlen (tmp_item->path) > 0)
        level += count_dir_levels (tmp_item->path) - 1;
    }
  }
  replace_table_add_key (global_replace_table, "NAV_BAR", s1);
  g_free (s1);

  /*  Supportfiles path  */
  s1 = make_string ("../", level - 1);
  replace_table_add_key (global_replace_table, "TEMPLATES_PATH", setup->support_files_use_common_root ? s1 : "");
  g_free (s1);

  /*  META tags  */
  s1 = g_strdup_printf ("\t<meta name=\"generator\" content=\"%s v%s\" />\n", APP_NAME_FULL, VERSION);
  if (setup->meta_author || items->meta_author) {
    s3 = g_strdup (items->meta_author ? items->meta_author : setup->meta_author);
    adjust_tags_parameter (&s3);
    s2 = g_strdup_printf ("%s\t<meta name=\"author\" content=\"%s\" />\n", s1, s3);
    g_free (s3);
    g_free (s1);
    s1 = s2;
  }
  if (setup->meta_description || items->meta_description) {
    s3 = g_strdup (items->meta_description ? items->meta_description : setup->meta_description);
    adjust_tags_parameter (&s3);
    s2 = g_strdup_printf ("%s\t<meta name=\"description\" content=\"%s\" />\n", s1, s3);
    g_free (s3);
    g_free (s1);
    s1 = s2;
  }
  if (setup->meta_keywords || items->meta_keywords) {
    s3 = g_strdup (items->meta_keywords ? items->meta_keywords : setup->meta_keywords);
    adjust_tags_parameter (&s3);
    s2 = g_strdup_printf ("%s\t<meta name=\"keywords\" content=\"%s\" />\n", s1, s3);
    g_free (s3);
    g_free (s1);
    s1 = s2;
  }
  if (setup->favicon_file && strlen (setup->favicon_file) > 0) {
    s3 = make_string ("../", level - 1);
    if (setup->favicon_type)
      s2 = g_strdup_printf ("%s\t<link rel=\"icon\" type=\"%s\" href=\"%s%s\" />\n", s1, setup->favicon_type, setup->support_files_use_common_root ? s3 : "", setup->favicon_file);
    else
      s2 = g_strdup_printf ("%s\t<link rel=\"icon\" href=\"%s%s\" />\n", s1, setup->support_files_use_common_root ? s3 : "", setup->favicon_file);
    g_free (s1);
    g_free (s3);
    s1 = s2;
  }
  replace_table_add_key (global_replace_table, "CGG_META_TAGS", s1);
  g_free (s1);

  /*  Atom feeds  */
  if (setup->feed_enabled) {
    s3 = make_string ("../", level - 1);
    s1 = g_strdup_printf ("\t<link href=\"%s%s\" type=\"application/atom+xml\" rel=\"alternate\" title=\"%s\" />\n", s3, setup->feed_filename, setup->feed_title);
    g_free (s3);
    replace_table_add_key (global_replace_table, "CGG_ATOM_FEED_TAGS", s1);
    g_free (s1);
  }


  /*  Setup block parser  */
  block_parser_register_key (block_parser, "IMG_LIST", "IMG_LIST");
  block_parser_register_key (block_parser, "IMG_LIST_LANDSCAPE", NULL);
  block_parser_register_key (block_parser, "IMG_LIST_PORTRAIT", NULL);
  block_parser_register_key (block_parser, "IMG_LIST_SQUARED", NULL);
  block_parser_register_key (block_parser, "LIST_SEPARATOR", NULL);
  block_parser_register_key (block_parser, "LIST_INTERSPACE", NULL);
  block_parser_register_key (block_parser, "GO_UP", "GO_UP");


  /*   Read through the template and replace placeholders with real data  */
  while (! feof (fin)) {
    line = block_parser_read_and_parse (block_parser, fin);
    if (line == NULL)
      break;

    /*  Blocks  */
    if (block_parser_has_unused_data (block_parser, "GO_UP")) {
      block = block_parser_get_data (block_parser, "GO_UP");
      if (block) {
        replace_table_process (&block, global_replace_table);
        replace_table_add_key (global_replace_table, "GO_UP", items->parent_index && setup->show_go_up ? block : "");
      }
      g_free (block);
    }

    if (block_parser_has_unused_data (block_parser, "IMG_LIST")) {
      block = g_strdup ("");

      /*  Now we have all block placeholders read, generate the items:  */
      for (i = 0; i < items->items->len; i++) {
        item = g_ptr_array_index (items->items, i);
        if (item == NULL) {
          log_error ("write_html_index: error getting item %d\n", i);
          continue;
        }

        /*  Generate images (preview, original, thumbnail)  */
        local_replace_table = replace_table_new ();
        s1 = NULL;

        switch (item->type) {
          case INDEX_ITEM_TYPE_PICTURE:
            /*  Skip HTML code generation if it's a hidden item  */
            if (! item->hidden) {
              s2 = item_get_thumbnail_src (setup, items, item, i);
              if (s2 != NULL) {
                s4 = g_build_filename (path_info->dest_dir, setup->thumbnail_dir, s2, NULL);
                get_image_sizes (s4, &img_thumb_w, &img_thumb_h);
                g_free (s4);
                s1 = (img_thumb_h > img_thumb_w) ? "IMG_LIST_PORTRAIT" : "IMG_LIST_LANDSCAPE";
              }

              if (setup->squared_thumbnail_type != THUMBNAIL_SQUARE_TYPE_NONE)
                s1 = "IMG_LIST_SQUARED";
              if (! s1)
                s1 = "IMG_LIST_LANDSCAPE";  /*  fallback case  */

              s1 = block_parser_get_data (block_parser, s1);
              if (s2 != NULL) {
                replace_table_add_key_printf (local_replace_table, "IMG_THUMBNAIL", "%s/%s", setup->thumbnail_dir, s2);
                replace_table_add_key_int (local_replace_table, "IMG_SIZE_THUMB_W", img_thumb_w);
                replace_table_add_key_int (local_replace_table, "IMG_SIZE_THUMB_H", img_thumb_h);
              }
              g_free (s2);
              replace_table_add_key_printf (local_replace_table, "ALBUM_SUBPATH", "%s/%s", item->path, setup->index_file_name);
              s3 = item_get_img_src (setup, items, item);
              replace_table_add_key_printf (local_replace_table, "IMG_SUBPAGE", "%s%s", s3, GET_EXT (setup->index_file_name));
              replace_table_add_key (local_replace_table, "IMG_FILENAME", s3);
              g_free (s3);
              replace_table_add_key (local_replace_table, "IMG_TITLE", item->title);
              replace_table_add_key (local_replace_table, "IMG_DESCRIPTION", item->title_description);
              replace_table_add_key_printf (local_replace_table, "IMG_LIST_ID", "i%d", i + 1);
              if (items->type == GALLERY_TYPE_INDEX) {
                s3 = g_build_filename (path_info->src_dir, item->path, "index.xml", NULL);
                replace_table_add_key_int (local_replace_table, "ALBUM_NUM_ITEMS", get_album_objects_count (s3));
                g_free (s3);
              }
            }
            break;

          case INDEX_ITEM_TYPE_SEPARATOR:
            s1 = block_parser_get_data (block_parser, "LIST_SEPARATOR");
            replace_table_add_key (local_replace_table, "LIST_SEPARATOR_TITLE", item->title);
            break;

          case INDEX_ITEM_TYPE_INTERSPACE:
            s1 = block_parser_get_data (block_parser, "LIST_INTERSPACE");
            replace_table_add_key (local_replace_table, "LIST_INTERSPACE_TITLE", item->title);
            break;
        }

        if (s1) {
          replace_table_process (&s1, local_replace_table);
          s2 = g_strdup_printf ("%s%s", block, s1);
          g_free (block);
          block = s2;
          g_free (s1);
        }
        replace_table_free (local_replace_table);
      }
      replace_table_process (&block, global_replace_table);
      replace_table_add_key (global_replace_table, "IMG_LIST", block);
      g_free (block);
    }

    /*  Replace all known tags  */
    replace_table_process (&line, global_replace_table);

    /*  Write to file  */
    bb = fputs (line, fout);
    g_free (line);
    if (bb < 0) {
      log_error ("write_html_index: error writing to file \"%s\": %s\n", dst, strerror (errno));
      res = FALSE;
      break;
    }
  }

  fclose (fout);
  fclose (fin);
  replace_table_free (global_replace_table);
  block_parser_free (block_parser);
  return res;
}


/*
 *   write_html_image: process single image template file
 *
 *      template_src  = template file of the album/index
 *      dst           = save generated file as
 *      item          = data for the current item
 *      parent_items  = array of items in the album, to determine our position and make links to previous/next image
 *
 */
gboolean
write_html_image (TGallerySetup *setup,
                  TPathInfo *path_info,
                  const gchar *template_src,
                  const gchar *dst,
                  TIndexItem *item,
                  TAlbum *parent_items)
{
  FILE *fin;
  FILE *fout;
  gchar *big_dst;
  gchar *orig_dst;
  TExifData *exif;
  unsigned long img_big_w, img_big_h, img_orig_w, img_orig_h;
  unsigned int item_index, real_item_index, real_total_items;
  TIndexItem *previous_item;
  TIndexItem *next_item;
  TIndexItem *tmp_item;
  TAlbum *parent;
  int i;
  gchar *line;
  gchar *block;
  gchar *s1, *s2, *s3, *s4, *s5;
  gchar *imgname, *preload_imgname;
  gchar *title, *title_desc;
  gboolean res;
  int bb;
  int level, old_parent_item_index;
  gboolean override_title_meta;
  gboolean image_fullsize;
  ReplaceTable *replace_table;
  BlockParser *block_parser;


  fin = fopen (template_src, "r");
  if (fin == NULL) {
    log_error ("write_html_image: error reading file \"%s\": %s\n", template_src, strerror (errno));
    return FALSE;
  }
  fout = fopen (dst, "w");
  if (fout == NULL) {
    log_error ("write_html_image: error writing to file \"%s\": %s\n", dst, strerror (errno));
    fclose (fin);
    return FALSE;
  }

  preload_imgname = NULL;

  replace_table = replace_table_new ();
  block_parser = block_parser_new ();

  /*  Get our index in the album  */
  item_index = 0;
  real_item_index = 0;
  real_total_items = 0;
  for (i = 0; i < parent_items->items->len; i++) {
    tmp_item = g_ptr_array_index (parent_items->items, i);
    if (tmp_item->type == INDEX_ITEM_TYPE_PICTURE) {
      if (! item_index)  real_item_index++;
      real_total_items++;
    }
    if (tmp_item == item)
      item_index = i + 1;
  }

  /*  Get previous and next items  */
  previous_item = NULL;
  next_item = NULL;
  for (i = item_index - 2; i >= 0 && (previous_item == NULL || previous_item->type != INDEX_ITEM_TYPE_PICTURE); i--)
    previous_item = g_ptr_array_index (parent_items->items, i);
  if (previous_item && previous_item->type != INDEX_ITEM_TYPE_PICTURE)
    previous_item = NULL;
  for (i = item_index; i < parent_items->items->len && (next_item == NULL || next_item->type != INDEX_ITEM_TYPE_PICTURE); i++)
    next_item = g_ptr_array_index (parent_items->items, i);
  if (next_item && next_item->type != INDEX_ITEM_TYPE_PICTURE)
    next_item = NULL;

  /*  Paths setup  */
  imgname = g_path_get_basename ((item->path == NULL && item->preview) ? item->preview : item->path);
  if (next_item && setup->preload) {
    s1 = g_path_get_basename ((next_item->path == NULL && next_item->preview) ? next_item->preview : next_item->path);
    preload_imgname = g_build_filename (setup->img_big_dir, s1, NULL);
    g_free (s1);
  }
  big_dst = g_build_filename (path_info->dest_dir, setup->img_big_dir, imgname, NULL);
  orig_dst = g_build_filename (path_info->dest_dir, setup->img_orig_dir, imgname, NULL);
  res = TRUE;

  /*  Get EXIF data from the original image  */
  s1 = g_build_filename (path_info->src_dir, (item->path == NULL && item->preview) ? item->preview : item->path, NULL);
  exif = get_exif (s1);
  g_free (s1);
  if (exif == NULL)
    log_error ("write_html_image: error getting exif data from file \"%s\"\n", orig_dst);

  /*  Retrieve image sizes of preview and original image  */
  get_image_sizes (big_dst, &img_big_w, &img_big_h);
  image_fullsize = item->force_fullsize || setup->override_nofullsize ||
                   (parent_items->fullsize && ! item->force_nofullsize) ||
                   (! item->force_nofullsize && ! parent_items->nofullsize && ! setup->nofullsize);
  if (image_fullsize)
    get_image_sizes (orig_dst, &img_orig_w, &img_orig_h);

  /*  Get title and description from IPTC/EXIF/JPEG if not defined  */
  title = g_strdup (item->title);
  title_desc = g_strdup (item->title_description);
  if (setup->use_iptc_exif && title == NULL && title_desc == NULL) {
    if (exif->iptc_caption)
      title = g_strdup (exif->iptc_caption);
    if (exif->jpeg_comment) {
      if (! title)
        title = g_strdup (exif->jpeg_comment);
      else
        title_desc = g_strdup (exif->jpeg_comment);
    }
    if (exif->exif_imgdesc) {
      if (! title)
        title = g_strdup (exif->exif_imgdesc);
/*        if (! title_desc)   -- disabled
        title_desc = g_strdup (exif->exif_imgdesc);  */
    }
    if (exif->exif_usercomment) {
      if (! title)
        title = g_strdup (exif->exif_usercomment);
      if (! title_desc)
        title_desc = g_strdup (exif->exif_usercomment);
    }
    /*  Convert line breaks to be visible in the HTML code  */
    if (title) {
      str_replace (&title, "\r\n", "<br />");
      str_replace (&title, "\n", "<br />");
    }
    if (title_desc) {
      str_replace (&title_desc, "\r\n", "<br />");
      str_replace (&title_desc, "\n", "<br />");
    }
  }
  if (title)
    title = g_strstrip (title);
  if (title_desc)
    title_desc = g_strstrip (title_desc);

  /*  Page title  */
  s1 = (title && strlen (title) > 0) ? g_strdup_printf ("%s | ", title) : NULL;
  s2 = g_strdup_printf ("%s [%d/%d]", parent_items->title ? parent_items->title : parent_items->ID, real_item_index, real_total_items);
  s3 = setup->site_title ? g_strdup_printf (" | %s", setup->site_title) : NULL;
  s4 = g_strconcat (s1 ? s1 : "", s2, s3 ? s3 : "", NULL);
  replace_table_add_key (replace_table, "PAGE_TITLE", s4);
  g_free (s1);
  g_free (s2);
  g_free (s3);
  g_free (s4);

  /*  Simple placeholders  */
  replace_table_add_key (replace_table, "FILE_NAME", imgname);
  replace_table_add_key (replace_table, "TITLE", title);
  replace_table_add_key (replace_table, "DESCRIPTION", title_desc);
  replace_table_add_key (replace_table, "FOOTER", setup->footer);
  replace_table_add_key_int (replace_table, "TOTAL_ITEMS", real_total_items);
  replace_table_add_key_int (replace_table, "FILE_NO", real_item_index);
  replace_table_add_key_int (replace_table, "IMG_SIZE_BIG_W", img_big_w);
  replace_table_add_key_int (replace_table, "IMG_SIZE_BIG_H", img_big_h);
  replace_table_add_key_int (replace_table, "IMG_SIZE_ORIG_W", img_orig_w);
  replace_table_add_key_int (replace_table, "IMG_SIZE_ORIG_H", img_orig_h);
  replace_table_add_key_printf (replace_table, "IMG_SRC_BIG", "%s/%s", setup->img_big_dir, imgname);
  replace_table_add_key_printf (replace_table, "IMG_SRC_FULL", "%s/%s", setup->img_orig_dir, imgname);
  replace_table_add_key (replace_table, "IMG_SRC_PRELOAD", preload_imgname ? preload_imgname : "");

  /*  Navigation bar  (NOTE: 'int level' is used below + favicon)  */
  s1 = g_strdup (imgname);
  parent = parent_items;
  old_parent_item_index = -1;
  level = 0;
  while (parent) {
    s3 = make_string ("../", level);
    s4 = g_strdup (parent->ID);
    s5 = setup->use_inpage_links ? g_strdup_printf ("#i%d", parent == parent_items ? item_index : old_parent_item_index) : g_strdup ("");
    s2 = g_strdup_printf ("<a href=\"%s%s%s\">%s</a> &gt; %s", s3, setup->index_file_name, s5, s4, s1);
    g_free (s3);
    g_free (s1);
    g_free (s4);
    g_free (s5);
    s1 = s2;
    old_parent_item_index = parent->parent_item_index + 1;
    parent = parent->parent_index;
    level++;
    if (parent) {
      tmp_item = g_ptr_array_index (parent->items, old_parent_item_index - 1);
      if (tmp_item && tmp_item->path && strlen (tmp_item->path) > 0)
        level += count_dir_levels (tmp_item->path) - 1;
    }
  }
  replace_table_add_key (replace_table, "NAV_BAR", s1);
  g_free (s1);

  /*  Supportfiles path  */
  s1 = make_string ("../", level - 1);
  replace_table_add_key (replace_table, "TEMPLATES_PATH", setup->support_files_use_common_root ? s1 : "");
  g_free (s1);

  /*  EXIF values  */
  replace_table_add_key (replace_table, "EXIF_ISO", exif->iso ? exif->iso : "??");
  replace_table_add_key (replace_table, "EXIF_TIME", exif->exposure ? exif->exposure : "??");
  replace_table_add_key (replace_table, "EXIF_APERTURE", exif->aperture ? exif->aperture : "??");
  replace_table_add_key (replace_table, "EXIF_FOCAL_LENGTH", exif->focal_length ? exif->focal_length : "??");
  replace_table_add_key (replace_table, "EXIF_FLASH", exif->flash ? exif->flash : "??");
  replace_table_add_key (replace_table, "EXIF_DATE", exif->datetime ? exif->datetime : "??");
  replace_table_add_key (replace_table, "EXIF_CAMERA_MODEL", exif->camera_model ? exif->camera_model : "??");
  s1 = g_strdup_printf ("(%s)", exif->focal_length_35mm);
  replace_table_add_key (replace_table, "EXIF_FOCAL_35", exif->focal_length_35mm ? s1 : "");
  g_free (s1);

  /*  Border style  */
  s1 = item->border_style;
  if (s1 == NULL)
    s1 = parent_items->border_style;
  if (s1 == NULL)
    s1 = setup->border_style;
  if (s1 == NULL)
    s1 = "border_single";
  replace_table_add_key (replace_table, "IMG_BORDER_STYLE", s1);

  /*  Next/Previous links  */
  if (next_item) {
    s2 = g_path_get_basename ((next_item->path == NULL && next_item->preview) ? next_item->preview : next_item->path);
    replace_table_add_key_printf (replace_table, "LINK_NEXT", "%s%s", s2, GET_EXT (setup->index_file_name));
    g_free (s2);
  }
  else
    replace_table_add_key (replace_table, "LINK_NEXT", setup->index_file_name);
  if (previous_item) {
    s2 = g_path_get_basename ((previous_item->path == NULL && previous_item->preview) ? previous_item->preview : previous_item->path);
    replace_table_add_key_printf (replace_table, "LINK_PREV", "%s%s", s2, GET_EXT (setup->index_file_name));
    g_free (s2);
  }
  else
    replace_table_add_key (replace_table, "LINK_PREV", setup->index_file_name);

  /*  META tags  */
  override_title_meta = setup->use_title_as_meta && title && (strlen (title) > 0);
  s1 = g_strdup_printf ("\t<meta name=\"generator\" content=\"Cataract Gallery Generator v%s\" />\n", VERSION);
  if (setup->meta_author || parent_items->meta_author) {
    s3 = g_strdup (parent_items->meta_author ? parent_items->meta_author : setup->meta_author);
    adjust_tags_parameter (&s3);
    s2 = g_strdup_printf ("%s\t<meta name=\"author\" content=\"%s\" />\n", s1, s3);
    g_free (s3);
    g_free (s1);
    s1 = s2;
  }
  if (setup->meta_description || parent_items->meta_description || override_title_meta) {
    s3 = g_strdup (override_title_meta ? title : (parent_items->meta_description ? parent_items->meta_description : setup->meta_description));
    adjust_tags_parameter (&s3);
    s2 = g_strdup_printf ("%s\t<meta name=\"description\" content=\"%s\" />\n", s1, s3);
    g_free (s3);
    g_free (s1);
    s1 = s2;
  }
  if ((setup->meta_keywords || parent_items->meta_keywords) && (! override_title_meta)) {
    s3 = g_strdup (parent_items->meta_keywords ? parent_items->meta_keywords : setup->meta_keywords);
    adjust_tags_parameter (&s3);
    s2 = g_strdup_printf ("%s\t<meta name=\"keywords\" content=\"%s\" />\n", s1, s3);
    g_free (s3);
    g_free (s1);
    s1 = s2;
  }
  if (setup->favicon_file && strlen (setup->favicon_file) > 0) {
    s3 = make_string ("../", level - 1);
    if (setup->favicon_type)
      s2 = g_strdup_printf ("%s\t<link rel=\"icon\" type=\"%s\" href=\"%s%s\" />\n", s1, setup->favicon_type, setup->support_files_use_common_root ? s3 : "", setup->favicon_file);
    else
      s2 = g_strdup_printf ("%s\t<link rel=\"icon\" href=\"%s%s\" />\n", s1, setup->support_files_use_common_root ? s3 : "", setup->favicon_file);
    g_free (s1);
    g_free (s3);
    s1 = s2;
  }
  replace_table_add_key (replace_table, "CGG_META_TAGS", s1);
  g_free (s1);

  /*  Atom feeds  */
  if (setup->feed_enabled) {
    s3 = make_string ("../", level - 1);
    s1 = g_strdup_printf ("\t<link href=\"%s%s\" type=\"application/atom+xml\" rel=\"alternate\" title=\"%s\" />\n", s3, setup->feed_filename, setup->feed_title);
    g_free (s3);
    replace_table_add_key (replace_table, "CGG_ATOM_FEED_TAGS", s1);
    g_free (s1);
  }


  /*  Setup block parser  */
  block_parser_register_key (block_parser, "IMG_FULLSIZE_LINK", "IMG_FULLSIZE_LINK");
  block_parser_register_key (block_parser, "EXIF_TABLE", "EXIF_TABLE");


  /*   Read through the template and replace placeholders with real data  */
  while (! feof (fin)) {
    line = block_parser_read_and_parse (block_parser, fin);
    if (line == NULL)
      break;

    /*  Blocks  */
    if (block_parser_has_unused_data (block_parser, "IMG_FULLSIZE_LINK")) {
      block = block_parser_get_data (block_parser, "IMG_FULLSIZE_LINK");
      if (block) {
        replace_table_process (&block, replace_table);
        replace_table_add_key (replace_table, "IMG_FULLSIZE_LINK", image_fullsize ? block : "");
      }
      g_free (block);
    }
    if (block_parser_has_unused_data (block_parser, "EXIF_TABLE")) {
      block = block_parser_get_data (block_parser, "EXIF_TABLE");
      if (block) {
        replace_table_process (&block, replace_table);
        replace_table_add_key (replace_table, "EXIF_TABLE", setup->show_exif_table ? block : "");
      }
      g_free (block);
    }

    /*  Replace all known tags  */
    replace_table_process (&line, replace_table);

    /*  Write to file  */
    bb = fputs (line, fout);
    g_free (line);
    if (bb < 0) {
      log_error ("write_html_image: error writing to file \"%s\": %s\n", dst, strerror (errno));
      res = FALSE;
      break;
    }
  }

  fclose (fout);
  fclose (fin);
  g_free (title);
  g_free (title_desc);
  g_free (big_dst);
  g_free (orig_dst);
  g_free (imgname);
  g_free (preload_imgname);
  free_exif_data (exif);
  replace_table_free (replace_table);
  block_parser_free (block_parser);
  return res;
}

