<?php
/**
 * Swatches frontend  class.
 *
 * @package Flatsome\Extensions
 */

namespace Flatsome\Extensions;

defined( 'ABSPATH' ) || exit;

/**
 * Class Swatches_Frontend
 *
 * @package Flatsome\Extensions
 */
class Swatches_Frontend {

	/**
	 * The single instance of the class
	 *
	 * @var Swatches_Frontend
	 */
	protected static $instance = null;

	/**
	 * Main instance
	 *
	 * @deprecated in favor of get_instance()
	 * @return Swatches_Frontend
	 */
	public static function instance() {
		_deprecated_function( __METHOD__, '3.19.0', 'get_instance()' );
		return self::get_instance();
	}

	/**
	 * Main instance
	 *
	 * @return Swatches_Frontend
	 */
	public static function get_instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Swatches_Frontend constructor.
	 */
	private function __construct() {
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );

		add_action( 'wp_head', array( $this, 'add_css' ), 110 );

		add_filter( 'woocommerce_dropdown_variation_attribute_options_html', array( $this, 'get_swatch_html' ), 100, 2 );
		add_filter( 'flatsome_swatch_html', array( $this, 'swatch_html' ), 5, 4 );

		// Add swatches in loop.
		add_action( 'woocommerce_before_shop_loop_item_title', array( $this, 'box_swatch_list' ) );

		// Layered nav.
		add_filter( 'woocommerce_layered_nav_term_html', array( $this, 'layered_nav_term_html' ), 10, 4 );

		// Handle cache.
		add_action( 'save_post', array( $this, 'cache_clear_save_post' ) );
		add_action( 'woocommerce_before_product_object_save', array( $this, 'cache_clear_product_object_save' ) );
		add_action( 'woocommerce_order_status_cancelled', array( swatches(), 'cache_clear' ) );
		add_filter( 'pre_set_theme_mod_swatches', array( swatches(), 'cache_clear_on_option' ), 10, 2 );
		add_filter( 'pre_set_theme_mod_swatches_box_attribute', array( swatches(), 'cache_clear_on_option' ), 10, 2 );
		add_filter( 'pre_update_option_woocommerce_thumbnail_image_width', array( swatches(), 'cache_clear_on_option' ), 10, 2 );
		add_filter( 'pre_update_option_woocommerce_thumbnail_cropping', array( swatches(), 'cache_clear_on_option' ), 10, 2 );

