<?php
	/**
	 * This file contains the QImageBase class.
	 *
	 * @package Controls
	 */

	/**
	 * @package Controls
	 *
	 * @property string $CacheFolder
	 * @property string $CacheFilename
	 * @property string $AlternateText
	 * @property string $ImageType
	 * @property integer $JpegQuality
	 */
	abstract class QImageBase extends QControl {
		///////////////////////////
		// Private Member Variables
		///////////////////////////

		// APPEARANCE
		protected $strAlternateText;
		protected $strImageType = QImageType::Png;
		protected $intJpegQuality = 100;

		// CacheFolder Location for Cached Generated Images (if applicable)
		protected $strCacheFolder = null;
		protected $strCacheFilename = null;

		// Internally Used
		protected $strCachedActualFilePath = null;
		protected $strControlClassName = 'QImageBase';
		protected $strImagickTempFilePath = '/tmp';

		//////////
		// Methods
		//////////
		public function ParsePostData() {}

		/**
		 * Validates the control. For now, just returns true (since there is nothing to validate in the control)
		 * @return bool
		 */
		public function Validate() {return true;}

		/**
		 * Constructor method
		 *
		 * @param QControl|QControlBase|QForm $objParentObject
		 * @param null|string                        $strControlId
		 *
		 * @throws Exception
		 * @throws QCallerException
		 */
		public function __construct($objParentObject, $strControlId = null) {
			if ($objParentObject)
				parent::__construct($objParentObject, $strControlId);
		}

		public function RenderAsImgSrc($blnDisplayOutput = true) {
			// Serialize and Hash Data
			$strSerialized = $this->Serialize();
			$strHash = md5($strSerialized);

			// Figure Out Image Filename
			if ($this->strCacheFilename)
				$strImageFilename = $this->strCacheFilename;
			else {
				$strImageFilename = $strHash;

				switch ($this->strImageType) {
					case QImageType::Gif:
						$strImageFilename .= '.gif';
						break;
					case QImageType::AnimatedGif:
						$strImageFilename .= '.gif';
						break;
					case QImageType::Jpeg:
						$strImageFilename .= '.jpg';
						break;
					default:
						$strImageFilename .= '.png';
						break;
				}
			}

			// Figure out IMG SRC path based on Caching prefs
			if ($this->strCacheFolder) {
				$strFilePath = sprintf('%s%s/%s',
					__DOCROOT__,
					str_replace(__VIRTUAL_DIRECTORY__, '', $this->strCacheFolder),
					$strImageFilename);

				if (!file_exists($strFilePath))
					$this->RenderImage($strFilePath);

				$strPath = sprintf('%s/%s',
					$this->strCacheFolder,
					$strImageFilename);

				// Store Cache Filepath Info
				$this->strCachedActualFilePath = $strFilePath;
			} else {
				$strPath = sprintf('%s/image_base.php/%s/%s?q=%s',
					__VIRTUAL_DIRECTORY__ . __PHP_ASSETS__,
					$this->strControlClassName,
					$strImageFilename,
					$strSerialized
				);
			}

			// Output or Display
			if ($blnDisplayOutput)
				print($strPath);
			else
				return $strPath;
		}

		/**
		 * Returns the HTML for rendering the control in the browser
		 *
		 * @return string HTML for the control
		 * @throws Exception
		 * @throws QCallerException
		 */
		protected function GetControlHtml() {
			try {
				// Figure Out the Path
				$strPath = $this->RenderAsImgSrc(false);
			} catch (QCallerException $objExc) {
				$objExc->IncrementOffset();
				throw $objExc;
			}

			if ($this->strCachedActualFilePath) {
				$objDimensions = getimagesize($this->strCachedActualFilePath);

				// Setup Style and Other Attribute Information (EXCEPT for "BackColor")
				// Use actual "Width" and "Height" values from cached image
				$strBackColor = $this->strBackColor;
				$strWidth = $this->strWidth;
				$strHeight = $this->strHeight;
				$this->strBackColor = null;
				$this->strWidth = $objDimensions[0];
				$this->strHeight = $objDimensions[1];
				$strStyle = $this->GetStyleAttributes();
				if ($strStyle)
					$strStyle = sprintf(' style="%s"', $strStyle);
				$this->strBackColor = $strBackColor;
				$this->strWidth = $strWidth;
				$this->strHeight = $strHeight;
			} else {
				// Setup Style and Other Attribute Information (EXCEPT for "BackColor", "Width" and "Height")
				$strBackColor = $this->strBackColor;
				$strWidth = $this->strWidth;
				$strHeight = $this->strHeight;
				$this->strBackColor = null;
				$this->strWidth = null;
				$this->strHeight = null;
				$strStyle = $this->GetStyleAttributes();
				if ($strStyle)
					$strStyle = sprintf(' style="%s"', $strStyle);
				$this->strBackColor = $strBackColor;
				$this->strWidth= $strWidth;
				$this->strHeight = $strHeight;
			}

			$strAlt = null;
			if ($this->strAlternateText)
				$strAlt = ' alt="' . QApplication::HtmlEntities($this->strAlternateText) . '"';

			// Render final "IMG SRC" tag
			$strToReturn = sprintf('<img src="%s" %s%s%s/>',
				$strPath,
				$this->GetAttributes(),
				$strStyle,
				$strAlt);
			return $strToReturn;
		}

		public function Serialize() {
			$objControl = clone($this);
			$objControl->objForm = null;
			$objControl->objParentControl = null;
			$objControl->strCacheFilename = null;
			$objControl->strCachedActualFilePath = null;
			$objControl->blnOnPage = null;
			$objControl->blnModified = null;
			$objControl->strControlId = null;
			$objControl->strRenderMethod = null;
			$objControl->blnOnPage = null;
			$objControl->blnRendered = null;
			$objControl->blnRendering = null;
			$objControl->objActionArray = null;
			$objControl->objChildControlArray = null;
			$objControl->blnWrapperModified = null;
			$objControl->strValidationError = null;
			$objControl->strWarning = null;

			$strData = serialize($objControl);

			if (function_exists('gzcompress'))
				$strData = base64_encode(gzcompress($strData, 9));
			else
				$strData = base64_encode($strData);

			$strData = str_replace('+', '-', $strData);
			$strData = str_replace('/', '_', $strData);
			
			return $strData;
		}
		
		public static function Run() {
			$strData = QApplication::QueryString('q');
			$strData = str_replace('-', '+', $strData);
			$strData = str_replace('_', '/', $strData);

			$strData = base64_decode($strData);

			if (function_exists('gzcompress'))
				$strData = gzuncompress($strData);

			$objControl = unserialize($strData);
			$objControl->RenderImage();
		}

		abstract public function RenderImage($strPath = null);

		/**
		 * Used by custom RenderImage method to output the final image.
		 * Uses $this->strImageType to determine type of image to be rendered.
		 * This version is to be used when rendering an image using the GD library.
		 * 
		 * If strPath is not set, output to the screen.  If it is, save to strPath.
		 *
		 * @param resource $objFinalImage image in GD format
		 * @param string $strPath
		 */
		protected function RenderImageHelper($objFinalImage, $strPath) {
			// Output the Image (if path isn't specified, output to buffer.  Otherwise, output to disk)
			if (!$strPath) {
				// Output to Output Stream
				QApplication::$ProcessOutput = false;
				header('Cache-Control: cache');
				header('Expires: Wed, 20 Mar 2019 05:00:00 GMT');
				header('Pragma: cache');

				switch ($this->strImageType) {
					case QImageType::Gif:
						header('Content-Type: image/gif');
						imagegif($objFinalImage);
						break;
					case QImageType::AnimatedGif:
						header('Content-Type: image/gif');
						imagegif($objFinalImage);
						break;
					case QImageType::Jpeg:
						header('Content-Type: image/jpeg');
						imagejpeg($objFinalImage, null, $this->intJpegQuality);
						break;
					default:
						header('Content-Type: image/png');
						imagepng($objFinalImage);
						break;
				}
			} else {
				// Make Directory
				QApplication::MakeDirectory(dirname($strPath), 0777);

				// Output to Disk
				switch ($this->strImageType) {
					case QImageType::Gif:
						imagegif($objFinalImage, $strPath);
						break;
					case QImageType::AnimatedGif:
						imagegif($objFinalImage, $strPath);
						break;
					case QImageType::Jpeg:
						imagejpeg($objFinalImage, $strPath, $this->intJpegQuality);
						break;
					default:
						imagepng($objFinalImage, $strPath);
						break;
				}
				chmod($strPath, 0777);
			}

			imagedestroy($objFinalImage);
		}

		/**
		 * Used by custom RenderImage method to output the final image.
		 * Uses $this->strImageType to determine type of image to be rendered.
		 * This version is to be used when rendering an image using the Imagick library.
		 * 
		 * If strPath is not set, output to the screen.  If it is, save to strPath.
		 *
		 * @param Imagick $objFinalImage image as an instance of the Imagick class
		 * @param string $strPath
		 */
		protected function RenderImageMagickHelper($objFinalImage, $strPath) {
			// Output the Image (if path isn't specified, output to buffer.  Otherwise, output to disk)
			if (!$strPath) {
				$strPath = $this->strImagickTempFilePath . '/image_' . str_replace('.', '_', microtime(true));

				// Output to a temporary location
				switch ($this->strImageType) {
					case QImageType::Gif:
						$strPath .= '.gif';
						$objFinalImage->setImageFormat('gif');
						header('Content-Type: image/gif');
						break;
					case QImageType::AnimatedGif:
						$strPath .= '.gif';
						$objFinalImage->setImageFormat('gif');
						header('Content-Type: image/gif');
						break;
					case QImageType::Jpeg:
						$strPath .= '.jpg';
						$objFinalImage->setImageFormat('jpeg');
						$objFinalImage->setCompressionQuality($this->intJpegQuality);
						header('Content-Type: image/jpeg');
						break;
					default:
						$strPath .= '.png';
						$objFinalImage->setImageFormat('png');
						header('Content-Type: image/png');
						break;
				}

				if ($this->strImageType == QImageType::AnimatedGif)
					file_put_contents($strPath, $objFinalImage->GetImagesBlob());
				else
					$objFinalImage->writeImage($strPath);

				QApplication::$ProcessOutput = false;
				header('Cache-Control: cache');
				header('Expires: Wed, 20 Mar 2019 05:00:00 GMT');
				header('Pragma: cache');

				print(file_get_contents($strPath));
				unlink($strPath);

			} else {
				// Make Directory
				QApplication::MakeDirectory(dirname($strPath), 0777);

				// Output to Disk
				switch ($this->strImageType) {
					case QImageType::Gif:
						$objFinalImage->setImageFormat('gif');
						break;
					case QImageType::AnimatedGif:
						$objFinalImage->setImageFormat('gif');
						break;
					case QImageType::Jpeg:
						$objFinalImage->setImageFormat('jpeg');
						$objFinalImage->setCompressionQuality($this->intJpegQuality);
						break;
					default:
						$objFinalImage->setImageFormat('png');
						break;
				}

				$objFinalImage->writeImage($strPath);
				chmod($strPath, 0777);
			}

			$objFinalImage->Destroy();
		}

		/////////////////////////
		// Public Properties: GET
		/////////////////////////
		/**
		 * PHP magic method to set a property of this class
		 *
		 * @param string $strName Name of the property
		 *
		 * @return int|mixed|null|string
		 * @throws Exception
		 * @throws QCallerException
		 */
		public function __get($strName) {
			switch ($strName) {
				// MISCELLANEOUS
				case "CacheFolder": return $this->strCacheFolder;
				case "CacheFilename": return $this->strCacheFilename;
				case "AlternateText": return $this->strAlternateText;
				case "ImageType": return $this->strImageType;
				case "JpegQuality": return $this->intJpegQuality;

				default:
					try {
						return parent::__get($strName);
					} catch (QCallerException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
			}
		}

		/////////////////////////
		// Public Properties: SET
		/////////////////////////
		/**
		 * PHP magic method to set the value of class properties
		 *
		 * @param string $strName Name of the property
		 * @param string $mixValue Value of the property
		 *
		 * @return mixed|void
		 * @throws Exception
		 * @throws QCallerException
		 * @throws QInvalidCastException
		 */
		public function __set($strName, $mixValue) {
			$this->blnModified = true;

			switch ($strName) {
				case "CacheFolder":
					try {
						$this->strCacheFolder = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case "CacheFilename":
					try {
						$this->strCacheFilename = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case "AlternateText":
					try {
						$this->strAlternateText = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case "ImageType":
					try {
						$this->strImageType = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case "JpegQuality":
					try {
						$this->intJpegQuality = QType::Cast($mixValue, QType::Integer);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				default:
					try {
						parent::__set($strName, $mixValue);
					} catch (QCallerException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
					break;
			}
		}
	}