<?php
/**
 * @file
 * Funktionen für den Aufbau der Bildergalerie.
 *
 * @author Jan Dittberner <jan@dittberner.info>
 * @version \$Id$
 *
 * Copyright (c) 2007, 2008, 2009 Jan Dittberner <jan@dittberner.info>
 * Jan Dittberner IT-Consulting & -Solutions,
 * Cottbuser Str. 1, D-01129 Dresden
 *
 * This file is part of the ScrollingJQueryGallery component of the
 * gnuviech-server.de Websitetools
 *
 * ScrollingJQueryGallery 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 3 of the
 * License, or (at your option) any later version.
 *
 * ScrollingJQueryGallery 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 ScrollingJQueryGallery.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

define(IMAGESEC, "images");
define(GALLERYSEC, "gallery");
define(GALLERY_RE, '/^[\w\d _-]+$/');

define(SCALE_WIDTH, 0);
define(SCALE_HEIGHT, 1);
define(SCALE_BOTH, 2);

$basedir = realpath(implode(DIRECTORY_SEPARATOR, array(dirname(__file__), '..')));
$inifile = implode(DIRECTORY_SEPARATOR, array($basedir, 'gallery.ini'));

if (!file_exists($inifile)) {
  die(sprintf(_("required %s not found."), $inifile));
}
$configuration = parse_ini_file($inifile);
if (array_key_exists('logfile', $configuration)) {
  error_reporting(E_ALL);
  ini_set('display_errors', 0);
  ini_set('log_errors', 1);
  ini_set('error_log', $configuration['logfile']);
}

require_once('theme.class.php');

if (array_key_exists('theme', $_GET) &&
    preg_match('/^[a-zA-Z0-9_-]+$/', $_GET['theme'])) {
  $theme = new Theme($_GET['theme'], $configuration);
} else {
  $theme = new Theme($configuration['defaulttheme'], $configuration);
}

/**
 * Prüft, ob eine Galerie mit dem übergebenen Namen existiert.
 *
 * @param $galleryname Galeriename
 *
 * @return @c true, wenn die Galerie existiert und einen erlaubten
 * Verzeichnisnamen hat, sonst @c false
 */
function galleryExists($galleryname) {
  global $configuration;

  return preg_match(GALLERY_RE, $galleryname) &&
    realpath($configuration['gallerydir'] . DIRECTORY_SEPARATOR . $galleryname);
}

/**
 * Liefert geparste Konfigurationsinformationen zu einer Galerie oder
 * die globale Konfiguration. Die Konfigurationsdaten einer Galerie
 * stehen in der Datei @c galleryinfo.ini in dem Verzeichnis der
 * Galerie. Die globale Konfiguration liegt in der gleichnamigen Datei
 * im Elternverzeichnis der Galerien.
 *
 * @param $galleryname Galeriename oder @c null, wenn die globale
 * Konfiguration geparst werden soll
 *
 * @return Ergebnis von @c parse_ini_file() oder ein leeres Array,
 * wenn keine Konfigurationsdatei vorhanden ist
 */
function getGalleryConfig($galleryname = null) {
  global $configuration;

  if ($galleryname) {
    $filepath = realpath(implode(DIRECTORY_SEPARATOR,
      array($configuration['gallerydir'], $galleryname,
            'galleryinfo.ini')));
  } else {
    $filepath = realpath(implode(DIRECTORY_SEPARATOR,
      array($configuration['gallerydir'], 'galleryinfo.ini')));
  }
  if (is_file($filepath)) {
    return parse_ini_file($filepath, true);
  }
  return array();
}

/**
 * Holt die Bildinformationen zu einem Bild.
 *
 * @param $imagename Bildname
 *
 * @param $galleryname Galleriename
 *
 * @return assoziatives Array mit folgenden Feldern
 * @li @a name Bildname
 * @li @a data Label des Bildes
 * @li @a preview relative URL des Vorschaubildes
 * @li @a full relative URL des Vollbildes
 */
function getImageInfo($galleryname, $imagename) {
  global $theme, $configuration;

  $label = getImageLabel($galleryname, $imagename);
  $gallerylabel = getGalleryLabel($galleryname);
  $scaledimage = getScaledImage($galleryname, $imagename,
                                $theme->previewsize, SCALE_BOTH);
  $scaledsize = getimagesize($configuration['gallerydir'] .
                             DIRECTORY_SEPARATOR . $scaledimage);  
  return array("name" => $imagename,
               "label" => $label,
               "preview" => array($configuration['gallerypath'] . '/' .
                                  $scaledimage, $scaledsize[0], $scaledsize[1]),
               "full" => implode('/', array($configuration['gallerypath'],
                                            $galleryname, $imagename)),
               "title" => sprintf("%s :: %s", $gallerylabel, $label)
               );
}

