<?php

uses( 'cloudfiles/cloudfiles' );


class ImageHandlerComponent extends Object {
	
	private $controller;

	private $thumb_width = 100;
	private $thumb_height = 100;
	
	private $icon_width = 80;
	private $icon_height = 80;
	
	private $logo_width = 300;
	private $logo_height = 150;
	
	/**
	 * array of all allowed image file extentions used to 
	 * verify uploaded images
	 */
	private $allowed_ext = array('jpg','gif','jpeg', 'png');
	
	/**
	 * once verified this holds a reference to the image file that will be 
	 * manipulated
	 */
	private $img = null;
	
	/**
	 * once an image is set this holds the height of the original image
	 * if height is 0 no image is set
	 */
	private $img_h = 0;
	
	/**
	 * once an image is set this holds the width of the original image
	 * if width is 0 no image is set
	 */
	var $img_w = 0;
	
	/**
	 * once an image is set this holds a numeric flag indicating the image file type
	 * if nothing is set this is null
	 */
	private $img_type = null;
	
	/**
	 * once an image is set this holds the original file name minus the extention
	 * if nothing is set or there is no name?? it defualts to untitled
	 */
	private $original_name = 'untitled';
	
	/**
	 *
	 */
	private $auth = null;
	
	/**
	 *
	 */
	private $conn = null;
	
	/**
	 * the path to a temp file that should be deleted when it is finished being used
	 * a temp file is created when an icon or logo is cropped from an existing photo
	 * that photo is retrieved from the Cloud Files and saved temporarily to this path
	 */
	private $temp_path = null;
	
	
	/**
	 * class initilization method called when loaded by a controller
	 *
	 * @param Object - $controller - a reference to the controller that has loaded this component
	 */
	public function startup(&$controller) {
		
		$this->controller = $controller;
		
		if( defined( 'CF_USERNAME') && defined('CF_API_KEY') ) { 
			
			$this->auth = new CF_Authentication(CF_USERNAME, CF_API_KEY);
			$this->auth->authenticate();
			$this->conn = new CF_Connection( $this->auth );
		}
	}
	
	/**
	 * checks an uploaded image file for errors and verifies its safe to use
	 * this must be called before using any other method in this class
	 * every method will act on the image verified here.
	 *
	 * @param array $img - the $_FILE[image_name] array created by the file upload
	 * @return boolean success 
	 */
	public function verifyUploadedImage( $img ) {
		
		//check for upload errors
		if($img['error'] > 0 ) {
			switch( $img['error'] ) {
				case 1:
				case 2:
					$this->controller->returnError( array('file' => 'image_max_upload_size'), true);
				break;
				case 3:
					$this->controller->returnError( array('file' => 'image_partial_upload'), true);
				break;
				case 4:
					$this->controller->returnError( array('file' => 'no_image_uploaded'), true);
				break;
			}
			return false;
		}
		
		//confirm its an uploaded file
		if( !is_uploaded_file( $img['tmp_name'] ) ) {
			$this->controller->returnError( array('file' => 'image_not_uploaded'), true);
			return false;
		}
		
		//confirm file extention
		$ext = extention( $img['name'] );
		if( !in_array( $ext, $this->allowed_ext ) ) {
			$this->controller->returnError( array('file' => 'image_wrong_filetype'), true);
			return false;
		}
		
		//every thing is good set image
		return $this->setImg( $img['tmp_name'], $img['name'] );
	}
	
	/**
	 * get an image from Cloud Files and set it ** SEE setImage
	 * @param string $container - the name of the Cloud File container the image is stored in
	 * @param string $file - the image file name
	 * @return boolean success 
	 */
	public function getSetImg( $container, $file ) {
		
		$file = $this->cleanCacheString($file);
		$this->temp_path = WWW_ROOT.'temp_photo'.DS.$file;
		
		$con = $this->conn->get_container( $container );
		$img = $con->get_object( $file );
		$img->save_to_filename( $this->temp_path );
		return $this->setImg( $this->temp_path );
	}
	
