<?php

use WPML\FP\Logic;
use WPML\FP\Obj;
use WPML\FP\Str;
use function WCML\functions\getSitePress;
use function WPML\FP\pipe;

class WCML_Multi_Currency_Prices {

	const WC_DEFAULT_STEP = 10;

	/**
	 * @var array
	 */
	private $currency_options;
	/**
	 * @var WCML_Multi_Currency
	 */
	private $multi_currency;
	/**
	 * @var orders_list_currency
	 */
	private $orders_list_currency;

	public function __construct( WCML_Multi_Currency $multi_currency, array $currency_options ) {
		$this->multi_currency   = $multi_currency;
		$this->currency_options = $currency_options;
	}

	public function add_hooks() {
		add_filter( 'wcml_raw_price_amount', [ $this, 'raw_price_filter' ], 10, 2 );

		add_filter( 'woocommerce_currency', [ $this, 'currency_filter' ] );
		add_filter( 'wcml_price_currency', [ $this, 'price_currency_filter' ] );
		add_filter( 'get_post_metadata', [ $this, 'product_price_filter' ], 10, 4 );
		add_filter( 'get_post_metadata', [ $this, 'variation_prices_filter' ], 12, 4 );
		add_filter( 'wcml_formatted_price', [ $this, 'formatted_price' ], 10, 2 );

		if ( $this->multi_currency->load_filters ) {
			add_filter(
				'wcml_product_price_by_currency',
				[
					$this,
					'get_product_price_in_currency',
				],
				10,
				2
			);  // WCML filters.
			add_filter( 'woocommerce_price_filter_widget_max_amount', [ $this, 'filter_widget_max_amount' ], 99 );
			add_filter( 'woocommerce_price_filter_widget_min_amount', [ $this, 'filter_widget_min_amount' ], 99 );

			add_filter( 'woocommerce_adjust_price', [ $this, 'raw_price_filter' ], 10 );

			// Shipping prices.
			add_filter( 'woocommerce_paypal_args', [ $this, 'filter_price_woocommerce_paypal_args' ] );
			add_filter(
				'woocommerce_get_variation_prices_hash',
				[
					$this,
					'add_currency_to_variation_prices_hash',
				]
			);
			add_filter(
				'woocommerce_cart_contents_total',
				[
					$this,
					'filter_woocommerce_cart_contents_total',
				],
				100
			);
			add_filter( 'woocommerce_cart_subtotal', [ $this, 'filter_woocommerce_cart_subtotal' ], 100, 3 );

			add_filter( 'posts_clauses', [ $this, 'price_filter_post_clauses' ], 100, 2 );

			add_action(
				'woocommerce_cart_loaded_from_session',
				[
					$this,
					'filter_currency_num_decimals_in_cart',
				]
			);

			add_filter( 'wc_price_args', [ $this, 'filter_wc_price_args' ] );
		}

		add_action( 'woocommerce_cart_loaded_from_session', [ $this, 'recalculate_totals' ], PHP_INT_MAX );

		// formatting options.
		add_filter( 'option_woocommerce_price_thousand_sep', [ $this, 'filter_currency_thousand_sep_option' ] );
		add_filter( 'option_woocommerce_price_decimal_sep', [ $this, 'filter_currency_decimal_sep_option' ] );
		add_filter( 'option_woocommerce_price_num_decimals', [ $this, 'filter_currency_num_decimals_option' ] );
		add_filter( 'option_woocommerce_currency_pos', [ $this, 'filter_currency_position_option' ] );

		// need for display correct price format for order on orders list page.
		add_filter( 'get_post_metadata', [ $this, 'save_order_currency_for_filter' ], 10, 4 );
	}

	public function currency_filter( $currency ) {

		if ( $this->is_multi_currency_filters_loaded() ) {
			$currency = apply_filters( 'wcml_price_currency', $currency );
		}

		return $currency;
	}

	public function price_currency_filter( $currency ) {

		if ( $this->is_multi_currency_filters_loaded() ) {
			if ( isset( $this->order_currency ) ) {
				$currency = $this->order_currency;
			} else {
				$currency = $this->multi_currency->get_client_currency();
			}
		}

		return $currency;
	}