/**
 * Liefert das Label zu einem Bild. Wenn es in der @a images-Sektion
 * der Datei @c galleryinfo.ini der angegebenen Galerie einen Eintrag
 * mit dem übergebenen Bildnamen gibt, wird dieser zurückgegeben,
 * ansonsten der Bildname selbst.
 *
 * @param $galleryname Galeriename
 *
 * @param $imagename Bildname
 *
 * @return Label zu dem Bild
 */
function getImageLabel($galleryname, $imagename) {
  global $configuration;

  $gallerypath = realpath($configuration['gallerydir'] . DIRECTORY_SEPARATOR . $galleryname);
  if (empty($gallerypath) || !is_dir($gallerypath)) {
    return false;
  }
  $filepath = $gallerypath . DIRECTORY_SEPARATOR . $imagename;
  if (!is_file($filepath)) {
    return false;
  }
  $inidata = getGalleryConfig($galleryname);
  $value = $inidata[IMAGESEC][$imagename];
  if ($value) {
    return $value;
  }
  return $imagename;
}

/**
 * Liefert das Label der Galerie. Das Label wird aus dem Wert @a title
 * der Sektion @a gallery der @c galleryinfo.ini der Galerie
 * geholt. Falls dieser Wert nicht definiert ist, wird der Name der
 * Galerie zurückgegeben.
 *
 * @param $galleryname Galeriename
 *
 * @return Label der Galerie
 */
function getGalleryLabel($galleryname) {
  $inidata = getGalleryConfig($galleryname);
  if ($inidata[GALLERYSEC]['title']) {
    $label = $inidata[GALLERYSEC]['title'];
  } else {
    $label = $galleryname;
  }
  return $label;
}

function cmpGalleryByWeight($a, $b) {
  if (($a[0] == $b[0]) && (strcmp($a[1], $b[1]) == 0)) {
    return 0;
  }
  if ($a[0] == $b[0]) {
    return strcmp($a[1], $b[1]);
  }
  return ($a[0] < $b[0]) ? -1 : 1;
}

function getOrderedGalleries() {
  global $configuration;
  $galleries = array();

  foreach (glob(realpath($configuration['gallerydir']) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR) as $directory) {
    $basename = basename($directory);
    if (galleryExists($basename)) {
      $inidata = getGalleryConfig($basename);
      $weight = (array_key_exists('weight', $inidata[GALLERYSEC])) ?
        intval($inidata[GALLERYSEC]['weight']) : 0;
      $galleries[] = array($weight, $basename);
    }
  }
  usort($galleries, 'cmpGalleryByWeight');
  return $galleries;
}

/**
 * Liefert die aktuelle Galerie. Die Galerie kann entweder im
 * GET-Parameter @c galleryname stehen, als Wert @a default in der
 * Sektion @a gallery der zentralen @c galleryinfo.ini angegeben
 * werden oder es wird das erste Unterverzeichnis von @c gallerydir
 * verwendet.
 *
 * @return Galeriename
 */
function getCurrentGallery() {
  global $configuration;

  if (array_key_exists('galleryname', $_GET) &&
      galleryExists($_GET["galleryname"])) {
    return $_GET["galleryname"];
  }
  $galleries = getOrderedGalleries();
  return (count($galleries) > 0) ? $galleries[0][1] : null;
}

/**
 * Liefert eine skalierte Version zu dem übergebenen Bild der
 * übergebenen Galerie und generiert dieses bei Bedarf.
 *
 * @param $galleryname Galeriename
 *
 * @param $basename Bildname
 *
 * @param $maxdim maximale Breite oder Höhe des skalierten Bildes in
 * Pixeln
 *
 * @param $scaleheight Angabe ob die Höhe oder die Breite für den
 * Maximalwert beachtet werden soll, wenn dieser Parameter @c true
 * ist, wird die Höhe auf @a $maxdim skaliert, ansonsten die Breite
 *
 * @return Pfad des skalierten Bildes relativ zu @a gallerydir
 */
