<?php

uses( 'cloudfiles/cloudfiles' );


class VideoHandlerComponent extends Object {
	
	private $controller;

	/**
	 * array of all allowed video file extentions used to 
	 * verify uploaded videos
	 */
	private $allowed_ext = array('flv', 'mp4', 'wmv', 'avi');
	
	/**
	 * once verified this holds a reference to the image file that will be 
	 * manipulated
	 */
	private $video = 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';
	
	/**
	 * the new unique file name the currently set video was saved to
	 */
	private $new_name = null;
	
	/**
	 *
	 */
	private $fileInfo = null;
	
	/**
	 *
	 */
	private $auth = null;
	
	/**
	 *
	 */
	private $conn = null;
	
	/**
	 * file extention for files after they have been encoded
	 */
	public $output_ext = 'mp4';
	
	
	/**
	 * 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) {
		
		vendor('getid3');
		$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 file for errors and verifies its safe to use
	 * file must be uploaded with correct file size and be of a valid type
	 * as defined in the allowed_ext array	 * 
	 *
	 * @param array $video - the $_FILE[video_file] array created by the file upload
	 * @return boolean success 
	 */
	public function verifyUpload( $video ) {
		
		//check for upload errors
		if($video['error'] > 0 ) {
			switch( $video['error'] ) {
				case 1:
				case 2:
					$this->controller->returnError( array('file' => 'video_max_upload_size'), true);
				break;
				case 3:
					$this->controller->returnError( array('file' => 'video_partial_upload'), true);
				break;
				case 4:
					$this->controller->returnError( array('file' => 'no_video_uploaded'), true);
				break;
			}
		}
		
		//confirm its an uploaded file
		if( !is_uploaded_file( $video['tmp_name'] ) ) {
			$this->controller->returnError( array('file' => 'video_not_uploaded'), true);
		}
		
		//confirm file extention
		$ext = extention( $video['name'] );
		if( !in_array( $ext, $this->allowed_ext ) ) {
			$this->controller->returnError( array('file' => 'video_wrong_filetype'), true);
		}
		
		//every thing is good set video
		return $this->setVideo( $video['tmp_name'], $video['name'] );
	}
	
	/**
	 * sets a video to be operated on by some methods of this class
	 * the video data is analized and the info is saved to properties of the class
	 *
	 * @param string $video - the file path to the uploaded video ( would be $_FILE['temp_name'] for file uploades )
	 * @param string $name - the name of the video file
	 * @return boolean success 
	 */
	public function setVideo( $video, $name=null ) {
		
		$getID3 = new getID3;
		$this->fileInfo = $getID3->analyze( $video );
		$this->video = $video;
		$this->ext = '.'.extention( $name );
		$this->original_name = str_replace($this->ext, '', $name);
		return true;
	}
	
	/**
	 * moves the set video to the destination container and gives it a unique name
	 * MUST call setVideo, or verifyUpload before calling this
	 *
	 * @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 ) {
		
		try {
			$this->new_name = time().$this->ext;
			$con = $this->conn->get_container( $container );
			$object = $con->create_object( $this->new_name );
			$object->load_from_filename( $this->video );
			return $this->new_name;
		} catch( Exception $e ) {
			$this->log("Unable to move video file: Container = $container, file = $file, ERROR: ". $e->getMessage() );	
		}
		return false;
	}
	
	/**
	 * sends a request to Encoding.com to have a video encoded.
	 *
	 *
	 * @param string $input_file - the name of the video file to encode. It must be stored in Cloud File container TEMP_VIDEO_CONTAINER
	 * @param int $id - the id of the content item that is using this video
	 * @return  boolean success
	 */
	public function encodeVideo($input_file, $id ) {
		
		$xml = $this->buildEncodeRequest( $input_file, $id );
		$res = $this->sendEncodeRequest( $xml );
		try {
			$response = new SimpleXMLElement($res);
		   
			if(isset($response->errors[0]->error[0])) {
				$error = $response->errors[0]->error[0] . ''; 
			} else if ($response->message[0]) {
				return true;
			}
		} catch(Exception $e) {
			// If wrong XML response received
			$error = $e->getMessage();
		}
	   	$this->log( $error );
		return false;
	}
	