	/**
	 * @param float $min_amount
	 *
	 * @return float
	 */
	public function filter_widget_min_amount( $min_amount ) {
		$step = $this->get_filter_widget_amount_step();

		return floor( $this->raw_price_filter( $min_amount ) / $step ) * $step;
	}

	/**
	 * @param float $max_price
	 *
	 * @return float
	 */
	public function filter_widget_max_amount( $max_price ) {
		$step = $this->get_filter_widget_amount_step();
		return ceil( $this->raw_price_filter( $max_price ) / $step ) * $step;
	}

	/**
	 * @return int
	 */
	private function get_filter_widget_amount_step() {
		return max( apply_filters( 'woocommerce_price_filter_widget_step', self::WC_DEFAULT_STEP ), 1 );
	}

	public function raw_price_filter( $price, $currency = false ) {

		if ( $currency === false ) {
			$currency = $this->multi_currency->get_client_currency();
		}

		if ( $currency !== wcml_get_woocommerce_currency_option() ) {
			$price = $this->convert_price_amount( $price, $currency );
			$price = $this->apply_rounding_rules( $price, $currency );
		}

		return $price;

	}

	public function get_product_price_in_currency( $product_id, $currency = false ) {

		if ( ! $currency ) {
			$currency = $this->multi_currency->get_client_currency();
		}

		remove_filter(
			'get_post_metadata',
			[
				$this,
				'product_price_filter',
			],
			10
		);

		$manual_prices = $this->multi_currency->custom_prices->get_product_custom_prices( $product_id, $currency );

		if ( $manual_prices && isset( $manual_prices['_price'] ) ) {

			$price = $manual_prices['_price'];

		} else {

			$product = wc_get_product( $product_id );
			$price   = $this->raw_price_filter( wc_get_price_including_tax( $product ), $currency );

		}

		add_filter(
			'get_post_metadata',
			[
				$this,
				'product_price_filter',
			],
			10,
			4
		);

		return $price;

	}

	/**
	 * @param mixed|null $null
	 * @param int        $object_id
	 * @param string     $meta_key
	 * @param bool       $single
	 *
	 * @return mixed
	 */
	public function product_price_filter( $null, $object_id, $meta_key, $single ) {
		static $unlocked = true;

		if (
			$unlocked
			&& in_array( get_post_type( $object_id ), [ 'product', 'product_variation' ] )
			&& in_array( $meta_key, wcml_price_custom_fields( $object_id ) )
			&& $this->is_multi_currency_filters_loaded()
		) {
			$unlocked = false;
			$currency  = $this->multi_currency->get_client_currency();

			// $get_price_by_legacy_ccr :: void -> float|void
			$get_price_by_legacy_ccr = function() use ( $object_id, $meta_key, $single, $currency ) {
				// exception for products migrated from before WCML 3.1 with independent prices.
				// legacy prior 3.1.
				$original_object_id = apply_filters( 'translate_object_id', $object_id, get_post_type( $object_id ), false, getSitePress()->get_default_language() );
				$ccr_rate           = Obj::path( [ $meta_key, $currency ], (array) get_post_meta( $original_object_id, '_custom_conversion_rate', true ) );

				if (
					$ccr_rate
					&& in_array( $meta_key, [ '_price', '_regular_price', '_sale_price'] )
				) {
					$price_original = get_post_meta( $original_object_id, $meta_key, $single );
					if ( is_numeric( $price_original ) ) {
						return $price_original * $ccr_rate;
					}
				}
			};

			// $get_manual_price :: void -> float|void
			$get_manual_price = function() use ( $object_id, $meta_key, $currency ) {
				return Obj::prop( $meta_key, (array) $this->multi_currency->custom_prices->get_product_custom_prices( $object_id, $currency ) );
			};

			// $get_price_by_auto_conversion :: void -> float|void
			$get_price_by_auto_conversion = function() use ( $object_id, $meta_key, $single ) {
				$price_original = get_post_meta( $object_id, $meta_key, $single );
				if( is_numeric( $price_original ) ){
					return apply_filters( 'wcml_raw_price_amount', $price_original );
				}
			};

			$get_price = Logic::firstSatisfying(
				Logic::isNotNull(),
				[
					$get_price_by_legacy_ccr,
					$get_manual_price,
					$get_price_by_auto_conversion,
				]
			);

			$price = $get_price( null );

			$unlocked = true;
		}

		return isset( $price ) ? $price : $null;
	}

