<?php

namespace Timber\Image\Operation;

use Timber\Helper;
use Timber\ImageHelper;
use Timber\Image\Operation as ImageOperation;

/**
 * Changes image to new size, by shrinking/enlarging
 * then cropping to respect new ratio.
 *
 * Arguments:
 * - width of new image
 * - height of new image
 * - crop method
 */
class Resize extends ImageOperation {

   private $w, $h, $crop;

	/**
	 * @param int    $w    width of new image
	 * @param int    $h    height of new image
	 * @param string $crop cropping method, one of: 'default', 'center', 'top', 'bottom', 'left', 'right', 'top-center', 'bottom-center'.
	 */
	public function __construct( $w, $h, $crop ) {
		$this->w = $w;
		$this->h = $h;
		// Sanitize crop position
		$allowed_crop_positions = array('default', 'center', 'top', 'bottom', 'left', 'right', 'top-center', 'bottom-center');
		if ( $crop !== false && !in_array($crop, $allowed_crop_positions) ) {
			$crop = $allowed_crop_positions[0];
		}
		$this->crop = $crop;
	}

	/**
	 * @param   string    $src_filename     the basename of the file (ex: my-awesome-pic)
	 * @param   string    $src_extension    the extension (ex: .jpg)
	 * @return  string    the final filename to be used (ex: my-awesome-pic-300x200-c-default.jpg)
	 */
	public function filename( $src_filename, $src_extension ) {
		$w = 0;
		$h = 0;
		if ( $this->w ) {
			$w = $this->w;
		}
		if ( $this->h ) {
			$h = $this->h;
		}
		$result = $src_filename.'-'.$w.'x'.$h.'-c-'.($this->crop ? $this->crop : 'f'); // Crop will be either user named or f (false)
		if ( $src_extension ) {
			$result .= '.' . $src_extension;
		}
		return $result;
	}

	/**
	 * Run a resize as animated GIF (if the server supports it)
	 *
	 * @param string           $load_filename the name of the file to resize.
	 * @param string           $save_filename the desired name of the file to save.
	 * @param \WP_Image_Editor $editor the image editor we're using.
	 * @return bool
	 */
	protected function run_animated_gif( $load_filename, $save_filename, \WP_Image_Editor $editor ) {
		$w = $this->w;
		$h = $this->h;
		if ( ! class_exists('Imagick') || ( defined('TEST_NO_IMAGICK') && TEST_NO_IMAGICK ) ) {
			Helper::warn('Cannot resize GIF, Imagick is not installed');
			return false;
		}
		$image = new \Imagick($load_filename);
		$image = $image->coalesceImages();
		$crop  = self::get_target_sizes($editor);
		foreach ( $image as $frame ) {
			$frame->cropImage(
				round($crop['src_w']),
				round($crop['src_h']),
				round($crop['x']),
				round($crop['y'])
			);
			$frame->thumbnailImage($w, $h);
			$frame->setImagePage($w, $h, 0, 0);
		}
		$image = $image->deconstructImages();
		return $image->writeImages($save_filename, true);
	}

	/**
	 * @param \WP_Image_Editor $image
	 */
	protected function get_target_sizes( \WP_Image_Editor $image ) {
		$w = $this->w;
		$h = $this->h;
		$crop = $this->crop;

		$current_size = $image->get_size();
		$src_w = $current_size['width'];
		$src_h = $current_size['height'];
		$src_ratio = $src_w / $src_h;
		if ( !$h ) {
			$h = round($w / $src_ratio);
		}
		if ( !$w ) {
			//the user wants to resize based on constant height
			$w = round($h * $src_ratio);
		}
		if ( !$crop ) {
			return array(
				'x' => 0, 'y' => 0,
				'src_w' => $src_w, 'src_h' => $src_h,
				'target_w' => $w, 'target_h' => $h
			);
		}
		// Get ratios
		$dest_ratio = $w / $h;
		$src_wt = $src_h * $dest_ratio;
		$src_ht = $src_w / $dest_ratio;
		$src_x = $src_w / 2 - $src_wt / 2;
		$src_y = ($src_h - $src_ht) / 6;
		//now specific overrides based on options:
		switch ( $crop ) {
			case 'center':
				// Get source x and y
				$src_x = round(($src_w - $src_wt) / 2);
				$src_y = round(($src_h - $src_ht) / 2);
				break;

			case 'top':
				$src_y = 0;
				break;

			case 'bottom':
				$src_y = $src_h - $src_ht;
				break;

			case 'top-center':
				$src_y = round(($src_h - $src_ht) / 4);
				break;

			case 'bottom-center':
				$src_y = $src_h - $src_ht - round(($src_h - $src_ht) / 4);
				break;

			case 'left':
				$src_x = 0;
				break;

			case 'right':
				$src_x = $src_w - $src_wt;
				break;
		}
		// Crop the image
		return ($dest_ratio > $src_ratio)
			? array(
				'x' => 0, 'y' => $src_y,
				'src_w' => $src_w, 'src_h' => $src_ht,
				'target_w' => $w, 'target_h' => $h
			)
			: array(
				'x' => $src_x, 'y' => 0,
				'src_w' => $src_wt, 'src_h' => $src_h,
				'target_w' => $w, 'target_h' => $h
			);
	}

	/**
	 * Performs the actual image manipulation,
	 * including saving the target file.
	 *
	 * @param  string $load_filename filepath (not URL) to source file
	 *                               (ex: /src/var/www/wp-content/uploads/my-pic.jpg)
	 * @param  string $save_filename filepath (not URL) where result file should be saved
	 *                               (ex: /src/var/www/wp-content/uploads/my-pic-300x200-c-default.jpg)
	 * @return boolean|null                  true if everything went fine, false otherwise
	 */
	public function run( $load_filename, $save_filename ) {
		// Attempt to check if SVG.
		if ( ImageHelper::is_svg($load_filename) ) {
			return false;
		}
		$image = wp_get_image_editor($load_filename);
		if ( !is_wp_error($image) ) {
			//should be resized by gif resizer
			if ( ImageHelper::is_animated_gif($load_filename) ) {
				//attempt to resize, return if successful proceed if not
				$gif = self::run_animated_gif($load_filename, $save_filename, $image);
				if ( $gif ) {
					return true;
				}
			}

			$crop = self::get_target_sizes($image);
			$image->crop(
				round( $crop['x'] ),
				round( $crop['y'] ),
				round( $crop['src_w'] ),
				round( $crop['src_h'] ),
				round( $crop['target_w'] ),
				round( $crop['target_h'] )
			);
			$quality = apply_filters( 'wp_editor_set_quality', 82, 'image/jpeg');
			$image->set_quality($quality);
			$result = $image->save($save_filename);
			if ( is_wp_error($result) ) {
				// @codeCoverageIgnoreStart
				Helper::error_log('Error resizing image');
				Helper::error_log($result);
				return false;
				// @codeCoverageIgnoreEnd
			} else {
				return true;
			}
		} else if ( isset($image->error_data['error_loading_image']) ) {
			// @codeCoverageIgnoreStart
			Helper::error_log('Error loading '.$image->error_data['error_loading_image']);
		} else {
			if ( !extension_loaded('gd') ) {
				Helper::error_log('Can not resize image, please install php-gd');
			} else {
				Helper::error_log($image);
			}
			// @codeCoverageIgnoreEnd
		}
	}
}
