<?php

class Media extends PhpObject {
	
	//DB Tables
	const MEDIA_TABLE = 'medias';
	
	//Media folders
	const MEDIA_FOLDER = 'files/';
	const THUMB_FOLDER = self::MEDIA_FOLDER.'thumbs/';
	
	const THUMB_MAX_WIDTH = 400;
	
	/**
	 * Database Handle
	 * @var Db
	 */
	private $oDb;
	
	/**
	 * Media Project
	 * @var Project
	 */
	private $oProject;
	private $asMedia;
	private $asMedias;
	private $sSystemType;
	
	private $iMediaId;
	
	public function __construct(Db &$oDb, &$oProject, $iMediaId=0) {
		parent::__construct(__CLASS__, Settings::DEBUG);
		$this->oDb = &$oDb;
		$this->oProject = &$oProject;
		$this->asMedia = array();
		$this->asMedias = array();
		$this->sSystemType = (substr(php_uname(), 0, 7) == "Windows")?'win':'unix';
		$this->setMediaId($iMediaId);
	}
		
	public function setMediaId($iMediaId) {
		$this->iMediaId = $iMediaId;
	}
	
	public function getMediaId() {
		return $this->iMediaId;
	}
	
	public function getProjectCodeName() {
		return $this->oProject->getProjectCodeName();
	}
	
	public function setComment($sComment) {
		$sError = '';
		$asData = array();
		if($this->iMediaId > 0) {
			$bResult = $this->oDb->updateRow(self::MEDIA_TABLE, $this->iMediaId, array('comment'=>$sComment));
			if(!$bResult) $sError = 'error_commit_db';
			else $asData = $this->getInfo();
		}
		else $sError = 'media_no_id';
		
		return Spot::getResult(($sError==''), $sError, $asData);
	}
	
	public function getMediasInfo($iMediaId=0) {
		$bOwnMedia = ($iMediaId > 0);
		if($bOwnMedia && empty($this->asMedia) || !$bOwnMedia && empty($this->asMedias)) {
			if($this->oProject->getProjectId()) {
				$asParams = array(
					'select'	=> array(Db::getId(self::MEDIA_TABLE), 'filename', 'taken_on', 'posted_on', 'rotate', 'type AS subtype', 'comment'),
					'from'		=> self::MEDIA_TABLE,
					'constraint'=> array(Db::getId(Project::PROJ_TABLE) => $this->oProject->getProjectId())
				);
				if($bOwnMedia) $asParams['constraint'][Db::getId(self::MEDIA_TABLE)] = $iMediaId;
				
				$asMedias = $this->oDb->selectRows($asParams);
				foreach($asMedias as &$asMedia) {
					$asMedia['media_path'] = self::getMediaPath($asMedia['filename']);
					$asMedia['thumb_path'] = $this->getMediaThumbnail($asMedia['filename']);
				}
				
				if(!empty($asMedias)) {
					if($bOwnMedia) $this->asMedia = array_shift($asMedias);
					else $this->asMedias = $asMedias;
				}
			}
		}
		return $bOwnMedia?$this->asMedia:$this->asMedias;
	}
	
	public function getInfo() {
		return $this->getMediasInfo($this->iMediaId);
	}
	
	public function isProjectModeValid() {
		return ($this->oProject->getMode() == Project::MODE_BLOG);
	}
	
	public function addMedia($sMediaName, $sMethod='upload') {
		$sError = '';
		$asParams = array();
		if(!$this->isProjectModeValid() && $sMethod!='sync') {
			$sError = 'upload_wrong_mode';
			$asParams[] = $this->oProject->getProjectCodeName();
		}
		elseif($this->oDb->pingValue(self::MEDIA_TABLE, array('filename'=>$sMediaName)) && $sMethod!='sync') {
			$sError = 'upload_media_exist';
			$asParams[] = $sMediaName;
		}
		else {
			//Add media to DB
			$asMediaInfo = $this->getMediaInfoFromFile($sMediaName);
			$asDbInfo = array(
				Db::getId(Project::PROJ_TABLE)	=> $this->oProject->getProjectId(),
				'filename'						=> $sMediaName,
				'taken_on'						=> ($asMediaInfo['taken_ts'] > 0)?date(Db::TIMESTAMP_FORMAT, $asMediaInfo['taken_ts']):0, 	//Site Time (Settings::TIMEZONE)
				'posted_on'						=> date(Db::TIMESTAMP_FORMAT, $asMediaInfo['file_ts']),										//Site Time
				'rotate'						=> $asMediaInfo['rotate'],
				'type'							=> $asMediaInfo['type']
			);
			
			if($sMethod=='sync')	$iMediaId = $this->oDb->insertUpdateRow(self::MEDIA_TABLE, $asDbInfo, array(Db::getId(Project::PROJ_TABLE), 'filename'));
			else					$iMediaId = $this->oDb->insertRow(self::MEDIA_TABLE, $asDbInfo);
			
			if(!$iMediaId) $sError = 'error_commit_db';
			else {
				$this->setMediaId($iMediaId);
				$asParams = $this->getInfo(); //Creates thumbnail
			}
		}
		
		return Spot::getResult(($sError==''), $sError, $asParams);
	}
	