	public function variation_prices_filter( $null, $object_id, $meta_key, $single ) {

		if ( empty( $meta_key ) && get_post_type( $object_id ) === 'product_variation' ) {
			static $no_filter = false;

			if ( empty( $no_filter ) && $this->is_multi_currency_filters_loaded() ) {
				$no_filter = true;

				$variation_fields = get_post_meta( $object_id );

				$manual_prices = $this->multi_currency->custom_prices->get_product_custom_prices( $object_id, $this->multi_currency->get_client_currency() );

				foreach ( $variation_fields as $k => $v ) {

					if ( in_array( $k, [ '_price', '_regular_price', '_sale_price' ] ) ) {

						foreach ( $v as $j => $amount ) {

							if ( isset( $manual_prices[ $k ] ) ) {
								$variation_fields[ $k ][ $j ] = $manual_prices[ $k ];     // manual price.

							} elseif ( $amount ) {
								$variation_fields[ $k ][ $j ] = apply_filters( 'wcml_raw_price_amount', $amount );   // automatic conversion.
							}
						}
					}
				}

				$no_filter = false;
			}
		}

		return isset( $variation_fields ) ? $variation_fields : $null;

	}

	/**
	 * @param float|int   $amount
	 * @param bool|string $currency
	 *
	 * @return float|int
	 */
	public function convert_price_amount( $amount, $currency = false ) {

		if ( empty( $currency ) ) {
			$currency = $this->multi_currency->get_client_currency();
		}

		if ( $currency !== wcml_get_woocommerce_currency_option() ) {

			$amount = $this->calculate_exchange_rate_price( $amount, $currency, '*' );

		}

		return $amount;

	}

	/**
	 * @param float|int $amount
	 * @param string    $from_currency
	 * @param string    $to_currency
	 *
	 * @return float|int
	 */
	public function convert_price_amount_by_currencies( $amount, $from_currency, $to_currency ) {

		if ( $to_currency !== wcml_get_woocommerce_currency_option() ) {
			$amount = $this->calculate_exchange_rate_price( $amount, $to_currency, '*' );
		} else {
			$amount = $this->calculate_exchange_rate_price( $amount, $from_currency, '/' );
		}

		return $amount;
	}

	/**
	 * @param float|int $amount
	 * @param string    $currency
	 * @param string    $operator
	 *
	 * @return float|int
	 */
	private function calculate_exchange_rate_price( $amount, $currency, $operator ) {

		$exchange_rates = $this->multi_currency->get_exchange_rates();

		if ( isset( $exchange_rates[ $currency ] ) && is_numeric( $amount ) ) {

			if ( '*' === $operator ) {
				$amount = $amount * $exchange_rates[ $currency ];
			} elseif ( '/' === $operator ) {
				$amount = $amount / $exchange_rates[ $currency ];
			}

			// exception - currencies_without_cents.
			if ( in_array( $currency, $this->multi_currency->get_currencies_without_cents() ) ) {
				$amount = $this->round_up( $amount );
			}
		} else {
			$amount = 0;
		}

		return $amount;

	}

	// convert back to default currency.
	public function unconvert_price_amount( $amount, $currency = false ) {

		if ( empty( $currency ) ) {
			$currency = $this->multi_currency->get_client_currency();
		}

		if ( $currency !== wcml_get_woocommerce_currency_option() ) {

			$exchange_rates = $this->multi_currency->get_exchange_rates();

			if ( isset( $exchange_rates[ $currency ] ) && is_numeric( $amount ) ) {
				$amount = $amount / $exchange_rates[ $currency ];

				// exception - currencies_without_cents.
				if ( in_array( $currency, $this->multi_currency->get_currencies_without_cents() ) ) {
					$amount = $this->round_up( $amount );
				}
			} else {
				$amount = 0;
			}
		}

		return $amount;

	}

