<?php
/**
* @version	$Id: image_tag_processor.php 16513 2017-01-20 14:10:53Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

defined('FULL_PATH') or die('restricted access!');

class ImageTagProcessor extends kDBTagProcessor {

	/**
	 * Prepares all image parameters as list block parameters (for easy usage)
	 *
	 * @param kDBList $object
	 * @param Array $block_params
	 * @return void
	 * @access protected
	 * @author Alex
	 */
	protected function PrepareListElementParams(&$object, &$block_params)
	{
		$image_url = $this->ImageSrc($block_params);
		if ( !$image_url ) {
			return ;
		}

		$parent_prefix = $this->Application->getUnitOption($object->Prefix, 'ParentPrefix');

		/** @var kDBItem $parent_item */
		$parent_item = $this->Application->recallObject($parent_prefix);

		$block_params['img_path'] = $image_url;
		$image_dimensions = $this->ImageSize($block_params);
		$block_params['img_size'] = $image_dimensions ? $image_dimensions : ' width="' . $block_params['DefaultWidth'] . '"';
		$block_params['alt'] = $object->GetField('AltName') ? $object->GetField('AltName') : $this->getItemTitle($parent_item);
		$block_params['align'] = array_key_exists('align', $block_params) ? $block_params['align'] : 'left';

		// TODO: consider escaping in template instead
		$block_params['alt'] = kUtil::escape($block_params['alt']);
	}

	/**
	 * Returns value of object's title field
	 *
	 * @param kDBItem $object
	 * @return string
	 * @access protected
	 */
	protected function getItemTitle(&$object)
	{
		$title_field = $this->Application->getUnitOption($object->Prefix, 'TitleField');

		return $object->GetField($title_field);
	}

	/**
	 * [AGGREGATED TAGS] works as <inp2:CatalogItemPrefix_Image, ImageSize, ImageSrc ..../>
	 *
	 * @param Array $params
	 * @return string
	 */
	function ItemImageTag($params)
	{
		$this->LoadItemImage($params);
		$tag_name = $params['original_tag'];

		return $this->$tag_name($params);
	}

	function LargeImageExists($params)
	{
		$object = $this->getObject($params);
		if ($object->GetDBField('SameImages') == null || $object->GetDBField('SameImages') == 1) {
			return false;
		}
		else {
			return true;
		}
	}

	function LoadItemImage($params)
	{
		/** @var kCatDBItem $parent_item */
		$parent_item = $this->Application->recallObject($params['PrefixSpecial']);

		/** @var kDBItem $object */
		$object = $this->Application->recallObject($this->getPrefixSpecial(), null, Array('skip_autoload' => true));

		$object->Clear();

		// if we need primary thumbnail which is preloaded with category item's list
		$is_primary = $this->SelectParam($params, 'primary,Primary');
		$image_name = $this->SelectParam($params, 'name,Name');
		$image_field = $this->SelectParam($params, 'field,Field'); // ie. virtual names PrimaryImage, Image1, Image2
		$image_id = $this->Application->GetVar($this->Prefix.'_id');

		if (
			// is primary, when primary mark set OR name & field not given
			($is_primary || !($image_name || $image_field)) &&

			// primary image is preloaded AND direct id not given
			$parent_item->isField('ThumbPath') && !$image_id
		) {
			if (is_null($parent_item->GetDBField('SameImages'))) {
				// JOIN definetly failed, because it's not-null column
				$object->setLoaded(false);
			}
			else {
				$object->SetDBField('Url', $parent_item->GetDBField('FullUrl'));

				$object->SetDBFieldsFromHash($parent_item->GetFieldValues(), Array('AltName', 'SameImages', 'LocalThumb', 'ThumbPath', 'ThumbUrl', 'LocalImage', 'LocalPath'));

				if (!$object->GetDBField('AltName')) {
					$object->SetDBField('AltName', $this->getItemTitle($parent_item));
				}

				$object->setLoaded();
			}
		}
		else { // if requested image is not primary thumbnail - load it directly
			$id_field = $this->Application->getUnitOption($this->Prefix, 'ForeignKey');
			$parent_table_key = $this->Application->getUnitOption($this->Prefix, 'ParentTableKey');

			$keys[$id_field] = $parent_item->GetDBField($parent_table_key);

			// which image to load?
			if ($is_primary) {
				// by PrimaryImage mark
				$keys['DefaultImg'] = 1;
			}
			elseif ($image_name) {
				// by ImageName
				$keys['Name'] = $image_name;
			}
			elseif ($image_field) {
				// by virtual field name in main object
				$field_options = $parent_item->GetFieldOptions( $image_field );
				$keys['Name'] = isset($field_options['original_field']) ? $field_options['original_field'] : $image_field;
			}
			elseif ($image_id) {
				// by ID
				$keys['ImageId'] = $image_id;
			}
			else {
				// by PrimaryImage if no other criteria given
				$keys['DefaultImg'] = 1;
			}

			$object->Load($keys);

			if ( $image_field ) {
				$image_src = $parent_item->GetDBField( $image_field );

				// when image is uploaded to virtual field in main item, but not saved to db
				$object->SetDBField('ThumbPath', $image_src);

				if (!$object->isLoaded() && $image_src) {
					// set fields for displaying new image during main item suggestion with errors
					$fields_hash = Array (
						'Url' => '',
						'ThumbUrl' => '',
						'LocalPath' => '',

						'SameImages' => 1,
						'LocalThumb' => 1,
						'LocalImage' => 1,
					);

					$object->SetDBFieldsFromHash($fields_hash);
					$object->setLoaded();
				}
			}
		}
	}

	function getImageDimension($type, $params)
	{
		$ret = isset($params['Max'.$type]) ? $params['Max'.$type] : false;
		if (!$ret) {
			return $ret;
		}
		$parent_prefix = $this->Application->getUnitOption($this->Prefix, 'ParentPrefix');

		if ($ret == 'thumbnail') {
			$ret = $this->Application->ConfigValue($parent_prefix.'_ThumbnailImage'.$type);
		}
		if ($ret == 'fullsize') {
			$ret = $this->Application->ConfigValue($parent_prefix.'_FullImage'.$type);
		}

		return $ret;
	}

	/**
	 * Appends "/" to beginning of image path (in case when missing)
	 *
	 * @param kDBItem $object
	 * @todo old in-portal doesn't append first slash, but we do => append first slash for him :)
	 */
	function makeRelativePaths(&$object)
	{
		$thumb_path = $object->GetDBField('ThumbPath');
		if ($thumb_path && substr($thumb_path, 0, 1) != DIRECTORY_SEPARATOR) {
			$object->SetDBField('ThumbPath', DIRECTORY_SEPARATOR . $thumb_path);
		}

		$local_path = $object->GetDBField('LocalPath');
		if ($local_path && substr($local_path, 0, 1) != DIRECTORY_SEPARATOR) {
			$object->SetDBField('LocalPath', DIRECTORY_SEPARATOR . $local_path);
		}
	}

	function ImageSrc($params)
	{
		/** @var kDBItem $object */
		$object = $this->getObject($params);

		$this->makeRelativePaths($object);

		// show "noimage.gif" when requested image is missing OR was not uploaded
		$use_default_image = !(defined('DBG_IMAGE_RECOVERY') && DBG_IMAGE_RECOVERY);

		$src_image_url = $this->_getImageUrl($params);
		$src_image = $this->_getImagePath($src_image_url);

		if (!$object->isLoaded() || ($src_image_url && $src_image)) {
			// we can auto-resize image, when it is stored locally
			$max_width = $this->getImageDimension('Width', $params);
			$max_height = $this->getImageDimension('Height', $params);
			$format = array_key_exists('format', $params) ? $params['format'] : false;

			if (!$max_width && $format) {
				// user watermarks from format param
				$max_width = $format;
			}

			if ($max_width > 0 || $max_height > 0 || $format) {
				list ($max_width, $max_height) = $this->_transformParams($params, $max_width, $max_height);

				if ($object->isLoaded() && file_exists($src_image)) {
					/** @var ImageHelper $image_helper */
					$image_helper = $this->Application->recallObject('ImageHelper');

					return $image_helper->ResizeImage($src_image, $max_width, $max_height);
				}
				elseif ($use_default_image) {
					return $this->_getDefaultImage($params, $max_width, $max_height);
				}

				return $src_image_url;
			}
		}

		if ($src_image_url) {
			// convert full url to full path!
			$dst_image = $this->_getImagePath($src_image_url);
			$image_found = $dst_image ? file_exists($dst_image) : true;

			if ($image_found) {
				// image isn't deleted OR is stored on remote location
				return $src_image_url;
			}
		}

		// return Default Image or false if NOT specified (only for case, when SameImages = 0)
		return $use_default_image ? $this->_getDefaultImage($params) : $src_image_url;
	}

	/**
	 * Get location on disk for images, stored locally and false for remote images
	 *
	 * @param string $src_image
	 * @return string
	 */
	function _getImagePath($src_image)
	{
		if (!$src_image) {
			return false;
		}

		/** @var FileHelper $file_helper */
		$file_helper = $this->Application->recallObject('FileHelper');

		$dst_image = $file_helper->urlToPath($src_image);

		return $dst_image != $src_image ? $dst_image : false;
	}

	function _getImageUrl($params)
	{
		/** @var kDBItem $object */
		$object = $this->getObject($params);

		$base_url = rtrim($this->Application->BaseURL(), '/');

		// if we need thumbnail, or full image is same as thumbnail
		$show_thumbnail =	$this->SelectParam($params, 'thumbnail,Thumbnail') || // old style
							(isset($params['MaxWidth']) && $params['MaxWidth'] == 'thumbnail') || // new style
							(isset($params['MaxHeight']) && $params['MaxHeight'] == 'thumbnail');

		if ($show_thumbnail || $object->GetDBField('SameImages')) {
			// return local image or url
			$ret = $object->GetDBField('LocalThumb') ? $base_url . $object->GetDBField('ThumbPath') : $object->GetDBField('ThumbUrl');
		}
		else { // if we need full which is not the same as thumb
			$ret = $object->GetDBField('LocalImage') ? $base_url . $object->GetDBField('LocalPath') : $object->GetDBField('Url');
		}

		return $ret == $base_url ? '' : $ret;
	}

	/**
	 * Transforms Image/ImageSrc aggregated tag parameters into ones, that ResizeImage method understands
	 *
	 * @param Array $params
	 * @param int|bool $max_width
	 * @param int|bool $max_height
	 * @return Array
	 */
	function _transformParams($params, $max_width = false, $max_height = false)
	{
		$resize_format = 'resize:' . $max_width . 'x' . $max_height;

		$crop = $this->SelectParam($params, 'Crop,crop');
		if ($crop) {
			if (strpos($crop, '|') === false) {
				$crop = 'c|c';
			}

			$max_width = (is_null($max_height) ? $max_width : $resize_format) . ';crop:' . $crop;
			$max_height = null;
		}

		$fill = $this->SelectParam($params, 'Fill,fill');
		if ($fill) {
			$max_width = (is_null($max_height) ? $max_width : $resize_format) . ';fill:' . $fill;
			$max_height = null;
		}

		$watermark = $this->SelectParam($params, 'Watermark,watermark');
		if ($watermark) {
			$max_width = (is_null($max_height) ? $max_width : $resize_format) . ';wm:' . $watermark;
			$max_height = null;
		}

		return Array ($max_width, $max_height);
	}

	/**
	 * Returns default full url to default images
	 *
	 * @param Array $params
	 * @param int|bool $max_width
	 * @param int|bool $max_height
	 * @return string
	 */
	function _getDefaultImage($params, $max_width = false, $max_height = false)
	{
		$default_image = $this->SelectParam($params, 'default_image,DefaultImage');
		if (!$default_image) {
			return '';
		}

		// show default image, use different base urls for admin and front-end
		$base_url = rtrim($this->Application->BaseURL(), '/');
		$sub_folder = $this->Application->isAdmin ? rtrim(IMAGES_PATH, '/') : THEMES_PATH;

		if (($max_width !== false) || ($max_height !== false)) {
			/** @var ImageHelper $image_helper */
			$image_helper = $this->Application->recallObject('ImageHelper');

			$src_image = FULL_PATH . $sub_folder . '/' . $default_image;

			return $image_helper->ResizeImage($src_image, $max_width, $max_height);
		}

		return $base_url . $sub_folder . '/' . $default_image;
	}

	function getFullPath($path)
	{
		if (!$path) {
			return $path;
		}

		// absolute url
		if (preg_match('/^(.*):\/\/(.*)$/U', $path)) {
			/** @var FileHelper $file_helper */
			$file_helper = $this->Application->recallObject('FileHelper');

			return $file_helper->urlToPath($path);
		}

		// TODO: change to urlToPath usage later
		// relative url (we add sort of <inp2:m_TemplatesBase/> does

		return FULL_PATH . '/' . mb_substr(THEMES_PATH, 1) . '/' . kUtil::unescape($path, kUtil::ESCAPE_URL);
	}

	/**
	 * Makes size clause for img tag, such as
	 * ' width="80" height="100"' according to max_width
	 * and max_heght limits.
	 *
	 * @param array $params
	 * @return string
	 */
	function ImageSize($params)
	{
		$img_path = $this->getFullPath($params['img_path']);

		/** @var ImageHelper $image_helper */
		$image_helper = $this->Application->recallObject('ImageHelper');

		$max_width = $this->getImageDimension('Width', $params);
		$max_height = $this->getImageDimension('Height', $params);

		$image_dimensions = $image_helper->GetImageDimensions($img_path, $max_width, $max_height, $params);
		if (!$image_dimensions) {
			return false;
		}

		return ' width="'.$image_dimensions[0].'" height="'.$image_dimensions[1].'"';
	}

	/**
	 * Prepares image parameters & parses block with them (for admin)
	 *
	 * @param Array $params
	 * @return string
	 * @access protected
	 */
	protected function Image($params)
	{
		$image_url = $this->ImageSrc($params);

		if ( !$image_url ) {
			return '';
		}

		/** @var kDBItem $object */
		$object = $this->getObject($params);

		$params['img_path'] = $image_url;
		$image_dimensions = $this->ImageSize($params);
		$params['img_size'] = $image_dimensions ? $image_dimensions : ' width="' . $params['DefaultWidth'] . '"';
		$params['alt'] = $object->GetField('AltName'); // really used ?
		$params['name'] = $this->SelectParam($params, 'block,render_as');
		$params['align'] = array_key_exists('align', $params) ? $params['align'] : 'left';
		$params['no_editing'] = 1;

		if ( !$object->isLoaded() && !$this->SelectParam($params, 'default_image,DefaultImage') ) {
			return '';
		}

		// TODO: consider escaping in template instead
		$params['alt'] = kUtil::escape($params['alt']);

		$this->Application->Parser->DataExists = true;

		return $this->Application->ParseBlock($params);
	}

	/**
	 * Returns url for image in case when image source is url (for admin)
	 *
	 * @param Array $params
	 * @return string
	 */
	function ImageUrl($params)
	{
		$object = $this->getObject($params);
		if ($object->GetDBField('SameImages') ? $object->GetDBField('LocalThumb') : $object->GetDBField('LocalImage') ) {
			$ret = $this->Application->Phrase(getArrayValue($params,'local_phrase'));
		}
		else {
			$ret = $object->GetDBField('SameImages') ? $object->GetDBField('ThumbUrl') : $object->GetDBField('Url');
		}
		return $ret;
	}

	/**
	 * If data was modfied & is in TempTables mode, then parse block with name passed;
	 * remove modification mark if not in TempTables mode
	 *
	 * @param Array $params
	 * @return string
	 * @access public
	 * @author Alexey
	 */
	function SaveWarning($params)
	{
		if ($this->Prefix == 'c-img') {
			return $this->Application->ProcessParsedTag('c', 'SaveWarning', $params);
		}

		return parent::SaveWarning($params);
	}
}