	/**
	 * One-shot function to initialize DB with existing images
	 */
	public function syncFileFolder() {
		$asMediaPaths = glob(self::getMediaPath('*.{jpg,JPG,jpeg,JPEG,png,PNG,mov,MOV}'), GLOB_BRACE);
		foreach($asMediaPaths as $sMediaPath)
		{
			$sMediaName = pathinfo($sMediaPath, PATHINFO_BASENAME);
			$this->addMedia($sMediaName, 'sync');
		}
		$this->setExtractMode(PhpObject::MODE_HTML);
		return $this->getCleanMessageStack();
	}
	
	private function getMediaInfoFromFile($sMediaName)
	{
		$sMediaPath = self::getMediaPath($sMediaName);
		$sType = self::getMediaType($sMediaName);
		
		$iTimeStamp = $iTakenOn = $iPostedOn = 0;
		$sRotate = '0';
		$sTakenOn = '';
		switch($sType) {
			case 'video':
				$asResult = array();
				$sParams = implode(' ', array(
					'-loglevel error', //Remove comments
					'-select_streams v:0', //First video channel
					'-show_entries stream_tags=rotate,creation_time', //filter tags :rotation & creation time only
					'-print_format json', //output format: json
					'-i' //input file
				));
				exec('ffprobe '.$sParams.' "'.$sMediaPath.'"', $asResult);
				$asResult = json_decode(implode('', $asResult), true);
				
				//Timestamps
				$sTakenOn = date(Db::TIMESTAMP_FORMAT, strtotime($asResult['streams'][0]['tags']['creation_time']));
				$iPostedOn = filemtime($sMediaPath);
				
				//Orientation
				if(isset($asResult['streams'][0]['tags']['rotate'])) $sRotate = $asResult['streams'][0]['tags']['rotate'];
				break;
			case 'image': 
				$asExif = @exif_read_data($sMediaPath, 0, true);
				if(!$asExif) $asExif['FILE']['FileDateTime'] = filemtime($sMediaPath);
				
				//Timestamps
				if(array_key_exists('EXIF', $asExif) && array_key_exists('DateTimeOriginal', $asExif['EXIF'])) $sTakenOn = $asExif['EXIF']['DateTimeOriginal'];
				if(array_key_exists('FILE', $asExif) && array_key_exists('FileDateTime', $asExif['FILE'])) $iPostedOn = $asExif['FILE']['FileDateTime'];
				
				//Orientation
				if(array_key_exists('IFD0', $asExif) && array_key_exists('Orientation', $asExif['IFD0'])) {
					switch($asExif['IFD0']['Orientation'])
					{
						case 1:	$sRotate = '0';		break; //None
						case 3:	$sRotate = '180';	break; //Flip over
						case 6:	$sRotate = '90';	break; //Clockwise
						case 8:	$sRotate = '-90';	break; //Trigo
					}
				}
				break;
		}
		
		//Media info do not have any TZ: Interpreting date time using project timezone (assuming all medias have been taken in this time zone)
		if($sTakenOn != '') {
			$oTakenOn = new DateTime($sTakenOn, new DateTimeZone($this->oProject->getTimeZone()));
			$iTakenOn = $oTakenOn->format('U');
		}
		
		//Merge timestamps
		$iTimeStamp = ($iTakenOn > 0)?$iTakenOn:$iPostedOn;
		
		return array(
			'timestamp'	=> $iTimeStamp,
			'taken_ts'	=> $iTakenOn,
			'file_ts'	=> $iPostedOn,
			'rotate'	=> $sRotate,
			'type'		=> $sType
		);
	}
	
	private function getMediaThumbnail($sMediaName)
	{
		$sMediaPath = self::getMediaPath($sMediaName);
		$sThumbPath = self::getMediaPath($sMediaName, 'thumbnail');
		
		if(!file_exists($sThumbPath)) {
			$sType = self::getMediaType($sMediaName);
			switch($sType) {
				case 'image':
					$asThumbInfo = ToolBox::createThumbnail($sMediaPath, self::THUMB_MAX_WIDTH, 0, $sThumbPath);
					break;
				case 'video':
					//Get a screenshot of the video 1 second in
					$sTempPath = self::getMediaPath(uniqid('temp_').'.png');
					$asResult = array();
					$sParams = implode(' ', array(
						'-i "'.$sMediaPath.'"',	//input file
						'-ss 00:00:01.000',		//Image taken after x seconds
						'-vframes 1',			//number of video frames to output
						'"'.$sTempPath.'"',		//output file
					));
					exec('ffmpeg '.$sParams, $asResult);
					
					//Resize
					$asThumbInfo = ToolBox::createThumbnail($sTempPath, self::THUMB_MAX_WIDTH, 0, $sThumbPath, true);
					break;
			}
			
		}
		else $asThumbInfo = array('error'=>'', 'out'=>$sThumbPath);
		
		return ($asThumbInfo['error']=='')?$asThumbInfo['out']:$sMediaPath;
	}
	
	private static function getMediaPath($sMediaName, $sFileType='media') {
		if($sFileType=='thumbnail') return self::THUMB_FOLDER.$sMediaName.(strtolower(substr($sMediaName, -3))=='mov'?'.png':'');
		else return self::MEDIA_FOLDER.$sMediaName;
	}
	
	private static function getMediaType($sMediaName) {
		$sMediaPath = self::getMediaPath($sMediaName);
		$sMediaMime = mime_content_type($sMediaPath);
		switch($sMediaMime) {
			case 'video/quicktime':	$sType = 'video';	break;
			default:				$sType = 'image';	break;
		}
		
		return $sType;
	}
}