	/**
	 * set the image to be used to crop icons and logos out of 
	 * @param string $img - the path to the image (must be a local path)
	 * @param string $name - a name to use for the image (overrides file name)
	 * @returns boolean success
	 */
	public function setImg( $img, $name=null ) {
		
		$img = $this->cleanCacheString( $img );
		$info = getimagesize( $img );
		
		$this->img = $img;
		$this->img_w = $info[0];
		$this->img_h = $info[1];
		$this->img_type = $info[2];
		$this->ext = image_type_to_extension($info[2]);
		if($name) {
			$this->original_name = substr($name, 0, -strlen(strrchr($name, '.')));
		}
		return true;
	}
	
	/**
	 * remove the random cache string from the filename
	 */
	private function cleanCacheString( $file ) {
		
		$imgp = explode( '?', $file);
		return $imgp[0]; 
	}
	
	/**
	 * returns the name of the file that was uploaded
	 */
	public function getOrigName() {
		
		return $this->original_name;
	}
	
	/**
	 * moves the set image to the destination directory and gives it a  unique name
	 *
	 * @param string $container - the name of the Cloud Files Container you want the file moved to
	 * @return 
	 *		- success the new name of the image file
	 *		- failure boolean false
	 */
	public function moveFileTo( $container ) {
		
		$t = time();
		$NewName = $t.$this->ext;
		
		$container = $this->conn->get_container( $container );
		$object = $container->create_object( $NewName );
		$object->load_from_filename( $this->img );
		
		return $NewName.'?'.$t;
	}
	
	/**
	 * creates a thumbnail of the set image.  
	 * The max width and height are set by the thumb_width and thumb_height properties of this class respectively
	 *
	 * @param string $container - the name of the Cloud Files container to save the thumbnail to
	 * @return 
	 *		- success the file name of the thumbnial;
	 * 		- failure boolean false
	 */
	public function createThumb( $container ) {
		
		//if the image is already the correct size just move it and return the name
		if( $this->img_w == $this->thumb_width && $this->img_h == $this->thumb_height ) {
			return $this->moveFileTo( $dest );
		}
		
		if( $this->img_w > 0 && $this->img_h > 0 ) {
			
			$xscale=$this->img_w/$this->thumb_width;
			$yscale=$this->img_h/$this->thumb_height;
			
			if ($yscale>$xscale){
				$new_width = round($this->img_w * (1/$yscale));
				$new_height = round($this->img_h * (1/$yscale));
			} else {
				$new_width = round($this->img_w * (1/$xscale));
				$new_height = round($this->img_h * (1/$xscale));
			}
			return $this->_doScale( $new_width, $new_height, $this->img_w, $this->img_h, 0, 0, $container );
		}
		return false;	
	}
	
	/**
	 * conveneince method for creating Icons.  
	 * an Icon has a set width and height (set by properties $icon_width and $icon_height)
	 *
	 * @param string $container - the name of the Cloud Files container to save the icon to
	 * @return 
	 *		- success the file name of the icon;
	 * 		- failure boolean false
	 */
	public function createIcon( $container ) {
		
		//if the image is already the correct size just move it and return the name
		if( $this->img_w == $this->icon_width && $this->img_h == $this->icon_height ) {
			return $this->moveFileTo( $container );
		}
		
		if( $this->img_w > 0 && $this->img_h > 0 ) {
			return $this->_doScale( $this->icon_width, $this->icon_height, $this->img_w, $this->img_h, 0, 0, $container );
		}
		return false;					
	}
	
	/**
	 * conveneince method for creating Logos.  
	 * a Logo has a set width and height (set by properties $icon_width and $icon_height)
	 *
	 * @param string $container - the name of the Cloud Files container to save the logo to
	 * @return 
	 *		- success the file name of the icon;
	 * 		- failure boolean false
	 */
	public function createLogo( $container ) {
		
		//if the image is already the correct size just move it and return the name
		if( $this->img_w == $this->logo_width && $this->img_h == $this->logo_height ) {
			return $this->moveFileTo( $container );
		}
		
		if( $this->img_w > 0 && $this->img_h > 0 ) {
			return $this->_doScale( $this->logo_width, $this->logo_height, $this->img_w, $this->img_h, 0, 0, $container );
		}
		return false;					
	}
	