		add_filter( 'woocommerce_variation_is_active', array( $this, 'variation_is_active' ), 10, 2 );
	}

	/**
	 * Enqueue scripts and stylesheets
	 */
	public function enqueue_scripts() {
		wp_enqueue_style( 'flatsome-swatches-frontend', get_template_directory_uri() . '/assets/css/extensions/flatsome-swatches-frontend.css', array(), swatches()->version );
		wp_style_add_data( 'flatsome-swatches-frontend', 'rtl', 'replace' );

		wp_enqueue_script( 'flatsome-swatches-frontend', get_template_directory_uri() . '/assets/js/extensions/flatsome-swatches-frontend.js', array(
			'jquery',
			'flatsome-js',
		), swatches()->version, true );
	}

	/**
	 * Add extension CSS.
	 */
	public function add_css() {
		ob_start();
		?>
		<?php if ( get_theme_mod( 'swatches_layout' ) === 'stacked' ) : ?>
			.variations th,
			.variations td {
			display: block;
			}

			.variations .label {
			display: flex;
			align-items: center;
			}

			.variations .label label {
			margin: .5em 0;
			}

			.ux-swatch-selected-value {
			font-weight: normal;
			font-size: .9em;
			}
		<?php endif; ?>

		.variations_form .ux-swatch.selected {
		box-shadow: 0 0 0 2px <?php echo get_theme_mod( 'swatches_color_selected' ) ?: 'var(--fs-color-secondary)'; // phpcs:ignore WordPress.Security.EscapeOutput ?>;
		}

		.ux-swatches-in-loop .ux-swatch.selected {
		box-shadow: 0 0 0 2px <?php echo get_theme_mod( 'swatches_box_color_selected' ) ?: 'var(--fs-color-secondary)'; // phpcs:ignore WordPress.Security.EscapeOutput ?>;
		}
		<?php
		$output = ob_get_clean();

		if ( ! $output ) {
			return;
		}

		$css  = '<style id="flatsome-swatches-css">';
		$css .= $output;
		$css .= '</style>';

		echo flatsome_minify_css( $css ); // phpcs:ignore WordPress.Security.EscapeOutput
	}

	/**
	 * The swatches HTML for an attribute.
	 *
	 * @param string $html The dropdown variation attribute options html.
	 * @param array  $args Args.
	 *
	 * @return string
	 * @uses swatch_html()
	 */
	public function get_swatch_html( $html, $args ) {
		$swatch_types = swatches()->get_attribute_types();
		$attr         = swatches()->get_attribute( $args['attribute'] );

		// Abort if this is normal attribute.
		if ( empty( $attr ) || ! array_key_exists( $attr->attribute_type, $swatch_types ) ) {
			return $html;
		}

		$swatches             = '';
		$options              = $args['options'];
		$product              = $args['product'];
		$attribute            = $args['attribute'];
		$classes              = array( 'ux-swatches', "ux-swatches-attribute-{$attr->attribute_type}" );
		$selector_classes     = array( 'variation-selector', "variation-select-{$attr->attribute_type}" );
		$args['tooltip']      = get_theme_mod( 'swatches_tooltip', 1 );
		$attr_options         = swatches()->get_attribute_option_by_name( $args['attribute'] );
		$use_variation_images = $this->use_variation_images( $attr_options );
		$variations           = $use_variation_images ? array_map( array( $this, 'get_variation' ), $product->get_children() ) : $product->get_available_variations();

		$args['swatches']                        = $this->get_swatches( $attribute, $options, $variations, $use_variation_images );
		$args['swatches']['use_variation_image'] = $use_variation_images;

		if ( isset( $attr_options['swatch_size'] ) && ! empty( $attr_options['swatch_size'] ) ) {
			$classes[] = 'ux-swatches--' . $attr_options['swatch_size'];
		}

		if ( isset( $attr_options['swatch_shape'] ) && ! empty( $attr_options['swatch_shape'] ) ) {
			$classes[] = 'ux-swatches--' . $attr_options['swatch_shape'];
		}

		if ( empty( $options ) && ! empty( $product ) && ! empty( $attribute ) ) {
			$attributes = $product->get_variation_attributes();
			$options    = $attributes[ $attribute ];
		}

		if ( ! empty( $options ) && taxonomy_exists( $attribute ) ) {
			$terms = wc_get_product_terms( $product->get_id(), $attribute, array( 'fields' => 'all' ) );

			foreach ( $terms as $term ) {
				if ( ! in_array( $term->slug, $options, true ) ) {
					continue;
				}
				$swatches .= apply_filters( 'flatsome_swatch_html', '', $term, $attr->attribute_type, $args );
			}
		}

		if ( ! empty( $swatches ) ) {
			$selector_classes[] = 'hidden';
			$swatches_atts      = [
				'class'               => esc_attr( implode( ' ', $classes ) ),
				'data-attribute_name' => esc_attr( 'attribute_' . $attribute ),
				'role'                => 'radiogroup',
				'aria-labelledby'     => esc_attr( $attribute ),
			];

			$swatches = '<div ' . flatsome_html_atts( $swatches_atts ) . '>' . $swatches . '</div>';
			$html     = '<div class="' . esc_attr( implode( ' ', $selector_classes ) ) . '">' . $html . '</div>' . $swatches;
		}

		return $html;
	}

	/**
	 * Single swatch HTML.
	 *
	 * @param string   $html HTML.
	 * @param \WP_Term $term WP Term object.
	 * @param string   $type Attribute type.
	 * @param array    $args Args.
	 *
	 * @return string
	 */
	public function swatch_html( $html, $term, $type, $args ) {
		$selected     = sanitize_title( $args['selected'] ) == $term->slug ? 'selected' : '';
		$aria_checked = ! empty( $selected ) ? 'true' : 'false';
		$name         = esc_html( apply_filters( 'woocommerce_variation_option_name', $term->name, $term, $args['attribute'], $args['product'] ) );
		$img_size     = apply_filters( 'flatsome_swatch_image_size', 'woocommerce_gallery_thumbnail', $term );
		$classes      = array( 'ux-swatch' );
		$thumb        = '';
		$tooltip      = '';

		if ( ! empty( $args['tooltip'] ) ) {
			$classes[] = 'tooltip';
			$tooltip   = $term->description ? $term->description : $name;
		}

		// Gather variation image if one is available.
		if ( $args['swatches']['use_variation_image'] ) {
			$options_flipped = array_flip( $args['options'] );
			$key             = $options_flipped[ $term->slug ];
			if ( isset( $args['swatches'][ $key ]['variation_id'] ) && $args['swatches'][ $key ]['variation_id'] ) {
				$thumb_id = get_post_thumbnail_id( $args['swatches'][ $key ]['variation_id'] );

				if ( $thumb_id ) {
					$thumb = wp_get_attachment_image( $thumb_id, $img_size, false, array(
						'class' => "ux-swatch__img attachment-$img_size size-$img_size",
						'alt'   => $name,
					) );
					$type  = 'variation-image';
				}
			}
		}

		switch ( $type ) {
			case 'ux_color':
				$color_classes = array( 'ux-swatch__color' );
				$value         = get_term_meta( $term->term_id, 'ux_color', true );
				$color         = swatches()->parse_ux_color_term_meta( $value );

				if ( $color['class'] ) $color_classes[] = $color['class'];

				$html = sprintf( '<div class="%s ux-swatch--color swatch-%s %s" aria-checked="%s" data-value="%s" data-name="%s" title="%s" role="radio" tabindex="0"><span class="%s" style="%s"></span><span class="ux-swatch__text">%s</span></div>',
					implode( ' ', $classes ),
					esc_attr( $term->slug ),
					$selected,
					$aria_checked,
					esc_attr( $term->slug ),
					$name,
					esc_attr( $tooltip ),
					implode( ' ', $color_classes ),
					$color['style'],
					$name
				);
				break;

			case 'ux_image':
				$image_id = get_term_meta( $term->term_id, 'ux_image', true );
				$image    = '';
				if ( $image_id ) {
					$image = wp_get_attachment_image( $image_id, $img_size, false, array(
						'class' => "ux-swatch__img attachment-$img_size size-$img_size",
						'alt'   => $name,
					) );
				}

				$html = sprintf( '<div class="%s ux-swatch--image %s" aria-checked="%s" data-value="%s" data-name="%s" title="%s" role="radio" tabindex="0">%s<span class="ux-swatch__text">%s</span></div>',
					implode( ' ', $classes ),
					$selected,
					$aria_checked,
					esc_attr( $term->slug ),
					$name,
					esc_attr( $tooltip ),
					$image ? $image : wc_placeholder_img( $img_size ),
					$name
				);
				break;

			case 'variation-image':
				$html = sprintf(
					'<div class="%s ux-swatch--image %s" aria-checked="%s" data-value="%s" data-name="%s" title="%s" role="radio" tabindex="0">%s<span class="ux-swatch__text">%s</span></div>',
					implode( ' ', $classes ),
					$selected,
					$aria_checked,
					esc_attr( $term->slug ),
					$name,
					esc_attr( $tooltip ),
					$thumb ? $thumb : wc_placeholder_img( $img_size ),
					$name
				);
				break;

			case 'ux_label':
				$label = get_term_meta( $term->term_id, 'ux_label', true );
				$label = $label ? $label : $name;
				$html  = sprintf(
					'<div class="%s ux-swatch--label %s" aria-checked="%s" data-value="%s" data-name="%s" title="%s" role="radio" tabindex="0"><span class="ux-swatch__text">%s</span></div>',
					implode( ' ', $classes ),
					$selected,
					$aria_checked,
					esc_attr( $term->slug ),
					$name,
					esc_attr( $tooltip ),
					esc_html( $label )
				);
				break;
		}

		return $html;
	}

	/**
	 * Get an array of types and values for each attribute.
	 *
	 * @param array $attributes Attributes.
	 *
	 * @return array
	 */
	public function get_variation_attributes_types( $attributes ) {
		global $wc_product_attributes;
		$types        = array();
		$defined_attr = swatches()->get_attribute_types();

		if ( ! empty( $attributes ) ) {
			foreach ( $attributes as $name => $options ) {
				$current = isset( $wc_product_attributes[ $name ] ) ? $wc_product_attributes[ $name ] : false;

				if ( $current && array_key_exists( $current->attribute_type, $defined_attr ) ) {
					$types[ $name ] = $current->attribute_type;
				}
			}
		}

		return $types;
	}

	/**
	 * Create a list of swatches on product boxes.
	 *
	 * @return string
	 */
	public function box_swatch_list() {
		global $product;

		$attribute = apply_filters( 'flatsome_swatches_box_attribute', $this->get_swatches_box_attribute(), $product );

		if ( ! $attribute ) {
			return;
		}

		$attribute_name = $attribute->slug;

		if ( ! $attribute_name || empty( $attribute_name ) ) {
			return;
		}

		$id = $product->get_id();

		if ( empty( $id ) || ! $product->is_type( 'variable' ) ) {
			return;
		}

		$attr_options  = swatches()->get_attribute_option_by_name( $attribute_name );
		$cache_enabled = apply_filters( 'flatsome_swatches_cache_enabled', true );
		$transient     = 'flatsome_swatches_cache_' . $id;
		$classes       = array( 'ux-swatches', 'ux-swatches-in-loop' );

		if ( $cache_enabled ) {
			$available_variations = get_transient( $transient );
		} else {
			$available_variations = array();
		}

		if ( ! $available_variations ) {
			/** @var $product \WC_Product_Variable */ // phpcs:ignore Generic.Commenting.DocComment.MissingShort
			$available_variations = $product->get_available_variations();
			if ( $cache_enabled ) {
				set_transient( $transient, $available_variations, apply_filters( 'flatsome_swatches_cache_time', WEEK_IN_SECONDS ) );
			}
		}

		if ( empty( $available_variations ) ) {
			return;
		}

		$swatches_to_show = $this->get_option_variations( $attribute_name, $available_variations );

		if ( empty( $swatches_to_show ) ) {
			return;
		}

		if ( count( $swatches_to_show ) < (int) apply_filters( 'flatsome_swatches_box_display_min_count', 1, $product, $attribute ) ) {
			return;
		}

		$html              = '';
		$mark_oos_inactive = get_theme_mod( 'swatches_box_out_of_stock_inactive', 0 );

		if ( get_theme_mod( 'swatches_box_shape' ) ) {
			$classes[] = 'ux-swatches--' . get_theme_mod( 'swatches_box_shape' );
		}
		if ( get_theme_mod( 'swatches_box_size' ) ) $classes[] = 'ux-swatches--' . get_theme_mod( 'swatches_box_size' );

		// Start ux-swatches.
		$html .= '<div ' . flatsome_html_atts( [
			'class' => $classes,
			'role'  => 'radiogroup',
		] ) . '>';

		// Order.
		$terms                = wc_get_product_terms( $product->get_id(), $attribute_name, array( 'fields' => 'slugs' ) );
		$swatches_to_show_tmp = $swatches_to_show;
		$swatches_to_show     = array();
		foreach ( $terms as $id => $slug ) {
			if ( ! isset( $swatches_to_show_tmp[ $slug ] ) ) {
				continue;
			}
			$swatches_to_show[ $slug ] = $swatches_to_show_tmp[ $slug ];
		}

		$index = 0;

		$swatch_count  = count( $swatches_to_show );
		$swatch_limit  = (int) get_theme_mod( 'swatches_box_limit', 5 );
		$swatch_layout = get_theme_mod( 'swatches_box_layout' );

		foreach ( $swatches_to_show as $key => $swatch ) {
			$swatch_classes    = array( 'ux-swatch' );
			$color_classes     = array( 'ux-swatch__color' );
			$term              = get_term_by( 'slug', $key, $attribute_name );
			$name              = esc_html( apply_filters( 'woocommerce_variation_option_name', $term->name, $term, $attribute_name, $product ) );
			$img_size          = apply_filters( 'flatsome_swatch_image_size', 'woocommerce_gallery_thumbnail', $term );
			$swatch_atts       = array();
			$type_tmp          = $attribute->type;
			$swatch_inner_html = '';

			if ( $this->use_variation_images( $attr_options ) && isset( $swatch['image_src'] ) ) {
				$thumb_id = get_post_thumbnail_id( $swatch['variation_id'] );

				if ( $thumb_id ) {
					$type_tmp          = 'variation-image';
					$swatch_classes[]  = 'ux-swatch--image';
					$swatch_inner_html = wp_get_attachment_image( $thumb_id, $img_size, false, array(
						'class' => "ux-swatch__img attachment-$img_size size-$img_size",
						'alt'   => $name,
					) );
				}
			}

			switch ( $type_tmp ) {
				case 'ux_color':
					$color = swatches()->parse_ux_color_term_meta( isset( $swatch['ux_color'] ) ? $swatch['ux_color'] : '' );

					if ( $color['class'] ) $color_classes[] = $color['class'];

					$swatch_classes[]  = 'ux-swatch--color';
					$swatch_inner_html = '<span class="' . implode( ' ', $color_classes ) . '" style="' . $color['style'] . '"></span>';
					break;
				case 'ux_image':
					$swatch_classes[]  = 'ux-swatch--image';
					$swatch_inner_html = isset( $swatch['ux_image'] ) ? wp_get_attachment_image( $swatch['ux_image'], $img_size, false, array(
						'class' => "ux-swatch__img attachment-$img_size size-$img_size",
						'alt'   => $name,
					) ) : wc_placeholder_img( $img_size );
					break;
				case 'ux_label':
					$swatch_classes[] = 'ux-swatch--label';
					break;
			}

			if ( isset( $swatch['image_src'] ) ) {
				$swatch_atts['data-image-src']    = $swatch['image_src'];
				$swatch_atts['data-image-srcset'] = $swatch['image_srcset'];
				$swatch_atts['data-image-sizes']  = $swatch['image_sizes'];
			}

			if ( isset( $swatch['is_in_stock'] ) && ! $swatch['is_in_stock'] ) {
				$swatch_classes[] = 'out-of-stock';
				if ( $mark_oos_inactive ) {
					$swatch_classes[]             = 'disabled';
					$swatch_atts['aria-disabled'] = 'true';
				}
			}

			if ( $swatch_layout === 'limit' && $swatch_count > $swatch_limit ) {
				if ( $index >= $swatch_limit ) {
					$swatch_classes[] = 'ux-swatch--limited hidden';
				}
				if ( $index === $swatch_limit ) {
					$remaining_count = $swatch_count - $swatch_limit;
					/* translators: %d: number of additional options */
					$html .= '<button type="button" class="ux-swatches__limiter" aria-label="' . esc_attr( sprintf( _n( 'Show %d more option', 'Show %d more options', $remaining_count, 'flatsome' ), $remaining_count ) ) . '" aria-expanded="false">+' . $remaining_count . '</button>';
				}
			}

			$swatch_atts['data-attribute_name'] = 'attribute_' . sanitize_title( $attribute_name );
			$swatch_atts['data-value']          = esc_attr( $term->slug );
			$swatch_atts['aria-label']          = $name;
			$swatch_atts['role']                = 'radio';
			$swatch_atts['aria-checked']        = 'false';
			$swatch_atts['tabindex']            = '0';
			$swatch_atts['class']               = $swatch_classes;

			$html .= '<div ' . flatsome_html_atts( $swatch_atts ) . '>' . $swatch_inner_html . '<span class="ux-swatch__text">' . $name . '</span></div>';

			$index ++;
		}

		$html .= '</div>';

		echo $html; // phpcs:ignore WordPress.Security.EscapeOutput
	}

	/**
	 * Get the attribute to show in loops by user preference.
	 *
	 * @return \stdClass|null The attribute object.
	 */
	private function get_swatches_box_attribute() {
		if ( ! get_theme_mod( 'swatches_box_attribute' ) ) {
			return null;
		}

		$attr = wc_get_attribute( get_theme_mod( 'swatches_box_attribute' ) );
		if ( $attr ) {
			return $attr;
		}

		return null;
	}

	/**
	 * Returns an array of data for a variation.
	 * (see \WC_Product_Variable->get_available_variation())
	 *
	 * @param \WC_Product $variation Variation product object or ID.
	 *
	 * @return array|bool
	 */
	public function get_variation( $variation ) {
		if ( is_numeric( $variation ) ) {
			$variation = wc_get_product( $variation );
		}
		if ( ! $variation instanceof \WC_Product_Variation ) {
			return false;
		}

		return array(
			'attributes'   => $variation->get_variation_attributes(),
			'image'        => wc_get_product_attachment_props( $variation->get_image_id() ),
			'image_id'     => $variation->get_image_id(),
			'is_in_stock'  => $variation->is_in_stock(),
			'variation_id' => $variation->get_id(),
		);
	}

	/**
	 * Get custom variation option data.
	 *
	 * @param string $attribute_name       Attribute name.
	 * @param array  $available_variations The available variation.
	 * @param mixed  $option               Whether to get only one variation by attribute option value.
	 *
	 * @return array|null
	 */
	private function get_option_variations( $attribute_name, $available_variations, $option = false ) {
		$swatches_to_show = array();
		$stock_status     = array();

		foreach ( $available_variations as $variation ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
			$option_variation = array();
			$attr_key         = 'attribute_' . sanitize_title( $attribute_name );
			if ( ! isset( $variation['attributes'][ $attr_key ] ) ) {
				return null;
			}

			$val = $variation['attributes'][ $attr_key ];

			if ( ! empty( $variation['image']['src'] ) ) {
				$option_variation = array(
					'variation_id' => $variation['variation_id'],
					'is_in_stock'  => $variation['is_in_stock'],
					'image_src'    => $variation['image']['thumb_src'],
					'image_srcset' => wp_get_attachment_image_srcset( $variation['image_id'], 'woocommerce_thumbnail' ),
					'image_sizes'  => wp_get_attachment_image_sizes( $variation['image_id'], 'woocommerce_thumbnail' ),
				);
			}

			// Get only one variation by attribute option value.
			if ( $option ) {
				if ( $val != $option ) {
					continue;
				} else {
					return $option_variation;
				}
			} else {
				if ( ! isset( $stock_status[ $val ] ) ) {
					$stock_status[ $val ] = false;
				}

				// If the variation is in stock, set the stock status to true for the relevant attribute.
				if ( $variation['is_in_stock'] ) {
					$stock_status[ $val ] = true;
				}

				// Or get all variations with swatches to show by attribute name.
				$swatch = $this->get_swatch( $attribute_name, $val );

				// If key already exist don't replace existing with sequential match.
				if ( ! array_key_exists( $val, $swatches_to_show ) ) {
					$swatches_to_show[ $val ] = array_merge( $swatch, $option_variation );
				}
			}
		}

		if ( ! $option ) {
			foreach ( $swatches_to_show as $key => $swatch ) {
				$swatches_to_show[ $key ]['is_in_stock'] = $stock_status[ $key ];
			}
		}

		return $swatches_to_show;
	}

	/**
	 * Get single swatch values.
	 *
	 * @param string     $attr_name Attribute name.
	 * @param string|int $value     Search for this term value.
	 *
	 * @return array
	 */
	private function get_swatch( $attr_name, $value ) {
		$swatches = array();
		$color    = '';
		$image    = '';
		$label    = '';

		$term = get_term_by( 'slug', $value, $attr_name );
		if ( is_object( $term ) ) {
			$color = get_term_meta( $term->term_id, 'ux_color', true );
			$image = get_term_meta( $term->term_id, 'ux_image', true );
			$label = get_term_meta( $term->term_id, 'ux_label', true );
		}

		if ( $color != '' ) {
			$swatches['ux_color'] = $color;
		}

		if ( $image != '' ) {
			$swatches['ux_image'] = $image;
		}

		if ( $label != '' ) {
			$swatches['ux_label'] = $label;
		}

		return $swatches;
	}

	/**
	 * Get swatches.
	 *
	 * @param string $attr_name            Attribute name.
	 * @param array  $options              Attribute options.
	 * @param array  $available_variations Available variations.
	 * @param bool   $use_variation_images Whether to search and collect the variation image. Default false.
	 *
	 * @return array
	 */
	private function get_swatches( $attr_name, $options, $available_variations, $use_variation_images = false ) {
		$swatches = array();

		foreach ( $options as $key => $value ) {
			$swatch = $this->get_swatch( $attr_name, $value );

			if ( ! empty( $swatch ) ) {
				if ( $available_variations && $use_variation_images ) {
					$variation = $this->get_option_variations( $attr_name, $available_variations, $value );
					if ( $variation ) {
						$swatch = array_merge( $swatch, $variation );
					}
				}
				$swatches[ $key ] = $swatch;
			}

			if ( empty( $swatch ) && $available_variations && $use_variation_images ) {
				$variation = $this->get_option_variations( $attr_name, $available_variations, $value );
				if ( $variation ) {
					$swatch           = array_merge( $swatch, $variation );
					$swatches[ $key ] = $swatch;
				}
			}
		}

		return $swatches;
	}

	/**
	 * Check if given attribute options wants to display variation images.
	 *
	 * @param array $options The attribute options array.
	 *
	 * @return bool Whether to us variation images.
	 */
	private function use_variation_images( $options ) {
		if ( ! $options ) {
			return false;
		}

		if ( isset( $options['swatch_variation_images'] ) && $options['swatch_variation_images'] ) {
			return true;
		}

		return false;
	}

	/**
	 * Render swatch value in layered nav.
	 *
	 * @param string     $term_html Term HTML.
	 * @param object     $term      Term.
	 * @param string     $link      Link.
	 * @param string|int $count     Count.
	 *
	 * @return string
	 */
	public function layered_nav_term_html( $term_html, $term, $link, $count ) {
		$swatch_types = swatches()->get_attribute_types();
		$attr         = swatches()->get_attribute( $term->taxonomy );

		// Abort if this is normal attribute.
		if ( empty( $attr ) || ! array_key_exists( $attr->attribute_type, $swatch_types ) ) {
			return $term_html;
		}

		$classes  = array( 'ux-swatch-widget-layered-nav-list__graphic' );
		$img_size = apply_filters( 'flatsome_swatch_image_size', 'woocommerce_gallery_thumbnail', $term );

		if ( get_theme_mod( 'swatches_box_shape' ) ) {
			$classes[] = 'ux-swatches--' . get_theme_mod( 'swatches_box_shape' );
		}

		switch ( $attr->attribute_type ) {
			case 'ux_image':
				$image_id  = get_term_meta( $term->term_id, 'ux_image', true );
				$image     = '';
				$classes[] = 'ux-swatch--image';

				if ( $image_id ) {
					$image = wp_get_attachment_image( $image_id, $img_size, false, array(
						'class' => "ux-swatch__img attachment-$img_size size-$img_size",
						'alt'   => $term->name,
					) );
				}

				$swatch = sprintf( '<div class="' . implode( ' ', $classes ) . '">%s</div>',
					$image ? $image : wc_placeholder_img( 'woocommerce_gallery_thumbnail' )
				);
				break;
			case 'ux_color':
				$color_classes = array( 'ux-swatch__color' );
				$value         = get_term_meta( $term->term_id, 'ux_color', true );
				$color         = swatches()->parse_ux_color_term_meta( $value );
				$classes[]     = 'ux-swatch--color';

				if ( $color['class'] ) $color_classes[] = $color['class'];

				$swatch = sprintf( '<div class="' . implode( ' ', $classes ) . '"><span class="%s" style="%s"></span></div>',
					implode( ' ', $color_classes ),
					$color['style']
				);
				break;
			default:
				return $term_html;
		}

		return $swatch . $term_html;
	}

	/**
	 * Clear cache on save post by ID.
	 *
	 * @param int $post_id Post ID.
	 */
	public function cache_clear_save_post( $post_id ) {
		if ( ! apply_filters( 'flatsome_swatches_cache_enabled', true ) ) {
			return;
		}

		$transient = 'flatsome_swatches_cache_' . $post_id;

		delete_transient( $transient );
	}

	/**
	 * Clear cache on product object save by ID.
	 *
	 * @param \WC_Product $product Product object.
	 */
	public function cache_clear_product_object_save( $product ) {
		if ( ! apply_filters( 'flatsome_swatches_cache_enabled', true ) ) {
			return;
		}
		$post_id   = $product->get_id();
		$transient = 'flatsome_swatches_cache_' . $post_id;
		delete_transient( $transient );
	}

	/**
	 * Whether this particular variation will appear greyed-out (inactive) or not (active).
	 * Makes a variation inactive if out of stock.
	 *
	 * @param bool                  $active    The state.
	 * @param \WC_Product_variation $variation The variation.
	 *
	 * @return bool
	 */
	public function variation_is_active( $active, $variation ) {
		if ( ! get_theme_mod( 'swatches_out_of_stock_inactive', 0 ) ) return $active;

		if ( ! $variation->is_in_stock() ) {
			return false;
		}

		return $active;
	}
}
