<?php
/**
 * Utility class for comparing objects, arrays, and values.
 *
 * @package Ealab\Deployer
 */

namespace EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility;

/**
 * Compares basic objects, arrays, or values.
 *
 * @deprecated Not used anymore, remove if not needed.
 */
class PropertiesComparer {
	/**
	 * Compares two values.
	 *
	 * @param mixed $a A value of any type.
	 * @param mixed $b A value of any type.
	 * @param bool  $check_same_order Whether to check the order of the values in arrays and object properties.
	 *
	 * @return bool
	 *   Returns true if the values are equal, false otherwise.
	 */
	public static function compareAny( $a, $b, bool $check_same_order = false ): bool {
		if ( $check_same_order ) {
			// The standard comparison operator already takes into account the order.
			return $a === $b;
		}

		if ( is_object( $a ) && is_object( $b ) ) {
			return static::compareObjects( $a, $b );
		} elseif ( is_array( $a ) && is_array( $b ) ) {
			return static::compareArrays( $a, $b );
		} else {
			return $a === $b;
		}
	}

	/**
	 * Compares two arrays.
	 *
	 * @param array $a An array.
	 * @param array $b Another array.
	 *
	 * @return bool
	 *   Returns true if the arrays are equal, false otherwise.
	 */
	public static function compareArrays( array $a, array $b ): bool {
		if ( count( $a ) !== count( $b ) ) {
			return false;
		}

		foreach ( $a as $a_key => $a_value ) {
			if ( ! isset( $b[ $a_key ] ) ) {
				return false;
			}

			if ( ! static::compareAny( $a_value, $b[ $a_key ] ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Compares two objects.
	 *
	 * @param object $a An object.
	 * @param object $b Another object.
	 *
	 * @return bool
	 *   Returns true if the objects are equal, false otherwise.
	 */
	public static function compareObjects( object $a, object $b ): bool {
		if ( get_class( $a ) !== get_class( $b ) ) {
			return false;
		}

		// We compare only the public properties.
		$a_properties = get_object_vars( $a );
		$b_properties = get_object_vars( $b );

		if ( count( $a_properties ) !== count( $b_properties ) ) {
			return false;
		}

		foreach ( $a_properties as $a_property_key => $a_property_value ) {
			if ( ! isset( $b_properties[ $a_property_key ] ) ) {
				return false;
			}

			if ( ! static::compareAny( $a_property_value, $b_properties[ $a_property_key ] ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Checks if a value contains another value.
	 *
	 * @param mixed $a A value of any type.
	 * @param mixed $b A value of any type.
	 *
	 * @return bool
	 *   Returns true if the first value contains the second one, false otherwise.
	 *   For arrays and objects, the second value must be a subset of the first one.
	 *   For scalar values, the first value must be equal to the second one.
	 */
	public static function containsAny( $a, $b ): bool {
		if ( is_object( $a ) && is_object( $b ) ) {
			return static::containsObject( $a, $b );
		} elseif ( is_array( $a ) && is_array( $b ) ) {
			return static::containsAssociativeArray( $a, $b );
		} else {
			return $a === $b;
		}
	}

	/**
	 * Checks if an associative array contains another associative array.
	 *
	 * @param array $a An associative array.
	 * @param array $b Another associative array.
	 *
	 * @return bool
	 *   Returns true if the first array contains the second one, false otherwise.
	 */
	public static function containsAssociativeArray( array $a, array $b ): bool {
		if ( count( $a ) < count( $b ) ) {
			return false;
		}

		foreach ( $b as $b_key => $b_value ) {
			if ( ! isset( $a[ $b_key ] ) ) {
				return false;
			}

			if ( ! static::containsAny( $a[ $b_key ], $b_value ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Checks if an object contains another object.
	 *
	 * The objects must be of the same class.
	 * The properties of the second object must be a subset of the properties of the first one.
	 *
	 * @param object $a An object.
	 * @param object $b Another object.
	 *
	 * @return bool
	 *   Returns true if the first object contains the second one, false otherwise.
	 */
	public static function containsObject( object $a, object $b ): bool {
		if ( get_class( $a ) !== get_class( $b ) ) {
			return false;
		}

		// We compare only the public properties.
		$a_properties = get_object_vars( $a );
		$b_properties = get_object_vars( $b );

		if ( count( $a_properties ) < count( $b_properties ) ) {
			return false;
		}

		foreach ( $b_properties as $b_property_key => $b_property_value ) {
			if ( ! isset( $a_properties[ $b_property_key ] ) ) {
				return false;
			}

			if ( ! static::containsAny( $a_properties[ $b_property_key ], $b_property_value ) ) {
				return false;
			}
		}

		return true;
	}
}