	public function apply_rounding_rules( $price, $currency = false ) {

		if ( is_null( $this->currency_options ) ) {
			global $woocommerce_wpml;
			$this->currency_options = $woocommerce_wpml->get_setting( 'currency_options' );
		}

		if ( ! $currency ) {
			$currency = $this->multi_currency->get_client_currency();
		}

		$currency_options = $this->currency_options[ $currency ];

		if ( $currency_options['rounding'] !== 'disabled' ) {

			if ( $currency_options['rounding_increment'] > 1 ) {
				$price = $price / $currency_options['rounding_increment'];
			}

			switch ( $currency_options['rounding'] ) {
				case 'up':
					$rounded_price = ceil( $price );
					break;
				case 'down':
					$rounded_price = floor( $price );
					break;
				case 'nearest':
				default:
					$rounded_price = $this->round_up( $price );
					break;
			}

			if ( $rounded_price > 0 ) {
				$price = $rounded_price;
			}

			if ( $currency_options['rounding_increment'] > 1 ) {
				$price = $price * $currency_options['rounding_increment'];
			}

			if ( $currency_options['auto_subtract'] && $currency_options['auto_subtract'] < $price ) {
				$price = $price - $currency_options['auto_subtract'];
			}
		} else {

			// Use configured number of decimals.
			$price = floor( $price * pow( 10, $currency_options['num_decimals'] ) + 0.0001 ) / pow( 10, $currency_options['num_decimals'] );

		}

		return apply_filters( 'wcml_rounded_price', $price, $currency );

	}

	/**
	 * The PHP 5.2 compatible equivalent to "round($amount, 0, PHP_ROUND_HALF_UP)"
	 *
	 * @param int $amount
	 *
	 * @return int
	 */
	private function round_up( $amount ) {
		if ( $amount - floor( $amount ) < 0.5 ) {
			$amount = floor( $amount );
		} else {
			$amount = ceil( $amount );
		}

		return $amount;
	}

	/*
	* Converts the price from the default currency to the given currency and applies the format
	*/
	public function formatted_price( $amount, $currency = false ) {

		if ( $currency === false ) {
			$currency = $this->multi_currency->get_client_currency();
		}

		$amount = $this->raw_price_filter( $amount, $currency );

		return $this->format_price_in_currency( $amount, $currency );
	}

	public function format_price_in_currency( $price, $currency ) {

		$currency_details = $this->multi_currency->get_currency_details_by_code( $currency );

		switch ( $currency_details['position'] ) {
			case 'left':
				$format = '%1$s%2$s';
				break;
			case 'right':
				$format = '%2$s%1$s';
				break;
			case 'left_space':
				$format = '%1$s&nbsp;%2$s';
				break;
			case 'right_space':
				$format = '%2$s&nbsp;%1$s';
				break;
			default:
				$format = get_woocommerce_price_format();
		}

		$wc_price_args = [
			'currency'           => $currency,
			'decimal_separator'  => $currency_details['decimal_sep'],
			'thousand_separator' => $currency_details['thousand_sep'],
			'decimals'           => $currency_details['num_decimals'],
			'price_format'       => $format,
		];

		return wc_price( $price, $wc_price_args );
	}

	// Exposed function.
	public function apply_currency_position( $price, $currency_code ) {

		$currencies = $this->multi_currency->get_currencies();

		if ( isset( $currencies[ $currency_code ]['position'] ) ) {
			$position = $currencies[ $currency_code ]['position'];
		} else {
			remove_filter(
				'option_woocommerce_currency_pos',
				[
					$this,
					'filter_currency_position_option',
				]
			);
			$position = get_option( 'woocommerce_currency_pos' );
			add_filter(
				'option_woocommerce_currency_pos',
				[
					$this,
					'filter_currency_position_option',
				]
			);
		}

		switch ( $position ) {
			case 'left':
				$price = sprintf( '%s%s', get_woocommerce_currency_symbol( $currency_code ), $price );
				break;
			case 'right':
				$price = sprintf( '%s%s', $price, get_woocommerce_currency_symbol( $currency_code ) );
				break;
			case 'left_space':
				$price = sprintf( '%s %s', get_woocommerce_currency_symbol( $currency_code ), $price );
				break;
			case 'right_space':
				$price = sprintf( '%s %s', $price, get_woocommerce_currency_symbol( $currency_code ) );
				break;
		}

		return $price;
	}