function getScaledImage($galleryname, $basename, $maxdim, $scaletype) {
  global $configuration;

  if ($maxdim == 0) {
    debug_print_backtrace();
  }
  $gallerydir = realpath($configuration['gallerydir'] . DIRECTORY_SEPARATOR . $galleryname);
  $originalfile = $gallerydir . DIRECTORY_SEPARATOR . $basename;
  switch($scaletype) {
  case SCALE_WIDTH:
    $scaleheight = false;
    break;
  case SCALE_HEIGHT:
    $scaleheight = true;
    break;
  case SCALE_BOTH:
    $originalsize = getimagesize($originalfile);
    $scaleheight = ($originalsize[1] > $originalsize[0]);
    break;
  default:
    header('Content-Type: text/plain');
    debug_print_backtrace();
    die(_('Unknown scale type'));
  }
  
  if ($scaleheight) {
    $scaleddir = sprintf("scaled_x%d", $maxdim);
  } else {
    $scaleddir = sprintf("scaled%dx_", $maxdim);
  }
  $scaleddirpath = implode(DIRECTORY_SEPARATOR,
    array($configuration['gallerydir'], $galleryname, $scaleddir));
  if (!is_dir($scaleddirpath)) {
    // versuchen das Thumbnail-Verzeichnis anzulegen
    $mkdir = @mkdir($scaleddirpath, 0755);
    if (!$mkdir) {
      trigger_error(sprintf(_("could not create directory %s.\n"),
                            $scaleddirpath),
                    E_USER_WARNING);
      return $galleryname . '/' . $basename;
    }
  }
  
  $scaledimage = $scaleddirpath . DIRECTORY_SEPARATOR . $basename;
  if (!is_file($scaledimage)) {
    // Datei erzeugen
    $origimage = imagecreatefromjpeg($originalfile);
    $origx = imagesx($origimage);
    $origy = imagesy($origimage);
    if ($scaleheight) {
      $scaleratio = $origy / (1.0 * $maxdim);
      $newy = $maxdim;
      $newx = (int) $origx / $scaleratio;
    } else {
      $scaleratio = $origx / (1.0 * $maxdim);
      $newx = $maxdim;
      $newy = (int) $origy / $scaleratio;
    }
    $newimage = imagecreatetruecolor($newx, $newy);
    imagecopyresampled($newimage, $origimage, 0, 0, 0, 0, $newx, $newy,
                       $origx, $origy);
    imagejpeg($newimage, $scaledimage, 90);
  }
  return implode('/', array($galleryname, $scaleddir, $basename));
}

/**
 * Gibt die Informationen über Vorschaubilder zurück.
 *
 * @param $galleryname Galeriename
 *
 * @return Array mit drei Elementen, dessen erstes Element die
 * aufsummierte Breite der Einzelbilder, dessen zweites Element der
 * Galeriename und dessen drittes Element ein assoziatives Array mit
 * den Bildnamen als Keys und Arrays mit dem Pfadnamen das Thumbnails
 * und dem Ergebnis von getimagesize() als Werten ist.
 */
function getThumbNailInfo($galleryname) {
  global $theme, $configuration;

  $thumbsizes = array();
  $thumbdimsum = 2;
  $scaletype = ($theme->themetype == 'horizontal') ? SCALE_HEIGHT : SCALE_WIDTH;
  foreach (glob(realpath($configuration['gallerydir'] . DIRECTORY_SEPARATOR .
                         $galleryname) . DIRECTORY_SEPARATOR .
                '*.jp{e,}g', GLOB_BRACE) as $filename) {
    $basename = basename($filename);
    $thumbfile = getScaledImage($galleryname, $basename, $theme->thumbsize,
                                $scaletype);
    if ($thumbsize = getimagesize(realpath($configuration['gallerydir'] .
                                           DIRECTORY_SEPARATOR .
                                           $thumbfile))) {
      $thumbsizes[$basename] = array($thumbfile, $thumbsize);
      if ($theme->themetype == 'horizontal') {
        $thumbdimsum = $thumbdimsum + $thumbsize[0] + 3;
      } else {
        $thumbdimsum = $thumbdimsum + $thumbsize[1] + 3;
      }
    }
  }
  return array($thumbdimsum, $galleryname, $thumbsizes);
}

/**
 * Gibt die Links zu den existierenden Galerien zurück.
 *
 * @return Array, welches je Gallerie ein assoziatives Array mit den
 * Keys @a gallery, @a label und @a url enthält. Die Labels für die
 * Galerien werden aus dem @a title-Feld der @a gallery-Sektion der @c
 * galleryinfo.ini ausgelesen. Wenn diese Feld nicht gesetzt ist, wird
 * der Galeriename verwendet.
 *
 * @see galleryExists()
 * @see getGalleryConfig()
 */
