<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
 * Tonesque
 * Grab an average color representation from an image.
 *
 * @author Automattic
 * @author Matias Ventura
 * @package automattic/jetpack
 */

if ( ! class_exists( 'Tonesque' ) ) {
	/**
	 * Color representation class.
	 */
	class Tonesque {
		/**
		 * Image URL.
		 *
		 * @var string
		 */
		private $image_url = '';
		/**
		 * Image identifier representing the image.
		 *
		 * @var null|object
		 */
		private $image_obj = null;
		/**
		 * Color code.
		 *
		 * @var string
		 */
		private $color = '';

		/**
		 * Constructor.
		 *
		 * @param string $image_url Image URL.
		 */
		public function __construct( $image_url ) {
			if ( ! class_exists( 'Jetpack_Color' ) ) {
				require_once JETPACK__PLUGIN_DIR . '/_inc/lib/class.color.php';
			}

			$this->image_url = esc_url_raw( $image_url );
			$this->image_url = trim( $this->image_url );
			/**
			 * Allows any image URL to be passed in for $this->image_url.
			 *
			 * @module theme-tools
			 *
			 * @since 2.5.0
			 *
			 * @param string $image_url The URL to any image
			 */
			$this->image_url = apply_filters( 'tonesque_image_url', $this->image_url );

			$this->image_obj = self::imagecreatefromurl( $this->image_url );
		}

		/**
		 * Get an image object from a URL.
		 *
		 * @param string $image_url Image URL.
		 *
		 * @return object|bool Image object or false if the image could not be loaded.
		 */
		public static function imagecreatefromurl( $image_url ) {
			$data = null;

			// If it's a URL.
			if ( preg_match( '#^https?://#i', $image_url ) ) {
				// If it's a url pointing to a local media library url.
				$content_url = content_url();
				$_image_url  = set_url_scheme( $image_url );
				if ( str_starts_with( $_image_url, $content_url ) ) {
					$_image_path = str_replace( $content_url, WP_CONTENT_DIR, $_image_url );
					if ( file_exists( $_image_path ) ) {
						$filetype = wp_check_filetype( $_image_path );
						$type     = $filetype['type'];

						if ( str_starts_with( $type, 'image/' ) ) {
							$data = file_get_contents( $_image_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
						}
					}
				}

				if ( empty( $data ) ) {
					$response      = wp_safe_remote_get( $image_url );
					$response_code = wp_remote_retrieve_response_code( $response );
					if (
						is_wp_error( $response )
						|| ! $response_code
						|| $response_code < 200
						|| $response_code >= 300
					) {
						return false;
					}
					$data = wp_remote_retrieve_body( $response );
				}
			}

			// If it's a local path in our WordPress install.
			if ( file_exists( $image_url ) ) {
				$filetype = wp_check_filetype( $image_url );
				$type     = $filetype['type'];

				if ( str_starts_with( $type, 'image/' ) ) {
					$data = file_get_contents( $image_url ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
				}
			}

			if ( null === $data ) {
				return false;
			}

			// Now turn it into an image and return it.
			return imagecreatefromstring( $data );
		}

		/**
		 * Construct object from image.
		 *
		 * @param string $type Type (hex, rgb, hsv) (optional).
		 *
		 * @return string|bool color as a string formatted as $type or false if the image could not be loaded.
		 */
		public function color( $type = 'hex' ) {
			// Bail if there is no image to work with.
			if ( ! $this->image_obj ) {
				return false;
			}

			// Finds dominant color.
			$color = self::grab_color();
			// Passes value to Color class.
			return self::get_color( $color, $type );
		}

		/**
		 * Grabs the color index for each of five sample points of the image
		 *
		 * @param string $type can be 'index' or 'hex'.
		 *
		 * @return array|false color indices or false if the image could not be loaded.
		 */
		public function grab_points( $type = 'index' ) {
			$img = $this->image_obj;
			if ( ! $img ) {
				return false;
			}

			$height = imagesy( $img );
			$width  = imagesx( $img );

			/*
			* Sample five points in the image
			* based on rule of thirds and center.
			*/
			$topy    = round( $height / 3 );
			$bottomy = round( ( $height / 3 ) * 2 );
			$leftx   = round( $width / 3 );
			$rightx  = round( ( $width / 3 ) * 2 );
			$centery = round( $height / 2 );
			$centerx = round( $width / 2 );

			// Cast those colors into an array.
			$points = array(
				imagecolorat( $img, $leftx, $topy ),
				imagecolorat( $img, $rightx, $topy ),
				imagecolorat( $img, $leftx, $bottomy ),
				imagecolorat( $img, $rightx, $bottomy ),
				imagecolorat( $img, $centerx, $centery ),
			);

			if ( 'hex' === $type ) {
				foreach ( $points as $i => $p ) {
					$c            = imagecolorsforindex( $img, $p );
					$points[ $i ] = self::get_color(
						array(
							'r' => $c['red'],
							'g' => $c['green'],
							'b' => $c['blue'],
						),
						'hex'
					);
				}
			}

			return $points;
		}

		/**
		 * Finds the average color of the image based on five sample points
		 *
		 * @return array|bool array with rgb color or false if the image could not be loaded.
		 */
		public function grab_color() {
			$img = $this->image_obj;
			if ( ! $img ) {
				return false;
			}

			$rgb = self::grab_points();

			$r = array();
			$g = array();
			$b = array();

			/*
			* Process the color points
			* Find the average representation
			*/
			foreach ( $rgb as $color ) {
				$index = imagecolorsforindex( $img, $color );
				$r[]   = $index['red'];
				$g[]   = $index['green'];
				$b[]   = $index['blue'];
			}
			$red   = round( array_sum( $r ) / 5 );
			$green = round( array_sum( $g ) / 5 );
			$blue  = round( array_sum( $b ) / 5 );

			// The average color of the image as rgb array.
			$color = array(
				'r' => $red,
				'g' => $green,
				'b' => $blue,
			);

			return $color;
		}

		/**
		 * Get a Color object using /lib class.color
		 * Convert to appropriate type
		 *
		 * @param string $color Color code.
		 * @param string $type  Color type (rgb, hex, hsv).
		 *
		 * @return string
		 */
		public function get_color( $color, $type ) {
			$c           = new Jetpack_Color( $color, 'rgb' );
			$this->color = $c;

			switch ( $type ) {
				case 'rgb':
					$color = implode( ',', $c->toRgbInt() );
					break;
				case 'hex':
					$color = $c->toHex();
					break;
				case 'hsv':
					$color = implode( ',', $c->toHsvInt() );
					break;
				default:
					return $c->toHex();
			}

			return $color;
		}

		/**
		 *
		 * Checks contrast against main color
		 * Gives either black or white for using with opacity
		 *
		 * @return string|bool Returns black or white or false if the image could not be loaded.
		 */
		public function contrast() {
			if ( ! $this->color ) {
				return false;
			}

			$c = $this->color->getMaxContrastColor();
			return implode( ',', $c->toRgbInt() );
		}
	}
}