	/**
	 * crops an icon out of an existing image 
	 * NOTE: the image the icon is cropped from must first be "set"
	 *       by calling the setImg method
	 *
	 * @param string $container - the name of the Cloud Files container to save the Icon to
	 * @param int $w - the width of the source image rectangle
	 * @param int $h - the height of the source image rectangle
	 * @param int $x - the x cordinate of the source image rectangle
	 * @param int $y - the y cordinate of the source image rectangle
	 *
	 * @return 
	 *		- success the file name of the img;
	 * 		- failure boolean false
	 */	
	public function cropIcon( $container, $x, $y, $w, $h  ) {
		
		if( $this->img_w > 0 && $this->img_h > 0 ) {
			return $this->_doScale( $this->icon_width, $this->icon_height, $w, $h, $x, $y, $container );
		}
		return false;	
	}
	
	/**
	 * crops an icon out of an existing image 
	 * NOTE: the image the icon is cropped from must first be "set"
	 *       by calling the setImg method
	 *
	 * @param string $container - the name of the Cloud Files container to save the logo to
	 * @param int $w - the width of the source image rectangle
	 * @param int $h - the height of the source image rectangle
	 * @param int $x - the x cordinate of the source image rectangle
	 * @param int $y - the y cordinate of the source image rectangle
	 *
	 * @return 
	 *		- success the file name of the img;
	 * 		- failure boolean false
	 */	
	public function cropLogo( $container, $x, $y, $w, $h  ) {
		
		if( $this->img_w > 0 && $this->img_h > 0 ) {
			return $this->_doScale( $this->logo_width, $this->logo_height, $w, $h, $x, $y, $container );
		}
		return false;	
	}
	