	public function filter_price_woocommerce_paypal_args( $args ) {

		foreach ( $args as $key => $value ) {
			if ( substr( $key, 0, 7 ) == 'amount_' ) {

				$currency_details = $this->multi_currency->get_currency_details_by_code( $args['currency_code'] );

				$args[ $key ] = number_format( $value, $currency_details['num_decimals'], '.', '' );
			}
		}

		return $args;
	}

	public function add_currency_to_variation_prices_hash( $data ) {

		$data['currency']            = $this->multi_currency->get_client_currency();
		$data['exchange_rates_hash'] = md5( json_encode( $this->multi_currency->get_exchange_rates() ) );

		return $data;

	}

	public function filter_woocommerce_cart_contents_total( $cart_contents_total ) {
		remove_filter(
			'woocommerce_cart_contents_total',
			[
				$this,
				'filter_woocommerce_cart_contents_total',
			],
			100
		);
		$this->recalculate_totals();
		$cart_contents_total = WC()->cart->get_cart_total();
		add_filter( 'woocommerce_cart_contents_total', [ $this, 'filter_woocommerce_cart_contents_total' ], 100 );

		return $cart_contents_total;
	}

	public function recalculate_totals() {
		WC()->cart->calculate_totals();
	}

	public function filter_woocommerce_cart_subtotal( $cart_subtotal, $compound, $cart_object ) {

		remove_filter( 'woocommerce_cart_subtotal', [ $this, 'filter_woocommerce_cart_subtotal' ], 100 );

		$cart_subtotal = $cart_object->get_cart_subtotal( $compound );

		add_filter( 'woocommerce_cart_subtotal', [ $this, 'filter_woocommerce_cart_subtotal' ], 100, 3 );

		return $cart_subtotal;
	}

	public function price_filter_post_clauses( $args, $wp_query ) {

		if ( ! $wp_query->is_main_query() || ( ! isset( $_GET['max_price'] ) && ! isset( $_GET['min_price'] ) ) ) {
			return $args;
		}

		$currency = $this->multi_currency->get_client_currency();

		if ( $currency !== wcml_get_woocommerce_currency_option() ) {
			global $wpdb;
			$min_price = isset( $_GET['min_price'] ) ? floatval( wp_unslash( $_GET['min_price'] ) ) : 0;
			$max_price = isset( $_GET['max_price'] ) ? floatval( wp_unslash( $_GET['max_price'] ) ) : PHP_INT_MAX;

			$min_price_in_default_currency = $this->unconvert_price_amount( $min_price, $currency );
			$max_price_in_default_currency = $this->unconvert_price_amount( $max_price, $currency );

			$replaceSinceWc5_1 = Str::replace(
				[ $wpdb->prepare( '%f<wc_product_meta_lookup.min_price', $max_price ), $wpdb->prepare( '%f>wc_product_meta_lookup.max_price', $min_price ) ],
				[ $wpdb->prepare( '%f<wc_product_meta_lookup.min_price', $max_price_in_default_currency ), $wpdb->prepare( '%f>wc_product_meta_lookup.max_price', $min_price_in_default_currency ) ]
			);

			$replaceBeforeWc5_1 = Str::replace(
				[ $wpdb->prepare( 'wc_product_meta_lookup.min_price >= %f', $min_price ), $wpdb->prepare( 'wc_product_meta_lookup.max_price <= %f', $max_price ) ],
				[ $wpdb->prepare( 'wc_product_meta_lookup.min_price >= %f', $min_price_in_default_currency ), $wpdb->prepare( 'wc_product_meta_lookup.max_price <= %f', $max_price_in_default_currency ) ]
			);

			return Obj::over(
				Obj::lensProp( 'where' ),
				pipe( $replaceSinceWc5_1, $replaceBeforeWc5_1 ),
				$args
			);
		}

		return $args;
	}

	/**
	 * @param array  $response
	 * @param string $to_currency
	 * @param string $from_currency
	 * @param array  $params
	 *
	 * @return array
	 */
	public function filter_pre_selected_widget_prices_in_new_currency( $response, $to_currency, $from_currency, $params ) {

		wpml_collect( $params )->each(
			function ( $value, $key ) use ( &$response, $from_currency, $to_currency ) {
				if ( wpml_collect( [ 'min_price', 'max_price' ] )->contains( $key ) ) {
					$response[ $key ] = $this->convert_price_amount( $this->unconvert_price_amount( $value, $from_currency ), $to_currency );
				}
			}
		);

		return $response;
	}