function getGalleryLinks() {
  global $configuration;

  $retval = array();
  $galleries = getOrderedGalleries();
  foreach ($galleries as $gallery) {
    $urlparams = array();
    $urlparams['galleryname'] = $gallery[1];
    if (array_key_exists('theme', $_GET)) {
      $urlparams['theme'] = $_GET['theme'];
    }
    $parts = array();
    foreach ($urlparams as $key => $value) {
      $parts[] = sprintf("%s=%s", $key, urlencode($value));
    }
    $url = sprintf('index.php?%s',
                   implode(ini_get('arg_separator.output'), $parts));
    $retval[] = array('gallery' => $gallery[1],
                      'label' => getGalleryLabel($gallery[1]),
                      'url' => htmlspecialchars($url));
  }
  return $retval;
}

/**
 * Gibt für die Ausgabe aufbereitete Informationen über die
 * Thumbnail-Bilder die in @a $thumbinfo definiert sind zurück.
 *
 * @param &$thumbinfo Referenz auf ein Array, wie es von
 * getThumbNailInfo() zurückgegeben wird
 *
 * @return ein Array mit einem assoziativen Array pro Thumbnail-Bild
 * mit den Keys @a src, @a sizes und @a alt, die jeweils die relative
 * URL des Thumbnail-Bildes, die Größenangaben für ein img-Tag sowie
 * die Angaben für das alt-Attribut eines img-Tags enthalten
 *
 * @see getThumbNailInfo()
 */
function getAllThumbnails(&$thumbinfo) {
  global $configuration;

  $retval = array();
  foreach ($thumbinfo[2] as $basename => $data) {
    $retval[] = array('src' => $configuration['gallerypath'] . '/' . $data[0],
                      'sizes' => $data[1][3],
                      'alt' => getImageLabel($thumbinfo[1], $basename));
  }
  return $retval;
}

/**
 * Gibt die Daten für das erste Vorschaubild aus @a $thumbinfo zurück.
 *
 * @param &$thumbinfo Referenz auf ein Array, wie es von
 * getThumbNailInfo() zurückgegeben wird
 *
 * @return ein assoziatives Array mit den Keys @a title, @a full, @a
 * src, @a alt und @a sizes jeweils mit den Werten Titel des Bildes,
 * relative URL der Vollbildansicht, relative URL des Vorschaubildes,
 * Alternativtext für das Bild und Größenangaben des Bildes für ein
 * img-Tag
 *
 * @see getThumbNailInfo()
 */
function getFirstPreview(&$thumbinfo) {
  global $theme, $configuration;

  reset($thumbinfo[2]);
  $basename = key($thumbinfo[2]);
  $data = current($thumbinfo[2]);
  $galleryname = $thumbinfo[1];
  $fullname = $configuration['gallerypath'] . '/' . $galleryname . '/' . $basename;
  $scaledimage = getScaledImage($galleryname, $basename,
                                $theme->previewsize, SCALE_BOTH);
  $scaledimagesize = getimagesize(realpath($configuration['gallerydir'] .
                                           DIRECTORY_SEPARATOR .
                                           $scaledimage));
  $label = getImageLabel($galleryname, $basename);
  return array('title' => $label,
               'full'  => $fullname,
               'src'   => $configuration['gallerypath'] . '/' . $scaledimage,
               'alt'   => $label,
               'sizes' => $scaledimagesize[3]);
}

/**
 * Gibt die Beschreibung des ersten Bildes aus @a $thumbinfo zurück.
 *
 * @param &$thumbinfo Referenz auf ein Array, wie es von
 * getThumbNailInfo() zurückgegeben wird
 *
 * @return HTML-kodierte Beschreibung des Bildes
 *
 * @see getThumbNailInfo()
 */
function getFirstDescription(&$thumbinfo) {
  reset($thumbinfo[2]);
  return htmlentities(getImageLabel($thumbinfo[1], key($thumbinfo[2])));
}

/**
 * Gibt den Titeltext für die Galerie aus.
 *
 * @param &$thumbinfo Referenz auf ein Array, wie es von
 * getThumbNailInfo() zurückgegeben wird
 *
 * @return Inhalt für das title-Tag auf der Ausgabeseite
 */
function getGalleryTitle(&$thumbinfo) {
  foreach ($thumbinfo[2] as $basename => $data) {
    return htmlentities(sprintf("%s :: %s", getGalleryLabel($thumbinfo[1]),
                                getImageLabel($thumbinfo[1], $basename)));
  }
}