<?php
/*
 * $RCSfile: ThumbnailHelper.class,v $
 *
 * Gallery - a web based photo album viewer and editor
 * Copyright (C) 2000-2005 Bharat Mediratta
 *
 * 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., 51 Franklin Street - Fifth Floor, Boston, MA  02110-1301, USA.
 */
/**
 * @version $Revision: 1.17 $ $Date: 2005/08/23 03:49:56 $
 * @package Thumbnail
 * @author Alan Harder <alan.harder@sun.com>
 */

/**
 * A helper class for the Thumbnail module.
 *
 * @package Thumbnail
 * @subpackage Classes
 */
class ThumbnailHelper {

    /**
     * Return a map of itemId => fileName for ThumbnailImage entities
     *
     * You can specify how many items to list, and where the windows is in
     * the list of all items.
     *
     * @param boolean [optional] true to list all; by default only gets items with mimeTypeList
     * @param int [optional] the number of items desired
     * @param int [optional] the start of the range
     * @param string [optional] a substring to match
     * @return array object GalleryStatus a status code
     *               array of itemId=>fileName
     * @static
     */
    function fetchThumbnails($listAll=false, $count=null, $offset=null, $substring=null) {
	global $gallery;

	$where = $data = array();
	$query = 'SELECT [ThumbnailImage::id], [ThumbnailImage::fileName] FROM [ThumbnailImage]';

	if (!$listAll) {
	    $where[] = '[ThumbnailImage::itemMimeTypes] IS NOT NULL';
	}
	if (!empty($substring)) {
	    $where[] = '[ThumbnailImage::fileName] LIKE ?';
	    $data[] = "%$substring%";
	}
	if (!empty($where)) {
	    $query .= ' WHERE ' . implode(' ', $where);
	}

	$query .= ' ORDER BY [ThumbnailImage::fileName] ASC';

	list ($ret, $searchResults) =
	    $gallery->search($query, $data,
			     array('limit' => array('count' => $count,
						    'offset' => $offset)));
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null);
	}

	$items = array();
	while ($result = $searchResults->nextResult()) {
	    $items[$result[0]] = $result[1];
	}

	return array(GalleryStatus::success(), $items);
    }

    /**
     * Fetch ThumbnailImage for given item
     *
     * @param int item id
     * @return array object GalleryStatus a status code
     *               object ThumbnailImage or null
     * @static
     */
    function fetchThumbnail($itemId) {
	global $gallery;

	list ($ret, $searchResults) = $gallery->search(
	    'SELECT [ThumbnailImage::id] FROM [ThumbnailImage], [ChildEntity]
	     WHERE [ThumbnailImage::id] = [ChildEntity::id] AND [ChildEntity::parentId] = ?',
	    array($itemId));
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null);
	}

	$thumbnailImage = null;
	if ($result = $searchResults->nextResult()) {
	    list ($ret, $thumbnailImage) = GalleryCoreApi::loadEntitiesById((int)$result[0]);
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null);
	    }
	}

	return array(GalleryStatus::success(), $thumbnailImage);
    }

    /**
     * Get supported mime types
     *
     * @return array object GalleryStatus a status code
     *               array of mimeType=>itemId
     * @static
     */
    function fetchMimeTypeMap() {
	global $gallery;
	list ($ret, $searchResults) = $gallery->search(
	    'SELECT [ThumbnailImage::id], [ThumbnailImage::itemMimeTypes]
	     FROM [ThumbnailImage] WHERE [ThumbnailImage::itemMimeTypes] IS NOT NULL');
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null);
	}
	$data = array();
	while ($result = $searchResults->nextResult()) {
	    foreach (explode('|', $result[1]) as $mimeType) {
		$data[$mimeType] = $result[0];
	    }
	}
	return array(GalleryStatus::success(), $data);
    }

    /**
     * Get information about thumbnail support from other toolkits
     *
     * @param boolean true for data by-mimeType, false for data by-toolkit
     * @return array :object GalleryStatus a status code
     *               :by-mimeType: array of mimeType=>array(toolkit ids)
     *                or by-toolkit: array of toolkitId=>array(mimeTypes)
     *               :by-mimeType: null
     *                or by-toolkit: array of mimeType conflicts
     * @static
     */
    function fetchToolkitSupport($byMimeType=true) {
	list ($ret, $tList) = GalleryCoreApi::getToolkitOperationMimeTypes('thumbnail');
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null, null);
	}
	list ($ret, $cList) = GalleryCoreApi::getToolkitOperationMimeTypes('convert-to-image/jpeg');
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null, null);
	}
	$data = array_merge_recursive($tList, $cList);
	if ($byMimeType) {
	    return array(GalleryStatus::success(), $data, null);
	}
	$badMime = array();
	$toolkitMime = array();
	foreach ($data as $mime => $toolkits) {
	    foreach ($toolkits as $id) {
		if (!isset($toolkitMime[$id]) || !in_array($mime, $toolkitMime[$id])) {
		    $toolkitMime[$id][] = $mime;
		}
	    }
	    if (in_array('Thumbnail', $toolkits) && count($toolkits) > 1) {
		$badMime[] = $mime;
	    }
	}
	return array(GalleryStatus::success(), $toolkitMime, $badMime);
    }

    /**
     * Add new ThumbnailImage
     *
     * @param string mime type to be supported, or null
     * @param int parent item id, or null
     * @param string name for image
     * @param string path to image file
     * @param string mime type of image file
     * @param array known width and height of image (used if toolkit is unable to get size)
     * @param object ThumbnailImage (optional -- if you don't provide it we'll
     *                               get one from the Factory)
     * @return array object GalleryStatus a status code
     *               id of newly created object
     * @static
     */
    function addItem($mimeType, $parentId,
		     $filename, $tmpfile, $fileMimeType, $knownSize=array(), $item=null) {
	global $gallery;
	/*
	 * If we don't get useful data from the form or its a type we don't
	 * recognize, take a swing at it using the file name.
	 */
	list ($ret, $mimeExtensions) = GalleryCoreApi::convertMimeToExtensions($fileMimeType);
	if ($fileMimeType == 'application/octet-stream' ||
		$fileMimeType == 'application/unknown' || empty($mimeExtensions)) {
	    $extension = GalleryUtilities::getFileExtension($filename);
	    list ($ret, $fileMimeType) = GalleryCoreApi::convertExtensionToMime($extension);
	    if ($ret->isError()) {
		$fileMimeType = 'application/unknown';
	    }
	}
	if ($fileMimeType != 'image/jpeg') {
	    return array(GalleryStatus::error(ERROR_UNSUPPORTED_FILE_TYPE, __FILE__, __LINE__),
			 null);
	}

	if (!isset($item)) {
	    list ($ret, $item) =
		GalleryCoreApi::newFactoryInstance('GalleryEntity', 'ThumbnailImage');
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null);
	    }
	    if (!isset($item)) {
		return array(GalleryStatus::error(ERROR_MISSING_OBJECT, __FILE__, __LINE__), null);
	    }
	}

	$ret = $item->create($tmpfile, $fileMimeType, $filename);
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null);
	}
	if (isset($mimeType)) {
	    $item->setItemMimeTypes($mimeType);
	}
	if (isset($parentId)) {
	    $item->setParentId($parentId);
	}
	if ($item->getWidth()==0 && $item->getHeight()==0 &&
		isset($knownSize['width']) && isset($knownSize['height'])) {
	    $item->setWidth($knownSize['width']);
	    $item->setHeight($knownSize['height']);
	}
	$ret = $item->save();
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null);
	}

	list ($ret, $adminGroupId) =
	    GalleryCoreApi::getPluginParameter('module', 'core', 'id.adminGroup');
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null);
	}
	$ret = GalleryCoreApi::addGroupPermission($item->getId(), $adminGroupId, 'core.viewSource');
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null);
	}

	if (isset($mimeType)) {
	    $ret = GalleryCoreApi::registerToolkitOperation('Thumbnail',
				   array($mimeType), 'convert-to-image/jpeg', array(),
				   $gallery->i18n('Convert to a JPEG'), 'image/jpeg', 50);
	    if ($ret->isError()) {
		return array($ret->wrap(__FILE__, __LINE__), null);
	    }
	}

	return array(GalleryStatus::success(), $item->getId());
    }

    /**
     * Add or delete a mime type from an existing ThumbnailImage
     *
     * @param id of the ThumbnailImage
     * @param string mime type
     * @param boolean true to add mime type, false to delete
     * @return object GalleryStatus a status code
     * @static
     */
    function updateItem($itemId, $mimeType, $isAdd=true) {
	list ($ret, $item) = GalleryCoreApi::loadEntitiesById($itemId);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}
	list ($ret, $lockId) = GalleryCoreApi::acquireWriteLock($itemId);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}
	$mimeTypes = $item->getItemMimeTypesList();
	if ($isAdd) {
	    $mimeTypes[] = $mimeType;
	    $item->setItemMimeTypesList($mimeTypes);
	} else {
	    $newList = array();
	    foreach ($mimeTypes as $mime) {
		if ($mime != $mimeType) {
		    $newList[] = $mime;
		}
	    }
	    $item->setItemMimeTypesList($newList);
	}
	$ret = $item->save();
	if ($ret->isError()) {
	    GalleryCoreApi::releaseLocks($lockId);
	    return $ret->wrap(__FILE__, __LINE__);
	}
	$ret = GalleryCoreApi::releaseLocks($lockId);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}
	if ($isAdd) {
	    global $gallery;
	    $ret = GalleryCoreApi::registerToolkitOperation('Thumbnail',
				   array($mimeType), 'convert-to-image/jpeg', array(),
				   $gallery->i18n('Convert to a JPEG'), 'image/jpeg', 50);
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	} else {
	    $ret = GalleryCoreApi::unregisterToolkitOperation('Thumbnail',
				   'convert-to-image/jpeg', array($mimeType));
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	}
	return GalleryStatus::success();
    }

    /**
     * Create or update thumbnail derivative to be sourced from a ThumbnailImage
     *
     * @param object GalleryItem the item
     * @param int id of ThumbnailImage
     * @return object GalleryStatus a status code
     * @static
     */
    function applyThumbnail(&$item, $thumbnailId) {
	list ($ret, $derivative) = GalleryCoreApi::fetchThumbnailsByItemIds(array($item->getId()));
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}
	if (!empty($derivative)) {
	    $derivative = array_shift($derivative);
	    list ($ret, $lockId) = GalleryCoreApi::acquireWriteLock($derivative->getId());
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	    /* Make sure operations just contains thumbnail|{size} */
	    $derivative->setDerivativeOperations(
		preg_replace('/^(?:.*;)?(thumbnail\|\d+)(?:;.*)?$/', '$1',
			     $derivative->getDerivativeOperations()));
	} else {
	    list ($ret, $derivative) =
		GalleryCoreApi::newFactoryInstanceByHint('GalleryDerivative', 'ThumbnailImage');
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	    if (!isset($derivative)) {
		return GalleryStatus::error(ERROR_MISSING_OBJECT, __FILE__, __LINE__);
	    }
	    $ret = $derivative->create($item->getId(), DERIVATIVE_TYPE_IMAGE_THUMBNAIL);
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	    list ($ret, $prefs) =
		GalleryCoreApi::fetchDerivativePreferencesForItem($item->getParentId());
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	    $op = 'thumbnail|150';
	    foreach ($prefs as $pref) {
		if ($pref['derivativeType'] == DERIVATIVE_TYPE_IMAGE_THUMBNAIL) {
		    $op = $pref['derivativeOperations'];
		    break;
		}
	    }
	    $derivative->setDerivativeOperations($op);
	}

	$derivative->setDerivativeSourceId($thumbnailId);
	$derivative->setMimeType('image/jpeg');
	$ret = $derivative->save();
	if ($ret->isError()) {
	    if (isset($lockId)) {
		GalleryCoreApi::releaseLocks($lockId);
	    }
	    return $ret->wrap(__FILE__, __LINE__);
	}
	if (isset($lockId)) {
	    $ret = GalleryCoreApi::releaseLocks($lockId);
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	}

	return GalleryStatus::success();
    }

    /**
     * Remove custom thumbnail; delete thumbnail derivative or restore source id to original item
     * (if there is toolkit support for that source mimeType).
     *
     * @param object GalleryItem the item
     * @return object GalleryStatus a status code
     * @static
     */
    function restoreThumbnail(&$item) {
	list ($ret, $derivative) = GalleryCoreApi::fetchThumbnailsByItemIds(array($item->getId()));
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}
	if (empty($derivative)) {
	    return GalleryStatus::error(ERROR_MISSING_OBJECT, __FILE__, __LINE__);
	}
	$derivative = array_shift($derivative);

	if (GalleryUtilities::isA($item, 'GalleryAlbumItem')) {
	    list ($ret, $success) = GalleryCoreApi::guaranteeAlbumHasThumbnail($item->getId());
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	    return GalleryStatus::success();
	}

	/* Make sure operations just contains thumbnail|{size} */
	$operations = preg_replace('/^(?:.*;)?(thumbnail\|\d+)(?:;.*)?$/', '$1',
				   $derivative->getDerivativeOperations());

	list ($ret, $source) = GalleryCoreApi::fetchPreferredSource($item);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	list ($ret, $operations, $outputMimeType) =
	    GalleryCoreApi::makeSupportedViewableOperationSequence(
					 $source->getMimeType(), $operations, true);
	if ($ret->isError()) {
	    return $ret->wrap(__FILE__, __LINE__);
	}

	if (empty($operations)) {
	    /* Operations not supported.. remove the thumbnail */
	    $ret = GalleryCoreApi::deleteEntityById($derivative->getId());
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	} else {
	    list ($ret, $lockId) = GalleryCoreApi::acquireWriteLock($derivative->getId());
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }

	    $derivative->setMimeType($outputMimeType);
	    $derivative->setDerivativeOperations($operations);
	    $derivative->setDerivativeSourceId($source->getId());

	    $ret = $derivative->save();
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }

	    $ret = GalleryCoreApi::releaseLocks($lockId);
	    if ($ret->isError()) {
		return $ret->wrap(__FILE__, __LINE__);
	    }
	}

	return GalleryStatus::success();
    }

    /**
     * Return mime types and applicable file extensions
     *
     * @return array object GalleryStatus a status code
     *               array (string mime type => string list of extensions)
     * @static
     */
    function getMimeTypeMap() {
	global $gallery;

	$query = '
	    SELECT [GalleryMimeTypeMap::mimeType], [GalleryMimeTypeMap::extension]
	    FROM [GalleryMimeTypeMap]
	    ORDER BY [GalleryMimeTypeMap::mimeType]
	';
	list ($ret, $searchResults) = $gallery->search($query);
	if ($ret->isError()) {
	    return array($ret->wrap(__FILE__, __LINE__), null);
	}
	while ($result = $searchResults->nextResult()) {
	    $typeMap[$result[0]][] = $result[1];
	}

	foreach(array_keys($typeMap) as $mimeType) {
	    $typeMap[$mimeType] = implode(' ', $typeMap[$mimeType]);
	}
	return array(GalleryStatus::success(), $typeMap);
    }
}
?>