	private function check_admin_order_currency_code() {
		global $pagenow;

		$actions              = [
			'woocommerce_add_order_item',
			'woocommerce_remove_order_item',
			'woocommerce_save_order_items',
			'woocommerce_calc_line_taxes',
		];
		$is_ajax_order_action =
			wp_doing_ajax() &&
			(
			(
				isset( $_POST['action'] ) &&
				in_array( $_POST['action'], $actions ) ||
				(
					isset( $_GET['action'] ) &&
					$_GET['action'] == 'woocommerce_json_search_products_and_variations'
				)
			)
			);

		$is_shop_order_new = $pagenow == 'post-new.php' && isset( $_GET['post_type'] ) && $_GET['post_type'] == 'shop_order';

		if ( ( $is_ajax_order_action || $is_shop_order_new ) && isset( $_COOKIE['_wcml_order_currency'] ) ) {
			$currency_code = $_COOKIE['_wcml_order_currency'];
		} elseif ( isset( $_GET['post'] ) && get_post_type( $_GET['post'] ) == 'shop_order' ) {
			$currency_code = get_post_meta( $_GET['post'], '_order_currency', true );
		} elseif ( isset( $_GET['post_type'] ) && $_GET['post_type'] == 'shop_order' && ! is_null( $this->orders_list_currency ) ) {
			$currency_code = $this->orders_list_currency;
		} elseif ( isset( $_GET['page'] ) && $_GET['page'] == 'wc-reports' && isset( $_COOKIE['_wcml_reports_currency'] ) ) {
			$currency_code = $_COOKIE['_wcml_reports_currency'];
		} elseif ( isset( $_COOKIE['_wcml_dashboard_currency'] ) && is_admin() && ! defined( 'DOING_AJAX' ) && $pagenow == 'index.php' ) {
			$currency_code = $_COOKIE['_wcml_dashboard_currency'];
		} else {
			$currency_code = $this->multi_currency->get_client_currency();
		}

		return apply_filters( 'wcml_filter_currency_position', $currency_code );

	}

	public function get_admin_order_currency_code() {

		return $this->check_admin_order_currency_code();

	}

	public function save_order_currency_for_filter( $null, $object_id, $meta_key, $single ) {

		if (
			$meta_key == '_order_currency' &&
			isset( $_GET['post_type'] ) &&
			$_GET['post_type'] == 'shop_order' &&
			! isset( $_GET['post'] ) &&
			get_post_type( $object_id ) == 'shop_order'
		) {
			remove_filter( 'get_post_metadata', [ $this, 'save_order_currency_for_filter' ], 10 );
			$this->orders_list_currency = get_post_meta( $object_id, $meta_key, true );
			add_filter( 'get_post_metadata', [ $this, 'save_order_currency_for_filter' ], 10, 4 );
		}

		return $null;
	}

	public function filter_currency_thousand_sep_option( $value ) {

		$default_currency = $this->multi_currency->get_default_currency();
		$currency_code    = $this->check_admin_order_currency_code();

		if ( $currency_code !== $default_currency && isset( $this->multi_currency->currencies[ $currency_code ]['thousand_sep'] ) ) {
			$value = $this->multi_currency->currencies[ $currency_code ]['thousand_sep'];
		}

		return $value;
	}

	public function filter_currency_decimal_sep_option( $value ) {

		$default_currency = $this->multi_currency->get_default_currency();
		$currency_code    = $this->check_admin_order_currency_code();

		if ( $currency_code !== $default_currency && isset( $this->multi_currency->currencies[ $currency_code ]['decimal_sep'] ) ) {
			$value = $this->multi_currency->currencies[ $currency_code ]['decimal_sep'];

		}

		return $value;
	}