	/**
	 * private  
	 * does the scaling and saving of images
	 *
	 * @param int $new_width - the width of output image
	 * @param int $new_height - the height of output image
	 * @param int $src_w - the width of the source image rectangle
	 * @param int $src_h - the height of the source image rectangle
	 * @param int $src_x - the x cordinate of the source image rectangle
	 * @param int $src_y - the y cordinate of the source image rectangle
	 * @param string $container - the Cloud File container that the image will be stored in
	 * @return 
	 *		- success the file name of the img;
	 * 		- failure boolean false
	 */
	private function _doScale( $new_width, $new_height, $src_w, $src_h, $src_x, $src_y, $container ) {
		
		$new_name = time().$this->ext;
			
		switch( $this->img_type ) {
			case 1://GIF
				$image_dest = imagecreate($new_width, $new_height);
				$image = imagecreatefromgif( $this->img );
				
				$trnprt_indx = imagecolortransparent($image);
				// If we have a specific transparent color
				if ($trnprt_indx >= 0) {
					// Get the original image's transparent color's RGB values
					$trnprt_color = imagecolorsforindex($image, $trnprt_indx);
					// Allocate the same color in the new image resource
					$trnprt_indx = imagecolorallocate($image_dest, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue']);
					// Completely fill the background of the new image with allocated color.
					imagefill($image_dest, 0, 0, $trnprt_indx);
					// Set the background color for new image to transparent
					imagecolortransparent($image_dest, $trnprt_indx);
				} 
				imagecopyresampled($image_dest, $image, 0, 0, $src_x, $src_y, $new_width, $new_height, $src_w, $src_h);
				ob_clean();
				ob_start();
				imagegif( $image_dest );
				$stream = ob_get_clean();
				$con = $this->conn->get_container( $container );
				$img = $con->create_object( $new_name );
				$img->write( $stream );
				
				imagedestroy($image);
				return $new_name;
			break;
			case 2://JPEG
				$image_dest = imagecreatetruecolor($new_width, $new_height);
				$image = imagecreatefromjpeg( $this->img );
				imagecopyresampled($image_dest, $image, 0, 0, $src_x, $src_y, $new_width, $new_height, $src_w, $src_h);
				ob_clean();
				ob_start();
				imagejpeg( $image_dest );
				$stream = ob_get_clean();
				$con = $this->conn->get_container( $container );
				$img = $con->create_object( $new_name );
				$img->write( $stream );
				
				imagedestroy($image);
				return $new_name;
			break;
			case 3://PNG
				$image_dest = imagecreatetruecolor($new_width, $new_height);
				$image = imagecreatefrompng( $this->img );
				
				$trnprt_indx = imagecolortransparent($image);
				// If we have a specific transparent color
				if ($trnprt_indx >= 0) {
					// Get the original image's transparent color's RGB values
					$trnprt_color = imagecolorsforindex($image, $trnprt_indx);
					// Allocate the same color in the new image resource
					$trnprt_indx = imagecolorallocate($image_dest, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue']);
					// Completely fill the background of the new image with allocated color.
					imagefill($image_dest, 0, 0, $trnprt_indx);
					// Set the background color for new image to transparent
					imagecolortransparent($image_dest, $trnprt_indx);
				} 
				// Turn off transparency blending (temporarily)
				imagealphablending($image_dest, false);
				// Create a new transparent color for image
				$color = imagecolorallocatealpha($image_dest, 255, 255, 255, 127);
				// Completely fill the background of the new image with allocated color.
				imagefill($image_dest, 0, 0, $color);
				// Restore transparency blending
				imagesavealpha($image_dest, true);
				imagecopyresampled($image_dest, $image, 0, 0, $src_x, $src_y, $new_width, $new_height, $src_w, $src_h);
				ob_clean();
				ob_start();
				imagepng( $image_dest );
				$stream = ob_get_clean();
				$con = $this->conn->get_container( $container );
				$img = $con->create_object( $new_name );
				$img->write( $stream );
				
				imagedestroy($image);
				return $new_name;
			break;
		}
		return false;
	}
	
	/**
	 * moderates an image by moving it one directory back from the file root
	 * into a failed directory
	 *
	 * @param string $file - the file to move into moderation
	 * @param string $rpl_file - OPTIONAL the file name of an image in the images/app_defaults/ folder 
	 *							 that will be copy inplace of the moved file
	 *
	 * @return boolean success
	 */	
	public function moderate( $file, $rpl_file) {
		
		$file = STATIC_LINK.$this->cleanCacheString( $file );
		if( file_exists( $file ) ) {
			$dir = explode(DS, $file);
			array_splice($dir, -1, 0, 'failed');
			$dest = join('/', $dir);
			
			if( rename($file, $dest) ) {
				if( $rpl_file && !copy( STATIC_LINK.'images/app_defaults/'.$rpl_file, $file) ) {
					return false;
				}
				return true;
			}
		}
		return false;
	}
	
	/**
	 * restores a moderated image by moving the file back to its main directory and out of the failed direcoty
	 *
	 * @param string $file - the file to move into moderation 
	 * @return boolean success
	 */
	public function restoreModerated( $file ) {
		
		$file = STATIC_LINK.$this->cleanCacheString( $file );
		$dir = explode(DS, $file);
		array_splice($dir, -1, 0, 'failed');
		$failed_file = join('/', $dir);
		
		if( file_exists( $failed_file ) ) {
			if( rename($failed_file, $file) ) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * removes and image file from the server
	 *
	 * @param string $container - the Cloud File container the image file is stored in
	 * @param string $file - the name of the file
	 * @return boolean success
	 */	
	public function delete( $container, $file ) {
		
		try {
			$images = $this->conn->get_container( $container );
			$images->delete_object( $this->cleanCacheString($file) );
			return true;
		} catch( Exception $e ) {
			$this->controller->log('Unable to delete Image: CONTAINER = '.$container.', FILE = '.$file.' error = '.$e->getMessage() );
			return false;
		}
	}
	
	function __destruct() {
		
		if( $this->temp_path ) {
			unlink( $this->temp_path );
		}		
	}
}
?>