<?php
// Copyright 2011 Toby Zerner, Simon Zerner
// This file is part of esoTalk. Please see the included license file for usage information.

if (!defined("IN_ESOTALK")) exit;

/**
 * The ETUpload class provides a way to easily validate and manage uploaded files. Typically, an upload should
 * be validated using the getUploadedFile() function, which will return the temporary filename of the uploaded
 * file. This can then be saved with saveAs() or saveAsImage().
 *
 * @package esoTalk
 */
class ETUpload extends ETPluggable {


/**
 * Get the maximum file upload size in bytes.
 *
 * @return int
 */
public function maxUploadSize()
{
	return min(iniToBytes(ini_get("post_max_size")), iniToBytes(ini_get("upload_max_filesize")));
}


/**
 * Validate an uploaded file and return its temporary file name.
 *
 * @param string $key The name of the key in the $_FILES array.
 * @param array $allowedTypes An array of allowed mime-types. If empty, any mime-type is allowed.
 * @return string The temporary filepath of the uploaded file.
 */
public function getUploadedFile($key, $allowedTypes = array())
{
	$error = false;

	// If the uploaded file doesn't exist, then we have to fail.
	if (!isset($_FILES[$key]) or !is_uploaded_file($_FILES[$key]["tmp_name"]))
		$error = T("message.fileUploadFailed");

	// Otherwise, check for an error.
	else {
		$file = $_FILES[$key];
		switch ($file["error"]) {
			case 1:
			case 2:
				$error = sprintf(T("message.fileUploadTooBig"), ini_get("upload_max_filesize"));
				break;
			case 3:
			case 4:
			case 6:
			case 7:
			case 8:
				$error = T("message.fileUploadFailed");
		}
	}

	// If there was an error, throw it as an exception.
	if ($error) throw new Exception($error);

	// Otherwise, return the path to the uploaded file.
	else return $file["tmp_name"];
}


/**
 * Save an uploaded file to the specified destination.
 *
 * @param string $source The source file (typically what is returned by getUploadedFile())
 * @param string $destination The destination file to save as.
 * @return string The destination file that was saved.
 */
public function saveAs($source, $destination)
{
	// Attempt to move the uploaded file to the destination. If we can't, throw an exception.
	if (!move_uploaded_file($source, $destination))
		throw new Exception(sprintf(T("message.fileUploadFailedMove"), $destination));

	return $destination;
}


/**
 * Save an uploaded file to the specified destination, and convert it to a safe image with size restraints.
 *
 * @param string $source The source file (typically what is returned by getUploadedFile())
 * @param string $destination The destination file to save as. If this includes an extension (eg. .jpg) then
 * 		the image will be saved as that type. Otherwise, the same type as the source file will be used and
 * 		the extension will be appended.
 * @param int $width The width to resize the image to.
 * @param int $height The height to resize the image to.
 * @param string $sizeMode How to handle resizing of the image and the size of the output image:
 * 		crop: the output image will be $width by $height regardless of the input image.
 * 		min: the output image will be at least $width by $height, but can be more if the input image is larger.
 * 		max: the output image will be at most $width by $height, but can be less if the input image is smaller.
 * @return string The destination file that was saved, including the image type extension.
 */
public function saveAsImage($source, $destination, $width, $height, $sizeMode = "max")
{
	// Get information about the source image and make sure it actually is an image.
	$size = getimagesize($source);
	if ($size === false) throw new Exception(T("message.fileUploadNotImage"));
	list($sourceWidth, $sourceHeight, $type) = $size;

	// Depending on the type of image, create a GD image object of it.
	switch ($type) {
		case 1:
			$image = @imagecreatefromgif($source);
			break;
		case 2:
			$image = @imagecreatefromjpeg($source);
			break;
		case 3:
			$image = @imagecreatefrompng($source);
	}

	// If we weren't able to create a GD image from the source, throw an exception.
	if (!$image) throw new Exception(T("message.fileUploadNotImage"));

	// Work out the image type which we will output the image as.
	$outputType = pathinfo($destination, PATHINFO_EXTENSION);
	$types = array(1 => "gif", 2 => "jpg", 3 => "png");

	// If an extension was specified in the destination, we'll use that and strip the it off of the
	// destination; otherwise, use the type from getimagesize().
	if (!$outputType or !in_array($outputType, $types)) {
		$outputType = $types[$type];

		// We don't support gif output, as it makes transparency difficult.
		if ($outputType == "gif") $outputType = "png";
	}
	else $destination = substr($destination, 0, -strlen($outputType) - 1);

	// Work out the ratios needed to get the image to fit into the specified width or height.
	$widthRatio = $width / $sourceWidth;
	$heightRatio = $height / $sourceHeight;

	// If we're cropping, use the larger of the two ratios so we fill the whole image area.
	if ($sizeMode == "crop") $ratio = max($widthRatio, $heightRatio);

	// If not, use the smaller of the two so we get the whole image in the area.
	else $ratio = min($widthRatio, $heightRatio);

	// If the provided width x height is a minimum, the ratio must be no smaller than one.
	if ($sizeMode == "min") $ratio = max(1, $ratio);

	// If the provided width x height is a maximum, the ratio must be no greater than one.
	elseif ($sizeMode == "max") $ratio = min(1, $ratio);

	// Work out the new width and height of the image depending on the selected ratio.
	$newWidth = ceil($ratio * $sourceWidth);
	$newHeight = ceil($ratio * $sourceHeight);

	// Work out the dimensions of the image we are creating.
	if ($sizeMode == "max" or $sizeMode == "min") {
		$width = $newWidth;
		$height = $newHeight;
	}

	// Create a new GD image of the specified width and height, and make sure we can handle transparency.
	$newImage = imagecreatetruecolor($width, $height);
	if ($outputType == "png") {
		imagecolortransparent($newImage, imagecolorallocate($newImage, 0, 0, 0));
		imagealphablending($newImage, false);
		imagesavealpha($newImage, true);
	}

	// Work out how much to offset the image by in order to center it.
	$x = $newWidth / 2 - $width / 2;
	$y = $newHeight / 2 - $height / 2;

	// Copy the source image onto our new canvas.
	imagecopyresampled($newImage, $image, -$x, -$y, 0, 0, $newWidth, $newHeight, $sourceWidth, $sourceHeight);

	// Save the image to the correct destination and format.
	switch ($outputType) {
		case "png":
			imagepng($newImage, $outputFile = "$destination.png");
			break;

		case "gif":
			imagegif($newImage, $outputFile = "$destination.gif");
			break;

		default:
			imagejpeg($newImage, $outputFile = "$destination.jpg", 90);
	}

	// Clean up.
	imagedestroy($newImage);
	imagedestroy($image);

	return $outputFile;
}

}