	public function filter_currency_num_decimals_option( $value ) {
		// no other way available (at the moment) to filter currency_num_decimals_option.
		$default_currency = $this->multi_currency->get_default_currency();

		$db = debug_backtrace();
		if (
			isset( $db['8']['function'] ) && isset( $db['5']['function'] ) &&
			$db['8']['function'] == 'calculate_shipping_for_package' && $db['5']['function'] == 'add_rate'
			||
			isset( $db['7']['function'] ) && isset( $db['4']['function'] ) &&
			$db['7']['function'] == 'calculate_shipping_for_package' && $db['4']['function'] == 'add_rate'
		) {
			$currency_code = $default_currency;
		} else {
			$currency_code = $this->check_admin_order_currency_code();
		}

		if ( $currency_code !== $default_currency && isset( $this->multi_currency->currencies[ $currency_code ]['num_decimals'] ) ) {
			$value = $this->multi_currency->currencies[ $currency_code ]['num_decimals'];
		}

		return $value;
	}

	public function filter_currency_position_option( $value ) {

		$default_currency = $this->multi_currency->get_default_currency();
		$currency_code    = $this->get_admin_order_currency_code();

		if ( $currency_code !== $default_currency &&
			 isset( $this->multi_currency->currencies[ $currency_code ]['position'] ) && wcml_get_woocommerce_currency_option() !== $currency_code &&
			in_array(
				$this->multi_currency->currencies[ $currency_code ]['position'],
				[
					'left',
					'right',
					'left_space',
					'right_space',
				]
			)
		) {
			$value = $this->multi_currency->currencies[ $currency_code ]['position'];
		}

		return $value;
	}

	public function filter_currency_num_decimals_in_cart( $cart ) {
		$cart->dp = wc_get_price_decimals();
	}

	/*
	 * Limitation: If the default currency is configured to display more decimals than the other currencies,
	 * the prices in the secondary currencies would be approximated to the number of decimals that they have more.
	*/
	public function price_in_specific_currency( $return, $price, $args ) {

		if ( isset( $args['currency'] ) && $this->multi_currency->get_client_currency() != $args['currency'] ) {
			remove_filter( 'wc_price', [ $this, 'price_in_specific_currency' ], 10 );
			$this->multi_currency->set_client_currency( $args['currency'] );
			$return = wc_price( $price, $args );
			add_filter( 'wc_price', [ $this, 'price_in_specific_currency' ], 10, 3 );
		}

		return $return;

	}

	public function filter_wc_price_args( $args ) {

		if ( isset( $args['currency'] ) ) {

			if ( isset( $this->multi_currency->currencies[ $args['currency'] ]['decimal_sep'] ) ) {
				$args['decimal_separator'] = $this->multi_currency->currencies[ $args['currency'] ]['decimal_sep'];
			}

			if ( isset( $this->multi_currency->currencies[ $args['currency'] ]['thousand_sep'] ) ) {
				$args['thousand_separator'] = $this->multi_currency->currencies[ $args['currency'] ]['thousand_sep'];
			}

			if ( isset( $this->multi_currency->currencies[ $args['currency'] ]['num_decimals'] ) ) {
				$args['decimals'] = $this->multi_currency->currencies[ $args['currency'] ]['num_decimals'];
			}

			if ( isset( $this->multi_currency->currencies[ $args['currency'] ]['position'] ) ) {
				$current_currency = $this->multi_currency->get_client_currency();
				$this->multi_currency->set_client_currency( $args['currency'] );
				$args['price_format'] = get_woocommerce_price_format();
				$this->multi_currency->set_client_currency( $current_currency ); // restore.
			}
		}

		return $args;
	}

	/**
	 * @param float       $price
	 * @param null|string $currency
	 *
	 * @return float
	 */
	public function convert_raw_woocommerce_price( $price, $currency = null ) {
		if ( null === $currency ) {
			$currency = $this->multi_currency->get_client_currency();
		}

		return apply_filters( 'wcml_raw_price_amount', $price, $currency );
	}

	/**
	 * @param float      $value
	 * @param WC_Product $product
	 *
	 * @return float
	 */
	public function get_original_product_price( $value, $product ) {
		return get_post_meta( $product->get_id(), '_price', 1 );
	}

	private function is_multi_currency_filters_loaded() {
		static $filters_loaded;

		if ( ! $filters_loaded ) {
			$filters_loaded = $this->multi_currency->are_filters_need_loading();
		}

		return $filters_loaded;
	}

}