	/**
	 * removes and video file from Cloud File Storage
	 *
	 * @param string $container - the Cloud File container the video file is stored in
	 * @param string $file - the name of the file
	 * @return boolean success
	 */	
	public function delete( $container, $file ) {
		
		try {
			$con = $this->conn->get_container( $container );
			if( $con->delete_object( $file ) ) {
				return true;
			}
		} catch(Exception $e ) {
			$this->log("Unsuccessfully deleted video: Container = $container, file = $file, ERROR: ". $e->getMessage() );	
		} 
		return false;
	}
	
	/**
	 * sends a xml document to Encoding.com
	 *
	 * @param xml Document $xml - a xml document reqest for encoding.com
	 * @return the reqiest object
	 */
	private function sendEncodeRequest( $xml ) {
		
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, "http://manage.encoding.com/");
		curl_setopt($ch, CURLOPT_POSTFIELDS, "xml=" . urlencode($xml));
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_HEADER, 0);
		return curl_exec($ch);
	}  
	
	/**
	 * build an xml request document to be sent to encoding.com to have a video file transcoded
	 *
	 * @param string $input_file - the name of the file to be transcoded ( file must be stored in Cloud Files TEMP_VIDEO_CONTAINER )
	 * @param int $id - the id of the content item that is using the video file 
	 * @return xml Document - the xml document to send to encoding.com to queue the request to transcode
	 */
	private function buildEncodeRequest( $input_file, $id ) { 

		$xml = new SimpleXMLElement('<?xml version="1.0"?><query></query>');
		$xml->addChild('userid', ENCODING_ID);
		$xml->addChild('userkey', ENCODING_KEY);
		$xml->addChild('action', 'AddMedia');
		$xml->addChild('mediaid', $id);
		$xml->addChild('notify', APP_ROOT.'encoding/responce');
		$xml->addChild('source', 'http://'.CF_USERNAME.':'.CF_API_KEY.'@storage.cloudfiles.com/'.TEMP_VIDEO_CONTAINER.'/'.$input_file);
	   
		$fNode = $xml->addChild('format');
			$fNode->addChild('output', 'mp4');// if this is changed then $this->output_ext must be changed to what ever the post transcoded file type will be
			$fNode->addChild('video_codec', 'libx264');
			$fNode->addChild('two_pass', 'yes'); 
			$fNode->addChild('keyframe', '300'); 
			$fNode->addChild('profile', 'high');
			$fNode->addChild('audio_codec', 'libfaac');
			$fNode->addChild('audio_channels_number', '2');
			$fNode->addChild('audio_volume', '100');
			$fNode->addChild('audio_bitrate', '128k');
			$vNode = $fNode->addChild('video_codec_parameters');
				$vNode->addChild('me_method','hex');
				$vNode->addChild('flags2','+bpyramid+wpred+mixed_refs+dct8x8');
				$vNode->addChild('subq','7');
				$vNode->addChild('trellis','1');
				$vNode->addChild('bf','3');
				$vNode->addChild('partitions','+parti4x4+parti8x8+partp4x4+partp8x8+partb8x8');
			$fNode->addChild('destination', 'http://'.CF_USERNAME.':'.CF_API_KEY.'@storage.cloudfiles.com/'.VIDEO_CONTAINER.'/'.$this->getOutputFileName($input_file) );
		
		return $xml->asXML();	
	}
	
	/**
	 * get the name of the set video
	 *
	 * @return string - the original file name (without extention
	 */
	public function getOrigName() {
		
		return $this->original_name;
	}
	
	/**
	 * get the name of the set video
	 *
	 * @return string - the original file name (without extention
	 */
	public function getOutputFileName() {
		
		$ext = extention( $this->new_name );
		return str_replace($ext, $this->output_ext, $this->new_name);
	}
	
	/**
	 * get the length of the video
	 *
	 * @return int - the length of the set video in seconds
	 */
	public function getLength() {
		
		return isset($this->fileInfo['playtime_seconds']) ? floor($this->fileInfo['playtime_seconds']) : 0;
	}
}
?>