<?php

/**
 * The main functions of this plugin.
 *
 * @link       https://wpconfiguratorbuilder.com/
 * @since      1.0.0
 *
 * @package    wcb-configurator-builder
 * @subpackage wcb-configurator-builder/includes
 */

if ( ! function_exists( 'wpcb_sanitize_title' ) ) {
	/**
	 * Sanitize and filter the title.
	 *
	 * @since    1.0.0
	 */
	function wpcb_sanitize_title( $title ) {
		return urldecode( sanitize_title( $title ) );
	}
}

if ( ! function_exists( 'wpcb_get_i18n_suffix' ) ) {
	/**
	 * Get i18n theme options suffix
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_i18n_suffix() {
		$suffix = '';
		if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
			$suffix = '_' . ICL_LANGUAGE_CODE;
		}
		if ( '_all' === $suffix ){
			$suffix = ''; // keep base option name for all languages.
		}
		return $suffix;
	}
}

if ( ! function_exists( 'wpcb_get_option' ) ) {
	/**
	 * Get options page setting value
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_option( $option_name, $suffix = '' ) {
		return get_option( '_' . $option_name . $suffix );
	}
}

if ( ! function_exists( 'wpcb_get_theme_option' ) ) {
	/**
	 * Get options page setting value
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_theme_option( $option_name, $suffix = '' ) {
		return carbon_get_theme_option( $option_name . $suffix );
	}
}

if ( ! function_exists( 'wpcb_get_value' ) ) {
	/**
	 * Sanitize and filter the title.
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_value( $value ) {
		if ( ! is_array( $value ) || count( $value ) === 0 ) {
			return '';
		}
		return $value[0]['value'];
	}
}

if ( ! function_exists( 'wpcb_get_icon' ) ) {
	/**
	 * Get icon
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_icon( $icon ) {
		$default = wpcb_get_icon_url( $icon );

		return '<img src="' . esc_url( $default ) . '" width="24" height="24" alt="' . esc_attr( $icon ) . '">';
	}
}

if ( ! function_exists( 'wpcb_product_has_configurator' ) ) {
	/**
	 * Check if WooCommerce product has configurator attached
	 *
	 * @since    1.0.0
	 */
	function wpcb_product_has_configurator( $product_id ) {

		$args = array(
			'post_type' => 'wpcb_configuration',
			'post_status' => 'publish',
			'fields' => 'ids',
			'posts_per_page' => 999999,
			'no_found_rows' => true,
			'meta_key' => '_product_display',
			'meta_value' => 'product',
		);
		if ( defined( 'WPCB_STRING_TRANSLATION' ) ) {
			$args['suppress_filters'] = false; // query posts from all languages
		}
		$configurator_prod_ids = get_posts( $args );

		if ( is_array( $configurator_prod_ids ) && count( $configurator_prod_ids ) > 0 ) {

			/**
			 * Check if configurators linked to single products
			 * 
			 */

			$args = array(
				'post_type' => 'wpcb_configuration',
				'post_status' => 'publish',
				'fields' => 'ids',
				'posts_per_page' => 999999,
				'no_found_rows' => true,
				'post__in' => $configurator_prod_ids,
				'meta_query' => array(
					array(
						'key' => '_wpcb_products',
						'value' => $product_id,
						'compare' => 'LIKE'
					),
					array(
						'key' => '_product_display_rule',
						'value' => 'in',
					)
				)
			);
			if ( defined( 'WPCB_STRING_TRANSLATION' ) ) {
				$args['suppress_filters'] = false; // query posts from all languages
			}

			$configurator_temp_ids = get_posts( $args );
			if ( isset($configurator_temp_ids[0]) ) {
				return $configurator_temp_ids[0];
			}

			$args = array(
				'post_type' => 'wpcb_configuration',
				'post_status' => 'publish',
				'posts_per_page' => 999999,
				'no_found_rows' => true,
				'post__in' => $configurator_prod_ids,
				'fields' => 'ids',
				'meta_query' => array(
					array(
						'key' => '_wpcb_products',
						'value' => $product_id,
						'compare' => 'NOT LIKE'
					),
					array(
						'key' => '_product_display_rule',
						'value' => 'notin',
					)
				)
			);
			if ( defined( 'WPCB_STRING_TRANSLATION' ) ) {
				$args['suppress_filters'] = false; // query posts from all languages
			}

			$configurator_temp_ids = get_posts( $args );
			if ( isset($configurator_temp_ids[0]) ) {
				return $configurator_temp_ids[0];
			}
		}
		
		/**
		 * Check product categories
		 *
		 */ 

		$args = array(
			'post_type' => 'wpcb_configuration',
			'post_status' => 'publish',
			'fields' => 'ids',
			'posts_per_page' => 999999,
			'no_found_rows' => true,
			'meta_key' => '_product_display',
			'meta_value' => 'product_cat',
		);
		if ( defined( 'WPCB_STRING_TRANSLATION' ) ) {
			$args['suppress_filters'] = false; // query posts from all languages
		}
		$configurator_cat_ids = get_posts( $args );

		if ( is_array( $configurator_cat_ids ) && count( $configurator_cat_ids ) > 0 ) {
			$terms = get_the_terms( $product_id, 'product_cat' );

			if ( $terms ) {
				$has_term = false;
				$term_ids = array();
				$configurator_term_ids = array();

				foreach ( $terms as $term ) {
					$term_ids[] = $term->term_id;

					if ( 0 !== $term->parent ) {
						$term_ids[] = $term->parent;
					}
				}

				$args = array(
					'post_type' => 'wpcb_configuration',
					'post_status' => 'publish',
					'fields' => 'ids',
					'posts_per_page' => 999999,
					'no_found_rows' => true,
					'post__in' => $configurator_cat_ids,
					'meta_query' => array(
						array(
							'key' => '_wpcb_product_cats',
							'value' => $term_ids,
							'compare' => 'IN'
						),
						array(
							'key' => '_product_display_rule',
							'value' => 'in',
						)
					)
				);
				if ( defined( 'WPCB_STRING_TRANSLATION' ) ) {
					$args['suppress_filters'] = false; // query posts from all languages
				}
				$configurator_term_ids = get_posts( $args );

				if ( isset($configurator_term_ids[0]) ) {
					$has_term = true;

				} else {
					$args = array(
						'post_type' => 'wpcb_configuration',
						'post_status' => 'publish',
						'fields' => 'ids',
						'posts_per_page' => 999999,
						'no_found_rows' => true,
						'post__in' => $configurator_cat_ids,
						'meta_query' => array(
							array(
								'key' => '_wpcb_product_cats',
								'value' => $term_ids,
								'compare' => 'NOT IN'
							),
							array(
								'key' => '_product_display_rule',
								'value' => 'notin',
							)
						)
					);
					if ( defined( 'WPCB_STRING_TRANSLATION' ) ) {
						$args['suppress_filters'] = false; // query posts from all languages
					}
					$configurator_term_ids = get_posts( $args );

					if ( isset($configurator_term_ids[0]) ) {
						$has_term = true;
					}
				}
	
				if ( $has_term ) {
					return $configurator_term_ids[0];
				}
			}
		}

		/**
		 * Check product tags
		 *
		 */ 

		$args = array(
			'post_type' => 'wpcb_configuration',
			'post_status' => 'publish',
			'fields' => 'ids',
			'posts_per_page' => 999999,
			'no_found_rows' => true,
			'meta_key' => '_product_display',
			'meta_value' => 'product_tag',
		);
		if ( defined( 'WPCB_STRING_TRANSLATION' ) ) {
			$args['suppress_filters'] = false; // query posts from all languages
		}
		$configurator_tag_ids = get_posts( $args );

		if ( is_array( $configurator_tag_ids ) && count( $configurator_tag_ids ) > 0 ) {
			$terms = get_the_terms( $product_id, 'product_tag' );

			if ( $terms ) {
				$has_term = false;
				$configurator_temp_tag_ids = array();
				$term_ids = [];

				foreach ( $terms as $term ) {
					$term_ids[] = $term->term_id;
				}

				$args = array(
					'post_type' => 'wpcb_configuration',
					'post_status' => 'publish',
					'fields' => 'ids',
					'posts_per_page' => 999999,
					'no_found_rows' => true,
					'post__in' => $configurator_tag_ids,
					'meta_query' => array(
						array(
							'key' => '_wpcb_product_tags',
							'value' => $term_ids,
							'compare' => 'IN',
						),
						array(
							'key' => '_product_display_rule',
							'value' => 'in',
						)
					)
				);
				
				if ( defined( 'WPCB_STRING_TRANSLATION' ) ) {
					$args['suppress_filters'] = false; // query posts from all languages
				}

				$configurator_temp_tag_ids = get_posts( $args );
				if ( isset($configurator_temp_tag_ids[0]) ) {
					$has_term = true;

				} else {
					$args = array(
						'post_type' => 'wpcb_configuration',
						'post_status' => 'publish',
						'fields' => 'ids',
						'posts_per_page' => 999999,
						'no_found_rows' => true,
						'post__in' => $configurator_tag_ids,
						'meta_query' => array(
							array(
								'key' => '_wpcb_product_tags',
								'value' => $term_ids,
								'compare' => 'NOT IN',
							),
							array(
								'key' => '_product_display_rule',
								'value' => 'notin',
							)
						)
					);
					if ( defined( 'WPCB_STRING_TRANSLATION' ) ) {
						$args['suppress_filters'] = false; // query posts from all languages
					}
					$configurator_inc_tag_ids = get_posts( $args );

					$args_alt = array(
						'post_type' => 'wpcb_configuration',
						'post_status' => 'publish',
						'fields' => 'ids',
						'posts_per_page' => 999999,
						'no_found_rows' => true,
						'post__in' => $configurator_tag_ids,
						'meta_query' => array(
							array(
								'key' => '_wpcb_product_tags',
								'value' => $term_ids,
								'compare' => 'IN',
							),
							array(
								'key' => '_product_display_rule',
								'value' => 'notin',
							)
						)
					);
					if ( defined( 'WPCB_STRING_TRANSLATION' ) ) {
						$args_alt['suppress_filters'] = false; // query posts from all languages
					}
					$configurator_alt_tag_ids = get_posts( $args_alt );

					$configurator_temp_tag_ids = array_diff( $configurator_inc_tag_ids, $configurator_alt_tag_ids );

					if ( isset($configurator_temp_tag_ids[0]) ) {
						$has_term = true;
					}
				}

				if ( $has_term ) {
					return $configurator_temp_tag_ids[0];
				}
			}
		}
		return false;
	}
}

if ( !function_exists( 'wpcb_display_inline_edit_link' ) ) {
	/**
	 * Get inline edit link for attribute
	 *
	 * @since    1.0.0
	 */
	function wpcb_display_inline_edit_link( $field_id ) {
		if ( is_user_logged_in() && current_user_can('administrator') ) {
			echo '<a href="' . esc_url( admin_url() ) . 'post.php?post=' . esc_attr( $field_id ) . '&action=edit" class="wpcb-admin-edit">('
				. esc_attr__( 'edit', 'wcb-configurator-builder' ) . ')</a>';
		}
	}
}

if ( ! function_exists( 'wpcb__' ) ) {
	/**
	 * Get translatable text
	 *
	 * @since    1.0.0
	 */
	function wpcb__( $text, $context = '' ) {
		if ( function_exists( 'pll__' ) ) {
			// Return Polylang translatable text
			return pll__( $text );
		} else if ( function_exists( 'icl_t' ) ) {
			// Return WPML translatable text
			return icl_t( $context, 'wcb-configurator-builder', $text );
		}
		return $text;
	}
}

if ( ! function_exists( 'wpcb_get_icon_url' ) ) {
	/**
	 * Get icon
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_icon_url( $icon, $set = '' ) {
		$default = esc_url( WPCB_BASE_URL ) . 'public/assets/img/' . $icon . '.svg';
		
		if ( 'light' === wpcb_get_option('wpcb_icon_set') || 'light' === $set ) {
			$default = esc_url( WPCB_BASE_URL ) . 'public/assets/img/light/' . $icon . '.svg';
		}

		if ( wpcb_get_option('wpcb_icon_' . $icon ) ) {
			$icon_size = apply_filters( 'wpcb_icon_image_size', 'wpcb-icon' );
			$default = wp_get_attachment_image_url( wpcb_get_option('wpcb_icon_' . $icon ), $icon_size );
		}

		return $default;
	}
}

if ( ! function_exists( 'wpcb_get_icon_kses_args' ) ) {
	/**
	 * Get icon filter args for safe output
	 * 
	 * @return array
	 * 
	 * @since  1.0.0
	 */
	function wpcb_get_icon_kses_args() {
		$kses_args = array(
			'button'   => array(
				'class'           => true,
				'aria-hidden'     => true,
				'aria-role'       => true,
				'style'           => true,
			),
			'svg'   => array(
				'class'           => true,
				'aria-hidden'     => true,
				'aria-labelledby' => true,
				'role'            => true,
				'xmlns'           => true,
				'width'           => true,
				'height'          => true,
				'viewbox'         => true // <= Must be lower case!
			),
			'g' => array( 
				'fill' => true,
				'transform' => true,
			),
			'title' => array( 'title' => true ),
			'path'  => array( 
				'd'   => true, 
				'fill' => true  
			),
			'circle' => array(
				'fill' => true,
				'cx' => true,
				'cy' => true,
				'r' => true,
			),
			'img' => array(
				'src'    => true,
				'width'  => true,
				'height' => true,
				'class'  => true,
				'alt'    => true,
			)
		);

		return $kses_args;
	}
}

if ( ! function_exists( 'wpcb_get_html_kses_args' ) ) {
	/**
	 * Get html filter args for safe output
	 * 
	 * @return array
	 * 
	 * @since  1.0.0
	 */
	function wpcb_get_html_kses_args() {
		$kses_args = wp_kses_allowed_html( 'post' );

		$global_attributes = [
            'id'        => 1,
            'class'     => 1,
            'title'     => 1,
            'dir'       => 1,
            'lang'      => 1,
            'data-*'    => 1, // Allow any data- attribute
            'role'      => 1,
            'tabindex'  => 1,
            'aria-*'    => 1, // Allow any ARIA attributes for accessibility
            'style'     => 1,
        ];

        // Form elements
        $kses_args['form'] = array_merge( $global_attributes, [
            'action'  => 1,
            'method'  => 1,
            'enctype' => 1,
            'target'  => 1,
            'autocomplete' => 1,
            'novalidate' => 1,
        ]);
        $kses_args['input'] = array_merge( $global_attributes, [
            'type'     => 1,
            'name'     => 1,
            'value'    => 1,
            'placeholder' => 1,
            'required' => 1,
            'readonly' => 1,
            'disabled' => 1,
            'checked'  => 1,
            'maxlength'=> 1,
            'pattern'  => 1,
        ]);
        $kses_args['textarea'] = array_merge( $global_attributes, [
            'name'  => 1,
            'rows'  => 1,
            'cols'  => 1,
            'placeholder' => 1,
            'required' => 1,
            'maxlength' => 1,
        ]);
        $kses_args['select'] = array_merge( $global_attributes, [
            'name'     => 1,
            'required' => 1,
            'multiple' => 1,
        ]);
        $kses_args['option'] = [
            'value'    => 1,
            'selected' => 1,
        ];
        $kses_args['button'] = array_merge( $global_attributes, [
            'type'     => 1,
            'disabled' => 1,
        ]);
        $kses_args['label'] = array_merge( $global_attributes, [
            'for' => 1,
        ]);

        // Media elements
        $kses_args['video'] = array_merge( $global_attributes, [
            'src'      => 1,
            'width'    => 1,
            'height'   => 1,
            'controls' => 1,
            'autoplay' => 1,
            'loop'     => 1,
            'muted'    => 1,
            'poster'   => 1,
        ]);
        $kses_args['audio'] = array_merge( $global_attributes, [
            'src'      => 1,
            'controls' => 1,
            'autoplay' => 1,
            'loop'     => 1,
            'muted'    => 1,
        ]);
        $kses_args['source'] = [
            'src'  => 1,
            'type' => 1,
        ];
        $kses_args['img'] = array_merge( $global_attributes, $kses_args['img'] );
        $kses_args['figure'] = $global_attributes;
        $kses_args['picture'] = $global_attributes;

        // Interactive elements
        $kses_args['details'] = $global_attributes;
        $kses_args['summary'] = $global_attributes;
        $kses_args['progress'] = array_merge( $global_attributes, [
            'value' => 1,
            'max'   => 1,
        ]);
        $kses_args['meter'] = array_merge( $global_attributes, [
            'value' => 1,
            'min'   => 1,
            'max'   => 1,
            'low'   => 1,
            'high'  => 1,
            'optimum' => 1,
        ]);
        $kses_args['canvas'] = array_merge( $global_attributes, [
            'id'     => 1,
            'class'  => 1,
			'width'  => 1,
			'height' => 1,
			'data-*' => 1
        ]);

        $kses_args['noscript'] = $global_attributes;

		return $kses_args;
	}
}

if ( ! function_exists( 'wpcb_define_field_settings' ) ) {
	/**
	 * Define global field settings for the configurator
	 *
	 * @since    1.0.0
	 */
	function wpcb_define_field_settings() {
		global $wpcb_field_settings;

		$suffix = wpcb_get_i18n_suffix();

		$wpcb_field_settings = array(
			'wpcb_values_inline' => wpcb_get_option( 'wpcb_show_values_inline' ),
			'wpcb_summary_position' => wpcb_get_option( 'wpcb_default_summary_position' ),
			'wpcb_note_position' => wpcb_get_option( 'wpcb_default_note_position' ),
			'wpcb_description_position' => wpcb_get_option( 'wpcb_default_description_button_position' ) ?: 'above',
			'wpcb_description_type' => wpcb_get_option( 'wpcb_default_description_button_type' ) ?: 'icon',
			'wpcb_description_label' => wpcb_get_option( 'wpcb_description_button_label' . $suffix ) ?: '',
			'wpcb_swatch_option_style' => wpcb_get_option( 'wpcb_swatch_option_style' ),
			'wpcb_checked_option_style' => wpcb_get_option( 'wpcb_checked_option_style' ),
			'wpcb_lightbox' => wpcb_get_option( 'wpcb_lightbox_library' ),
		);
	}
}

if ( ! function_exists( 'wpcb_define_price_settings' ) ) {
	/**
	 * Define global price settings for configurator
	 *
	 * @since    1.0.0
	 */
	function wpcb_define_price_settings() {
		global $wpcb_price_settings;

		$suffix = wpcb_get_i18n_suffix();
		$wpcb_price_settings = array(
			'wpcb_free_price_label' => wpcb_get_option( 'wpcb_free_price_label', $suffix ),
			'wpcb_price_currency_symbol' => wpcb_get_option( 'wpcb_price_currency_symbol', $suffix ),
			'wpcb_price_currency_pos' => wpcb_get_option( 'wpcb_price_currency_pos', $suffix ),
			'wpcb_price_thousand_sep' => wpcb_get_option( 'wpcb_price_thousand_sep', $suffix ),
			'wpcb_price_decimal_sep' => wpcb_get_option( 'wpcb_price_decimal_sep', $suffix ),
			'wpcb_price_decimal_number' => wpcb_get_option( 'wpcb_price_decimal_number', $suffix ),
			'wpcb_extra_price_plus_prefix' => wpcb_get_option( 'wpcb_extra_price_plus_prefix' ),
			'wpcb_price_discount_label' => wpcb_get_option( 'wpcb_show_price_discount_labels' )
		);
	}
}

if ( ! function_exists( 'wpcb_define_stock_settings' ) ) {
	/**
	 * Define global stock settings for the configurator
	 *
	 * @since    1.0.0
	 */
	function wpcb_define_stock_settings() {
		global $wpcb_stock_settings;

		if ( function_exists( 'wpcb_get_global_stock_options' ) ) {

			$wpcb_stock_settings = wpcb_get_global_stock_options();

		} else {

			$wpcb_stock_settings = array(
				'wpcb_manage_stock' => false,
				'wpcb_display_low_stock' => false,
				'wpcb_hide_out_of_stock_options' => false,
				'wpcb_allow_backorders' => true,
				'wpcb_in_stock_label' => '',
			);
		}
	}
}

if ( ! function_exists( 'wpcb_define_price_rates' ) ) {
	/**
	 * Define global price settings for configurator
	 *
	 * @since    1.0.0
	 */
	function wpcb_define_price_rates() {
		global $wpcb_price_rates;

		$wpcb_price_rates = array(
			'currencies' => array(),
			'include_tax' => 'yes',
			'tax_display' => 'incl',
		);

		if ( wpcb_get_theme_option( 'wpcb_use_woocommerce_multi_currencies' ) ) {
			$currencies = array();
			$wcml_settings = get_option('_wcml_settings');
			if ( isset( $wcml_settings['currency_options'] ) && is_array( $wcml_settings['currency_options'] ) ) {
				foreach ( $wcml_settings['currency_options'] as $currency_id => $currency ) {
					$currencies[] = array(
						'currency_id' => $currency_id,
						'exchange_rate' => $currency['rate'],
						'exchange_rounding' => '0.01',
					);
				}
			}
			$wpcb_price_rates['currencies'] = $currencies;
		}

		if ( $currency_options = wpcb_get_theme_option( 'wpcb_multi_currencies' ) ) {
			$wpcb_price_rates['currencies'] = $currency_options;
		}
		
		if ( $discounts = wpcb_get_theme_option( 'wpcb_discounts' ) ) {
			$formatted_discounts = [];

			if ( is_array( $discounts ) ) {
				foreach ( $discounts as $discount ) {
					$discount_option = [
						'type' => $discount['discount_type'],
						'value' => floatval( $discount['discount'] ),
					];

					if ( isset( $discount['wpcb_discount_rules'] ) && is_array( $discount['wpcb_discount_rules'] ) ) {
						$discount_option['rules'] = array();
						foreach ( $discount['wpcb_discount_rules'] as $rule ) {
							if ( isset( $discount_option['rules'][$rule['choice']] ) ) {
								$discount_option['rules'][$rule['choice']][] = $rule['option'];
							} else {
								$discount_option['rules'][$rule['choice']] = [ $rule['option'] ];
							}
						}
					}

					if ( '' !== $discount['option_id'] ) {
						$formatted_discounts[ $discount['option_id'] ] = $discount_option;
					} else {
						$formatted_discounts[ $discount['choice_id'] ] = $discount_option;
					}
				}
			}

			$wpcb_price_rates['discounts'] = $formatted_discounts;
		}
		
		if ( $include_tax = get_option( 'woocommerce_prices_include_tax' ) ) {
			$wpcb_price_rates['include_tax'] = $include_tax;
		}

		if ( $tax_display = get_option( 'woocommerce_tax_display_shop' ) ) {
			$wpcb_price_rates['tax_display'] = $tax_display;
		}
	}
}

if ( ! function_exists( 'wpcb_display_required_icon_html' ) ) {
	/**
	 * Displays html for required icon
	 *
	 * @since    1.0.0
	 */
	function wpcb_display_required_icon_html() {
		$required_html = apply_filters( 'wpcb_required_icon_html', '<span class="wpcb-field-label-required">*</span>' );
		echo wp_kses_post( $required_html );
	}
}

if ( ! function_exists( 'wpcb_display_description_button_html' ) ) {
	/**
	 * Displays html for description button
	 *
	 * @since    1.4.1
	 */
	function wpcb_display_description_button_html( $settings ) {
		$class = ' wpcb-view-field-details-' . $settings['wpcb_description_position'];
		$type = $settings['wpcb_description_type'];
		if ( 'button' == $type || 'button_icon' == $type ) {
			$class .= ' wpcb-button';
		}
		if ( 'button' == $type || 'button_icon' == $type || 'text_icon' == $type ) {
			$class .= ' wpcb-text-link';
		}
		$button_html = '<button class="wpcb-view-field-details' . $class . '" aria-role="button">';
		if ( ( 'text' == $type || 'button' == $type || 'button_icon' == $type || 'text_icon' == $type ) && '' !== $settings['wpcb_description_label'] ) {
			$button_html .= '<span>' . $settings['wpcb_description_label'] . '</span>';
		}
		if ( 'icon' == $type || 'button_icon' == $type || 'text_icon' == $type ) {
			$button_html .= wpcb_get_icon( 'info' );
		}
		$button_html .= '</button>';

		$button_html = apply_filters( 'wpcb_description_button_html', $button_html );

		echo wp_kses( $button_html, wpcb_get_icon_kses_args() );
	}
}

if ( ! function_exists( 'wpcb_output_modelviewer_attributes' ) ) {
	/**
	 * Get 3D model viewer settings.
	 */
	function wpcb_output_modelviewer_attributes( $configurator_id ) {
		$touch_action = get_post_meta( $configurator_id, '_wpcb_model_camera_touch_action', true ) ?: 'pan-y';
		echo ' touch-action="' . esc_attr( $touch_action ) . '"';

		if ( ! get_post_meta( $configurator_id, '_wpcb_model_interaction_prompt', true ) ) {
			echo ' interaction-prompt="none"';
			// echo ' interaction-prompt-threshold="1"';
		}

		if ( $speed = get_post_meta( $configurator_id, '_wpcb_model_camera_speed' ) ) {
			echo ' interpolation-decay="' . esc_attr( intval( $speed ) ) . '"';
		}

		if ( get_post_meta( $configurator_id, '_wpcb_model_display_ar_button', true ) ) {
			echo ' ar camera-controls';

			if ( $placement = get_post_meta( $configurator_id, '_wpcb_model_ar_placement', true ) ) {
				echo ' ar-placement="' . esc_attr( $placement ) . '"';
			}

			if ( get_post_meta( $configurator_id, '_wpcb_model_fixed_ar', true ) ) {
				echo ' ar-scale="fixed"';
			}
		}

		$env_image_url = '';
		if ( $env_image_id = get_post_meta( $configurator_id, '_wpcb_model_env_image', true ) ) {
			$env_image_path = get_attached_file( $env_image_id );
			$env_image_url = str_replace( ABSPATH, trailingslashit( get_site_url() ), $env_image_path );
		} else if ( $env_url = get_post_meta( $configurator_id, '_wpcb_model_env_image_url', true ) ) {
			$env_image_url = $env_url;
		}

		if ( '' !== $env_image_url ) {
			echo ' environment-image="' . esc_url( $env_image_url ) . '"';
			echo ' data-env-image="' . esc_url( $env_image_url ) . '"';

			if ( get_post_meta( $configurator_id, '_wpcb_model_show_env_image', true ) ) {
				echo ' skybox-image="' . esc_attr( $env_image_url ) . '"';
				echo ' data-skybox-image="' . esc_attr( $env_image_url ) . '"';
			}

			if ( $sky_height = get_post_meta( $configurator_id, '_wpcb_model_env_image_height', true ) ) {
				echo ' skybox-height="' . esc_attr( $sky_height ) . '"';
			}
		}

		if ( $exposure = get_post_meta( $configurator_id, '_wpcb_model_env_image_exposure', true ) ) {
			echo ' exposure="' . esc_attr( $exposure ) . '"';
			echo ' data-exposure="' . esc_attr( $exposure ) . '"';
		}

		$poster_image_url = '';
		if ( $poster_id = get_post_meta( $configurator_id, '_wpcb_model_poster', true ) ) {
			$preview_size = apply_filters( 'wpcb_product_image_size', 'full' );
			$poster_image_url = wp_get_attachment_image_url( $poster_id, $preview_size );
		} else if ( $poster_url = get_post_meta( $configurator_id, '_wpcb_model_poster_url', true ) ) {
			$poster_image_url = $poster_url;
		}

		if ( '' !== $poster_image_url ) {
			echo ' poster="' . esc_url( $poster_image_url ) . '"';
		}

		if ( get_post_meta( $configurator_id, '_wpcb_model_custom_shadow', true ) ) {
			$intensity = get_post_meta( $configurator_id, '_wpcb_model_shadow_intensity', true );
			$softness = get_post_meta( $configurator_id, '_wpcb_model_shadow_softness', true );

			if ( '' !== $intensity ) {
				echo ' shadow-intensity="' . esc_attr( $intensity ) . '"';
			}
			if ( '' !== $softness ) {
				echo ' shadow-softness="' . esc_attr( $softness ) . '"';
			}
		}

		if ( get_post_meta( $configurator_id, '_wpcb_model_custom_target' ) ) {
			$target = get_post_meta( $configurator_id, '_wpcb_model_target' );

			if ( $target ) {
				echo ' camera-target="' . esc_attr( $target ) . '"';
				echo ' data-target="' . esc_attr( $target ) . '"';
			}
		}

		if ( get_post_meta( $configurator_id, '_wpcb_model_custom_view', true ) ) {
			$position = get_post_meta( $configurator_id, '_wpcb_model_initial_view', true );
			$view = get_post_meta( $configurator_id, '_wpcb_model_initial_fov', true );

			if ( '' !== $position ) {
				echo ' camera-orbit="' . esc_attr( $position ) . '"';
				echo ' data-orbit="' . esc_attr( $position ) . '"';
			}
			if ( '' !== $view ) {
				echo ' field-of-view="' . esc_attr( $view ) . '"';
				echo ' data-view="' . esc_attr( $view ) . '"';
			}
		}

		if ( get_post_meta( $configurator_id, '_wpcb_model_custom_view_limits', true ) ) {
			$counter_clockwise = get_post_meta( $configurator_id, '_wpcb_model_counter_clockwise_limit', true );
			$clockwise = get_post_meta( $configurator_id, '_wpcb_model_clockwise_limit', true );
			$topdown = get_post_meta( $configurator_id, '_wpcb_model_topdown_limit', true );
			$bottomup = get_post_meta( $configurator_id, '_wpcb_model_bottomup_limit', true );

			if ( '' !== $counter_clockwise ) {
				$counter_clockwise .= 'deg';
			} else {
				$counter_clockwise = 'auto';
			}
			if ( '' !== $clockwise ) {
				$clockwise .= 'deg';
			} else {
				$clockwise = 'auto';
			}
			if ( '' !== $topdown ) {
				$topdown .= 'deg';
			} else {
				$topdown = 'auto';
			}
			if ( '' !== $bottomup ) {
				$bottomup .= 'deg';
			} else {
				$bottomup = 'auto';
			}

			echo ' min-camera-orbit="' . esc_attr( $counter_clockwise ) . ' ' . esc_attr( $topdown ) . ' auto"';
			echo ' max-camera-orbit="' . esc_attr( $clockwise ) . ' ' . esc_attr( $bottomup ) . ' auto"';
		}

		if ( get_post_meta( $configurator_id, '_wpcb_model_auto_rotation', true ) ) {
			echo ' auto-rotate';
		}

		if ( get_post_meta( $configurator_id, '_wpcb_model_disable_zoom', true ) ) {
			echo ' disable-zoom';
		}
	}
}

if ( ! function_exists( 'wpcb_output_modelviewer_contents' ) ) {
	/**
	 * Get 3D model viewer settings.
	 */
	function wpcb_output_modelviewer_contents() {
		global $wpcb_configuration_id;

		if ( get_post_meta( $wpcb_configuration_id, '_wpcb_model_display_loader', true ) ) {
			?>
			<div slot="progress-bar" class="wpcb-model-viewer-loader">
				<div class="wpcb-model-viewer-loader-bar">
					<div class="wpcb-model-viewer-loader-progress"></div>
					<div class="wpcb-model-viewer-loader-value"></div>
				</div>
			</div>
			<?php
		}

		if ( get_post_meta( $wpcb_configuration_id, '_wpcb_model_display_ar_button', true ) ) {
			?>
			<button slot="ar-button" id="ar-button" class="ar-button">
				<?php
				$suffix = wpcb_get_i18n_suffix();
				$ar_icon = wpcb_get_icon( 'view-ar' );
				$ar_icon_text = wpcb_get_option( 'wpcb_button_view_ar_text' . $suffix ) ?: 'View in room';

				echo $ar_icon;
				if ( $ar_icon_text ) {
					echo '<span>' . $ar_icon_text . '</span>';
				}
				?>
			</button>
			<?php
		}

		$wpcb_actions = carbon_get_post_meta( $wpcb_configuration_id, 'gallery_actions' ) ?: array();
		$toggle_dimensions = in_array( 'dimensions', $wpcb_actions );
		if ( 'yes' == get_post_meta( $wpcb_configuration_id, '_wpcb_model_show_dimensions', true ) || $toggle_dimensions ) {
			$unit = get_post_meta( $wpcb_configuration_id, '_wpcb_model_dimension_unit', true );
			$show_dims = 'yes' == get_post_meta( $wpcb_configuration_id, '_wpcb_model_show_dimensions', true );
			?>
			<button slot="hotspot-dot+X-Y+Z" class="wpcb-dot" data-position="1 -1 1" data-normal="1 0 0"></button>
			<button slot="hotspot-dim+X-Y" class="wpcb-dim" data-position="1 -1 0" data-normal="1 0 0"<?php echo ! $show_dims ? ' style="display: none;"' : '' ?>></button>
			<button slot="hotspot-dot+X-Y-Z" class="wpcb-dot" data-position="1 -1 -1" data-normal="1 0 0"></button>
			<button slot="hotspot-dim+X-Z" class="wpcb-dim" data-position="1 0 -1" data-normal="1 0 0"<?php echo ! $show_dims ? ' style="display: none;"' : '' ?>></button>
			<button slot="hotspot-dot+X+Y-Z" class="wpcb-dot" data-position="1 1 -1" data-normal="0 1 0"></button>
			<button slot="hotspot-dim+Y-Z" class="wpcb-dim" data-position="0 -1 -1" data-normal="0 1 0"<?php echo ! $show_dims ? ' style="display: none;"' : '' ?>></button>
			<button slot="hotspot-dot-X+Y-Z" class="wpcb-dot" data-position="-1 1 -1" data-normal="0 1 0"></button>
			<button slot="hotspot-dim-X-Z" class="wpcb-dim" data-position="-1 0 -1" data-normal="-1 0 0"<?php echo ! $show_dims ? ' style="display: none;"' : '' ?>></button>
			<button slot="hotspot-dot-X-Y-Z" class="wpcb-dot" data-position="-1 -1 -1" data-normal="-1 0 0"></button>
			<button slot="hotspot-dim-X-Y" class="wpcb-dim" data-position="-1 -1 0" data-normal="-1 0 0"<?php echo ! $show_dims ? ' style="display: none;"' : '' ?>></button>
			<button slot="hotspot-dot-X-Y+Z" class="wpcb-dot" data-position="-1 -1 1" data-normal="-1 0 0"></button>
			<svg id="wpcb-dim-lines-<?php echo esc_attr( $wpcb_configuration_id ); ?>" class="wpcb-dim-lines" data-unit="<?php echo esc_attr( $unit ); ?>" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" <?php echo ! $show_dims ? ' style="display: none;"' : '' ?>>
				<line class="wpcb-dim-line"></line>
				<line class="wpcb-dim-line"></line>
				<line class="wpcb-dim-line"></line>
				<line class="wpcb-dim-line"></line>
				<line class="wpcb-dim-line"></line>
			</svg>
			<?php
		}
	}
}

if ( ! function_exists( 'wpcb_display_modelviewer_scenes' ) ) {
	/**
	 * Get 3D model viewer settings.
	 */
	function wpcb_display_modelviewer_scenes( $scenes, $wpcb_configuration_id ) {
		$nav_labels = explode( ',', get_post_meta( $wpcb_configuration_id, '_wpcb_model_label_controls', true ) );
		$nav_images = carbon_get_post_meta( $wpcb_configuration_id, 'wpcb_model_image_controls' );

		$nav_type = get_post_meta( $wpcb_configuration_id, '_wpcb_model_controls', true );
		$nav_class = $nav_type;

		if ( wpcb_get_option( 'wpcb_border_style' ) ) {
			$nav_class .= ' wpcb-border-' . wpcb_get_option( 'wpcb_border_style' );
		}

		$position = get_post_meta( $wpcb_configuration_id, '_wpcb_model_control_position', true );
		$nav_class .= ' wpcb-configurator-gallery-nav--' . $position;

		$nav_icon_size = apply_filters( 'wpcb_gallery_nav_icon_size', 'thumbnail' );
		?>
		<div class="wpcb-configurator-gallery-nav wpcb-configurator-gallery-nav--<?php echo esc_attr( $nav_class ); ?>">
			<?php
			foreach ( $scenes as $wpcb_slot => $wpcb_image_id ) : 
				$active = '';
				if ( 0 == $wpcb_slot ) {
					$active = ' active';
				}
				?>
				<button 
					class="wpcb-configurator-gallery-nav__item<?php echo esc_attr( $active ); ?>"
					data-slot="<?php echo esc_attr( $wpcb_slot + 1 ); ?>"
					<?php

					if ( isset( $scenes[ $wpcb_slot ] ) ) {
						$target = wpcb_get_value( $scenes[ $wpcb_slot ]['scene_target'] );
						if ( $target ) {
							echo ' data-model-target="' . esc_attr( $target ) . '"';
						}

						$orbit = wpcb_get_value( $scenes[ $wpcb_slot ]['scene_orbit'] );
						if ( $orbit ) {
							echo ' data-model-orbit="' . esc_attr( $orbit ) . '"';
						}

						$view = wpcb_get_value( $scenes[ $wpcb_slot ]['scene_view'] );
						if ( $view ) {
							echo ' data-model-view="' . esc_attr( $view ) . '"';
						}

						$image_id = wpcb_get_value( $scenes[ $wpcb_slot ]['scene_env_image'] );
						$image_url = wpcb_get_value( $scenes[ $wpcb_slot ]['scene_env_image_url'] );
						if ( $image_id && ! $image_url ) {
							$image_url = str_replace( ABSPATH, trailingslashit( get_site_url() ), get_attached_file( $image_id ) );
						}
						if ( $image_url ) {
							echo ' data-env="' . esc_url( $image_url ) . '"';
						}

						$exposure = wpcb_get_value( $scenes[ $wpcb_slot ]['scene_env_exposure'] );
						if ( $exposure ) {
							echo ' data-exposure="' . esc_attr( $exposure ) . '"';
						}
					}
					?>>
					<?php
					if ('labels' === $nav_type) {

						echo '<div class="wpcb-configurator-gallery-nav-label">' . esc_attr( $nav_labels[$wpcb_slot] ) . '</div>';

					} 
					else if ('icons' === $nav_type) {

						echo '<div class="wpcb-configurator-gallery-nav-icon">' . wp_get_attachment_image( $nav_images[$wpcb_slot], $nav_icon_size ) . '</div>';

					}
					?>
				</button>
				<?php
			endforeach;
			?>
		</div>
		<?php
	}
}

if ( ! function_exists( 'wpcb_configurator_gallery_info_button' ) ) {
	/**
	 * Product configurator gallery info button.
	 *
	 * @return void
	 */
	function wpcb_configurator_gallery_info_button() {
		$suffix = wpcb_get_i18n_suffix();
		$info_label = wpcb_get_option( 'wpcb_button_instruction_text', $suffix ) ?: __( 'View instructions', 'wcb-configurator-builder' );
		?>
		<button class="wpcb-configurator-gallery-action show-viewer-info">
			<?php echo wp_kses( wpcb_get_icon( 'info' ), wpcb_get_icon_kses_args() ); ?>
			<span class="wpcb-configurator-gallery-action-label">
				<?php echo esc_attr( $info_label ); ?>
			</span>
		</button>
		<?php
	}
}

if ( ! function_exists( 'wpcb_configurator_gallery_qr_button' ) ) {
	/**
	 * Product configurator gallery info button.
	 *
	 * @return void
	 */
	function wpcb_configurator_gallery_qr_button() {
		$suffix = wpcb_get_i18n_suffix();
		$qr_label = wpcb_get_option( 'wpcb_button_qr_code_text', $suffix ) ?: __( 'View QR code', 'wcb-configurator-builder' );
		?>
		<button class="wpcb-configurator-gallery-action show-qr-code">
			<?php echo wp_kses( wpcb_get_icon( 'qr' ), wpcb_get_icon_kses_args() ); ?>
			<span class="wpcb-configurator-gallery-action-label">
				<?php echo esc_attr( $qr_label ); ?>
			</span>
		</button>
		<?php
	}
}

if ( ! function_exists( 'wpcb_configurator_gallery_info_popup' ) ) {
	/**
	 * Product configurator gallery info popup.
	 *
	 * @return void
	 */
	function wpcb_configurator_gallery_info_popup( $suffix ) {
		global $wpcb_configuration_id;

		$qr_title = wpcb_get_option( 'wpcb_instruction_popup_title', $suffix ) ?: __( 'Configuration viewer instructions', 'wcb-configurator-builder' );
		$qr_intro = wpcb_get_option( 'wpcb_instruction_popup_intro', $suffix ) ?: __( 'Here is a list of instructions:<br>1. Pan to zoom<br>2. Use mouse to zoom<br>3. Drag to rotate', 'wcb-configurator-builder' );
	
		$content = sprintf(
			'<p>%1$s</p>',
			$qr_intro,
		);
	
		$qr_args = array(
			'description' => $content,
			'id' => 'wpcb-viewer-info-popup-' . $wpcb_configuration_id,
		);

		wpcb_display_popup_html_safe( $qr_title, $qr_args ); 
	}
}

if ( ! function_exists( 'wpcb_configurator_gallery_qr_popup' ) ) {
	/**
	 * Product configurator gallery qr popup.
	 *
	 * @return void
	 */
	function wpcb_configurator_gallery_qr_popup( $suffix ) {
		global $wpcb_configuration_id;

		$qr_title = wpcb_get_option( 'wpcb_qr_code_popup_title', $suffix ) ?: __( 'Your configuration QR code', 'wcb-configurator-builder' );
		$qr_intro = wpcb_get_option( 'wpcb_qr_code_popup_intro', $suffix ) ?: __( 'Please scan the following QR with your mobile phone to view this model in your room.', 'wcb-configurator-builder' );

		$content = sprintf(
			'<p>%1$s</p>
			<div class="wpcb_configuration_qr" data-color="%2$s" data-background="%3$s"></div>',
			$qr_intro,
			wpcb_get_option( 'wpcb_template_title_color' ),
			wpcb_get_option( 'wpcb_primary_color' )
		);
	
		$qr_args = array(
			'description' => $content,
			'id' => 'wpcb-qr-code-popup-' . $wpcb_configuration_id,
		);

		wpcb_display_popup_html_safe( $qr_title, $qr_args ); 
	}
}

if ( ! function_exists( 'wpcb_output_input_popup_button' ) ) {
	/**
	 * Output input field popup customize control
	 */
	function wpcb_output_input_popup_button() {
		global $wpcb_field;

		$wpcb_field_id = wpcb_sanitize_title( $wpcb_field['id'] );
		if ( ( isset( $wpcb_field['visual']['customize'] ) && 'yes' == $wpcb_field['visual']['customize'] ) ) :
			?>

			<a href="#canvas_popup_<?php echo esc_attr( $wpcb_field_id ); ?>" id="customize_<?php echo esc_attr( $wpcb_field_id ); ?>" class="wpcb-button wpcb-button-alt wpcb-button-customize-popup" aria-label="Customize">
				<?php echo wp_kses( wpcb_get_icon('load'), wpcb_get_icon_kses_args() ); ?>
			</a>

			<?php 
		endif;
	}
}

if ( ! function_exists( 'wpcb_output_input_field_canvas' ) ) {
	/**
	 * Output input field popup canvas.
	 */
	function wpcb_output_input_field_canvas() {
		global $wpcb_field;

		if ( isset( $wpcb_field['visual']['canvas_width'] ) && $wpcb_field['visual']['canvas_width'] && $wpcb_field['visual']['canvas_height'] ) {
			$wpcb_field_id = wpcb_sanitize_title( $wpcb_field['id'] );
			$material = isset( $wpcb_field['visual']['material_name'] ) ? $wpcb_field['visual']['material_name'] : '';

			$html = '
			<div class="wpcb-input-field-canvas"
				data-width="' .  esc_attr( $wpcb_field['visual']['canvas_width'] ) . '"
				data-height="' .  esc_attr( $wpcb_field['visual']['canvas_height'] ) . '">
				<canvas 
					id="canvas_' .  esc_attr( $wpcb_field_id ) . '" 
					class="wpcb-configurator-field-canvas"
					data-material-name="' . esc_attr( $material ) . '"
					width="' .  esc_attr( $wpcb_field['visual']['canvas_width'] ) . '"
					height="' .  esc_attr( $wpcb_field['visual']['canvas_height'] ) . '">
				</canvas>
				<div class="wpcb-configurator-field-canvas-controls">
					<button type="button" class="wpcb-canvas-plus">' . wpcb_get_icon('plus') . '</button>
					<button type="button" class="wpcb-canvas-minus">' . wpcb_get_icon('minus') . '</button>
					<button type="button" class="wpcb-canvas-rotate">' . wpcb_get_icon('refresh') . '</button>
					<button type="button" class="wpcb-canvas-rotate-alt">' . wpcb_get_icon('refresh') . '</button>
				</div>
			</div>
			<div class="wpcb-input-field-canvas-actions">
				<button type="button" class="wpcb-button wpcb-button-alt">' . __( 'Cancel', 'wcb-configurator-builder' ) . '</button>
				<button type="button" class="wpcb-button wpcb-button-primary">' . __( 'Apply', 'wcb-configurator-builder' ) . '</button>
			</div>';

			if ( ( isset( $wpcb_field['visual']['customize'] ) && 'yes' == $wpcb_field['visual']['customize'] ) ) {
				wpcb_display_popup_html_safe( 
					$wpcb_field['field']['label'],
					array(
						'id' => 'canvas_popup_' . $wpcb_field_id,
						'class' => 'wpcb-canvas-popup',
						'description' => $html
					)
				);
			} else {
				echo '<div class="wpcb-input-field-inline-canvas">';
				echo wp_kses( $html, wpcb_get_html_kses_args() );
				echo '</div>';
			}
		}
	}
}

if ( ! function_exists( 'wpcb_display_option_price_wrapper_html' ) ) {
	/**
	 * Display html of option price wrapper
	 *
	 * @since    1.0.0
	 */
	function wpcb_display_option_price_wrapper_html( $wpcb_option, $show_discount = false ) {
		global $wpcb_price_settings;
		if ( ! $wpcb_price_settings ) {
			wpcb_define_price_settings();
		}

		$show_discount_label = false;
		if ( isset( $wpcb_price_settings['wpcb_price_discount_label'] ) && $wpcb_price_settings['wpcb_price_discount_label'] && $show_discount ) {
			$show_discount_label = true;
		}

		$price = isset( $wpcb_option['price'] ) ? $wpcb_option['price'] : '';
		if ( '' !== $price ) {
			$sale = isset( $wpcb_option['sale_price'] ) ? $wpcb_option['sale_price'] : '';
			// Display fixed price.
			echo '<p class="wpcb-option-price">';
			echo wp_kses_post( wpcb_get_price_html( $price, $sale ) );
			if ( $show_discount_label && '' !== $sale ) {
				$percent = ceil( ( $price - $sale ) / $price * 100 );
				echo '<span class="wpcb-discount-label">-' . absint( $percent ) . '%</span>';
			}
			echo '</p>';
		} else {
			$percent = isset( $wpcb_option['percent'] ) ? $wpcb_option['percent'] : '';
			$formula = isset( $wpcb_option['formula'] ) ? $wpcb_option['formula'] : '';
			$table   = isset( $wpcb_option['price_table'] ) ? $wpcb_option['price_table'] : '';
			if ( '' !== $percent || '' !== $formula || '' !== $table ) {
				// Display placeholder for price.
				echo '<p class="wpcb-option-price"></p>';
			}
		}
	}
}

if ( ! function_exists( 'wpcb_get_option_note_html' ) ) {
	/**
	 * Returns html of option note
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_option_note_html( $wpcb_option ) {
		$note_html = '';
		if ( isset( $wpcb_option['note'] ) && '' !== $wpcb_option['note'] ) {
			$note_html = sprintf( '<small class="wpcb-option-note">%s</small>', $wpcb_option['note'] );
		}
		return $note_html;
	}
}

if ( ! function_exists( 'wpcb_get_option_summary_html' ) ) {
	/**
	 * Returns html of option summary
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_option_summary_html( $id, $wpcb_option, $price_hidden = 'no' ) {
		global $wpcb_price_settings;
		if ( ! $wpcb_price_settings ) {
			wpcb_define_price_settings();
		}

		$show_discount_label = false;
		if ( isset( $wpcb_price_settings['wpcb_price_discount_label'] ) && $wpcb_price_settings['wpcb_price_discount_label'] ) {
			$show_discount_label = true;
		}

		$note_html = wpcb_get_option_note_html( $wpcb_option );

		$price_html = '';
		if ( 'no' === $price_hidden ) {
			if ( isset( $wpcb_option['price'] ) && '' !== $wpcb_option['price'] ) {
				$sale = isset( $wpcb_option['sale_price'] ) ? $wpcb_option['sale_price'] : '';

				$discount_label = '';
				if ( $show_discount_label && '' !== $sale ) {
					$percent = ceil( ( $wpcb_option['price'] - $sale ) / $wpcb_option['price'] * 100 );
					$discount_label = '<span class="wpcb-discount-label">-' . absint( $percent ) . '%</span>';
				}

				$price_html = sprintf( '<p class="wpcb-option-price">%s</p>', wpcb_get_price_html( $wpcb_option['price'], $sale ) . $discount_label );
			}
		}

		$stock_html = ''; 
		if ( function_exists( 'wpcb_get_option_stock_html' ) ) {
			$stock_html = wpcb_get_option_stock_html( $wpcb_option );
		}

		$button_html = '';
		if ( isset( $wpcb_option['description'] ) && '' !== $wpcb_option['description'] ) {
			$button_html = sprintf(
				'<button data-id="%1$s" class="wpcb-view-option-details">%2$s</button>',
				esc_attr( $id ),
				wpcb_get_icon( 'info' )
			);
		}

		$summary_html = sprintf(
			'<div data-summary-id="%s" class="wpcb-option-summary">
				<div class="wpcb-option-details">
					<p class="wpcb-option-label">%s</p>
					%s
				</div>
				<div class="wpcb-option-totals">
					%s
					%s
					%s
				</div>
			</div>',
			$id,
			$wpcb_option['label'],
			$note_html,
			$stock_html,
			$price_html,
			$button_html
		);

		return apply_filters( 'wpcb_get_configurator_option_summary_html', $summary_html, $id, $wpcb_option, $price_hidden );
	}
}

if ( ! function_exists( 'wpcb_display_panel_html_safe' ) ) {
	/**
	 * Returns html of option summary html
	 *
	 * @since    1.0.0
	 */
	function wpcb_display_panel_html_safe( $label, $args = [] ) {
		global $wpcb_configuration_id;

		$wpcb_panel_type = 'panel';
		if ( wpcb_get_option( 'wpcb_panel_view_type' ) ) {
			$wpcb_panel_type = wpcb_get_option( 'wpcb_panel_view_type' );
		}
		if ( isset( $args['popup'] ) && $args['popup'] ) {
			$wpcb_panel_type = 'popup';
		}

		$panel_class = '';
		if ( wpcb_get_option( 'wpcb_border_style' ) ) {
			$panel_class .= ' wpcb-border-' . wpcb_get_option( 'wpcb_border_style' );
		}
		if ( isset( $args['class'] ) && '' !== $args['class'] ) {
			$panel_class .= ' ' . $args['class'];
		}

		$id = '';
		if ( isset( $args['id'] ) ) {
			$id = $args['id'];
		}
		?>
		<div id="<?php echo esc_attr( $id ); ?>" class="wpcb-configurator-<?php echo esc_attr( $wpcb_panel_type ); ?> <?php echo esc_attr( $panel_class ); ?>" data-wpcb-id="<?php echo esc_attr( $wpcb_configuration_id ); ?>">
			<div class="wpcb-configurator-<?php echo esc_attr( $wpcb_panel_type ); ?>-inner">
				<div class="wpcb-configurator-<?php echo esc_attr( $wpcb_panel_type ); ?>-gallery"></div>
				<div class="wpcb-configurator-<?php echo esc_attr( $wpcb_panel_type ); ?>-header">
					<div class="wpcb-configurator-<?php echo esc_attr( $wpcb_panel_type ); ?>-details">
						<p class="wpcb-configurator-<?php echo esc_attr( $wpcb_panel_type ); ?>-label"><?php echo esc_attr( $label ); ?></p>
						<p class="wpcb-configurator-<?php echo esc_attr( $wpcb_panel_type ); ?>-price"></p>
					</div>
					<button class="wpcb-configurator-<?php echo esc_attr( $wpcb_panel_type ); ?>-close wpcb-panel-close" aria-role="button">
						<?php echo wp_kses( wpcb_get_icon( 'close' ), wpcb_get_icon_kses_args() ); ?>
					</button>
				</div>
				<div class="wpcb-configurator-<?php echo esc_attr( $wpcb_panel_type ); ?>-title"></div>
				<div class="wpcb-configurator-<?php echo esc_attr( $wpcb_panel_type ); ?>-content wpcb-panel-content">
					<?php 
					if ( isset( $args['description'] ) && '' !== $args['description'] ) {
						echo wp_kses( $args['description'], wpcb_get_html_kses_args() );
					}
					
					if ( isset( $args['shortcode'] ) && '' !== $args['shortcode'] ) {
						echo do_shortcode( $args['shortcode'] );
					}
					?>
				</div>
				<?php
				if ( isset( $args['action'] ) && '' !== $args['action'] ) {
					echo '<div class="wpcb-configurator-' . esc_attr( $wpcb_panel_type ) . '-action">' . wp_kses_post( $args['action'] ) . '</div>';
				}
				?>
			</div>
		</div>
		<?php
	}
}

if ( ! function_exists( 'wpcb_display_popup_html_safe' ) ) {
	/**
	 * Returns html of option summary html
	 *
	 * @since    1.0.0
	 */
	function wpcb_display_popup_html_safe( $label, $args = [] ) {
		$args['popup'] = true;
		wpcb_display_panel_html_safe( $label, $args );
	}
}

if ( ! function_exists( 'wpcb_get_field_attribute_keys' ) ) {
	/**
	 * Returns (data) attributes for fields
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_field_attribute_keys( $wpcb_field, $allowed_keys = array() ) {
		$attribute_keys = [];

		if ( count( $allowed_keys ) === 0 ) {
			$allowed_keys = array( 'class', 'placeholder', 'value', 'min', 'max', 'min_height', 'max_height', 'step', 'rows', 'cols', 'accept' );
		}

		if ( isset( $wpcb_field['field'] ) && is_array( $wpcb_field['field'] ) && count( $wpcb_field['field'] ) > 0 ) {
			foreach ( $wpcb_field['field'] as $fieldkey => $fieldval ) {
				if ( '' === $fieldval ) {
					continue;
				}

				if ( ! in_array( $fieldkey, $allowed_keys ) ) {
					continue;
				}

				if ( isset( $wpcb_field['field']['type'] ) ) {
					if ( in_array( $wpcb_field['field']['type'], array( 'text-input', 'textarea' ) ) ) {
						if ( 'min' === $fieldkey ) {
							$fieldkey = 'minlength';
						}
						if ( 'max' === $fieldkey ) {
							$fieldkey = 'maxlength';
						}
					}
					
					if ( in_array( $wpcb_field['field']['type'], array( 'datepicker', 'file-upload' ) ) ) {
						if ( 'min' === $fieldkey ) {
							$fieldkey = 'data-min';
						}
						if ( 'max' === $fieldkey ) {
							$fieldkey = 'data-max';
						}
						if ( 'min_height' === $fieldkey ) {
							$fieldkey = 'data-min-height';
						}
						if ( 'max_height' === $fieldkey ) {
							$fieldkey = 'data-max-height';
						}
					}
				}
		
				$attribute_keys[] = [
					'key' => $fieldkey,
					'value' => $fieldval
				];
			}
		}
		
		if ( isset( $wpcb_field['field']['value'] ) && '' !== $wpcb_field['field']['value'] ) {
			$attribute_keys[] = [
				'key' => 'data-value',
				'value' => $wpcb_field['field']['value']
			];
		}

		if ( isset( $wpcb_field['field']['required'] ) && 'yes' === $wpcb_field['field']['required'] ) {
			$attribute_keys[] = [
				'key' => 'required',
				'value' => 'required'
			];
		}

		$data_attributes = array( 'key', 'price', 'sale', 'price_type', 'price_unit', 'price_unit_sale', 'price_table', 'date_format', 'date_inline', 'days_blocked', 'date_invalid', 'range_value', 'max_size', 'file_types', 'file', 'is_dim' );
		foreach ( $data_attributes as $data_attribute ) {
			if ( isset( $wpcb_field['field'][ $data_attribute ] ) && '' !== $wpcb_field['field'][ $data_attribute ] ) {
				$attribute_keys[] = [
					'key' => 'data-' . str_replace( '_', '-', $data_attribute ),
					'value' => $wpcb_field['field'][ $data_attribute ]
				];
			}
		}

		if ( isset( $wpcb_field['visual'] ) && is_array( $wpcb_field['visual'] ) && count( $wpcb_field['visual'] ) > 0 ) {
			foreach ( $wpcb_field['visual'] as $visualkey => $visualval ) {
				if ( '' === $visualval ) {
					continue;
				}

				$attribute_keys[] = [
					'key' => 'data-' . str_replace( '_', '-', $visualkey ),
					'value' => $visualval
				];
			}
		}

		return apply_filters( 'wpcb_get_configurator_field_attribute_keys', $attribute_keys, $wpcb_field );
	}
}

if ( ! function_exists( 'wpcb_display_field_attributes' ) ) {
	/**
	 * Returns (data) attributes for fields
	 *
	 * @since    1.0.0
	 */
	function wpcb_display_field_attributes( $wpcb_field, $allowed_keys = array() ) {
		$field_keys = wpcb_get_field_attribute_keys( $wpcb_field, $allowed_keys );

		foreach ( $field_keys as $key_pair ) {
			echo esc_attr( $key_pair['key'] ) . '="' . esc_attr( $key_pair['value'] ) . '"';
		}
	}
}

if ( ! function_exists( 'wpcb_get_product_attributes' ) ) {
	/**
	 * Returns product details based on configuration or product page
	 *
	 * @param    int   $wpcb_configurator_id
	 * @since    1.0.0
	 */
	function wpcb_get_product_attributes( $wpcb_configuration_id, $cart_product_id = false ) {
		global $wpcb_price_rates;
		if ( ! $wpcb_price_rates ) {
			wpcb_define_price_rates();
		}

		$wpcb_product_id = 0;
		$tax_percentage = 0;
		$base_weight = 0;
		$stock_qty = 999999;
		$backorder = false;
		$product_sku = '';
		$product_name = get_the_title( $wpcb_configuration_id );
		$price = get_post_meta( $wpcb_configuration_id, '_price', true  ) ?: 0;
		$sale_price = get_post_meta( $wpcb_configuration_id, '_sale_price', true ) ?: false;

		if ( function_exists( 'is_product' ) ) {
			global $product;

			if ( is_product() ) {
				$wpcb_product_id = get_the_ID();
				$product_name = get_the_title( $wpcb_product_id );

				if ( ! is_object( $product ) ) {
					$product = wc_get_product( $wpcb_product_id );
				}
			}

			if ( $product ) {
				$base_weight = $product->get_weight() ?: 0;
				$product_sku = $product->get_sku();

				if ( $product->is_on_sale() ) {
					$sale_price = $product->get_price();
					$price = $product->get_regular_price();
				} else {
					$price = $product->get_price();
					$sale_price = 0;
				}

				if ( $product->get_manage_stock() ) {
					$stock_qty = $product->get_stock_quantity();
				} else if ( ! $product->is_in_stock() ) {
					$stock_qty = 0;
				}
				$backorder = $product->is_on_backorder();

				if ( 'yes' === get_option( 'woocommerce_calc_taxes' ) ) {
					// Take only the item rate and round it.
					$tax_rates = WC_Tax::get_rates_for_tax_class( $product->get_tax_class() );
		
					if ( is_array( $tax_rates ) && count( $tax_rates ) > 0 ) {
						$tax = reset( $tax_rates );
						$tax_percentage = $tax->tax_rate;
					}
				}
			}
			else if ( $wpcb_product_id = get_post_meta( $wpcb_configuration_id, '_woo_product_id', true ) ) {
				$product = wc_get_product( $wpcb_product_id );
			}
		}

		if ( $cart_product_id ) {
			// Cart override for correct calculations.
			$product = wc_get_product( $cart_product_id );
			if ( $product->is_on_sale() ) {
				$sale_price = $product->get_price();
				$price = $product->get_regular_price();
			} else {
				$price = $product->get_price();
				$sale_price = 0;
			}
		}

		$include_tax = $wpcb_price_rates['include_tax'];
		$tax_display = $wpcb_price_rates['tax_display'];
		if ( 'excl' === $tax_display && 'yes' == $include_tax ) {
			$price = wpcb_get_price_excluding_tax( $price, $tax_percentage );
			if ( $sale_price ) {
				$sale_price = wpcb_get_price_excluding_tax( $sale_price, $tax_percentage );
			}
		} else if ( 'incl' === $tax_display && 'yes' !== $include_tax ) {
			$price = wpcb_get_price_including_tax( $price, $tax_percentage );
			if ( $sale_price ) {
				$sale_price = wpcb_get_price_including_tax( $sale_price, $tax_percentage );
			}
		}

		$currencies = $wpcb_price_rates['currencies'];
		if ( is_array( $currencies ) && count( $currencies ) > 0 ) {
			$price = wpcb_get_converted_currency_price( $price, $currencies );
			if ( $sale_price ) {
				$sale_price = wpcb_get_converted_currency_price( $sale_price, $currencies );
			}
		}

		$sku_format = false;
		if ( get_post_meta( $wpcb_configuration_id, '_configuration_sku_format', true ) ) {
			$sku_format = sanitize_text_field( get_post_meta( $wpcb_configuration_id, '_configuration_sku_format', true ) );
		}

		return array(
			'id' => $wpcb_product_id,
			'title' => $product_name,
			'price' => $price,
			'sale' => $sale_price,
			'tax' => $tax_percentage,
			'sku' => $product_sku,
			'stock' => $stock_qty,
			'backorder' => $backorder,
			'sku_format' => $sku_format,
			'weight' => $base_weight
		);
	}
}

if ( ! function_exists( 'wpcb_get_product_vars' ) ) {
	/**
	 * Returns product vars based on configuration id
	 *
	 * @param    int   $wpcb_configurator_id
	 * @since    1.0.0
	 */
	function wpcb_get_product_vars( $wpcb_configuration_id ) {
		$quantity_input = '.wpcb-configurator-form-add-to-cart input.qty';

		if ( $input_id = get_post_meta( $wpcb_configuration_id, '_quantity_input', true ) ) {
			$input_name = wpcb_sanitize_title( get_post_meta( $input_id, '_label', true ) );

			if ( $input_name ) {
				if ( 'input' === get_post_type( $input_id ) ) {
					$quantity_input = '.wpcb-configurator-form [name="' . $input_name . '"]';
				} else {
					$quantity_input = '.wpcb-configurator-form [name="' . $input_name . '"]:checked';
				}
			}
		}

		$cart_product_id = false;
		if ( function_exists( 'is_product' ) && ! is_product() ) {
			$cart_product_id = get_post_meta( $wpcb_configuration_id, '_woo_product_id', true );
		}

		$details = wpcb_get_product_attributes( $wpcb_configuration_id, $cart_product_id );
		$price = $details['price'] ?: '';
		$sale_price = $details['sale'] ?: false;

		$wpcb_details = array(
			'id' => (int) $details['id'],
			'name' => $details['title'],
			'price' => floatval( $price ),
			'sale_price' => $sale_price ? floatval( $sale_price ) : '',
			'base_price' => $sale_price && $sale_price < $price ? floatval( $sale_price ) : floatval( $price ),
			'tax_percent' => floatval( $details['tax'] ),
			'weight' => floatval( $details['weight'] ),
			'stock' => floatval( $details['stock'] ),
			'backorder' => $details['backorder'],
			'quantity_name' => $quantity_input,
			// 'show_final_step' => ( 'final' === get_post_meta( $wpcb_configuration_id, '_step_action', true ) ? true : false ),
			// 'image_stack' => ( 'stacked' === esc_attr( get_post_meta( $wpcb_configuration_id, '_gallery_type', true ) ) ? true : false ),
		);

		// if ( 'model' === wpcb_get_option( 'wpcb_configuration_visual_type' ) ) {
		// 	$base_nodes = get_post_meta( $wpcb_configuration_id, '_wpcb_model_base_nodes', true ) ?: false;
		// 	$base_hotspots = get_post_meta( $wpcb_configuration_id, '_wpcb_model_base_hotspots', true ) ?: false;
		// 	$all_hotspots = get_post_meta( $wpcb_configuration_id, '_wpcb_model_hotspots', true ) ?: array();

		// 	$hotspots = array();
		// 	if ( $all_hotspots ) {
		// 		$label = wpcb_sanitize_title( get_the_title( $wpcb_configuration_id ) );
		// 		foreach ( $all_hotspots as $index => $hotspot ) {
		// 			$context = $label . '_model_hotspot_' . $index;
		// 			$hotspots[] = [
		// 				'id' => wpcb_get_value( $hotspot['hotspot_id'] ),
		// 				'content' => wpcb__( trim( wpcb_get_value( $hotspot['hotspot_text'] ) ), $context ),
		// 				'position' => wpcb_get_value( $hotspot['hotspot_target'] ),
		// 				'normal' => wpcb_get_value( $hotspot['hotspot_normal'] ),
		// 			];
		// 		}
		// 	}
		// 	$wpcb_details['hotspots'] = $hotspots;

		// 	if ( $base_nodes ) {
		// 		$base_nodes = array_map( 'trim', explode( ',', $base_nodes ) );
		// 	} else {
		// 		$base_nodes = array();
		// 	}
		// 	$wpcb_details['base_nodes'] = $base_nodes;

		// 	if ( $base_hotspots ) {
		// 		$base_hotspots = array_map( 'trim', explode( ',', $base_hotspots ) );
		// 	} else {
		// 		$base_hotspots = array();
		// 	}
		// 	$wpcb_details['base_hotspots'] = $base_hotspots;
		// }

		return $wpcb_details;
	}
}

if ( ! function_exists( 'wpcb_get_field_attributes' ) ) {
	/**
	 * Returns (data) attributes for fields
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_field_attributes( $wpcb_field, $allowed_keys = array() ) {
		$field_attributes = '';
		$field_keys = wpcb_get_field_attribute_keys( $wpcb_field, $allowed_keys );

		foreach ( $field_keys as $key_pair ) {
			$field_attributes .= $key_pair['key'] . '="' . $key_pair['value'] . '"';
		}

		return $field_attributes;
	}
}

if ( ! function_exists( 'wpcb_get_choice_option_data' ) ) {
	/**
	 * Returns an array of registered choice visual meta keys
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_choice_option_data( $option_type, $option = array(), $context = '' ) {
		if ( ! isset( $option['label'] ) ) {
			return array();
		}

		$option_data = array(
			'label' => wpcb__( wpcb_get_value( $option['label'] ), $context . '_label' ),
			'color' => '',
			'note' => '',
			'description' => '',
			'gallery' => '',
			'thumbnail' => '',
			'template' => '',
			'preview_value' => '',
			'product_id' => '',
			'weight' => '',
			'key' => '',
			'price' => '',
			'sale_price' => '',
			'formula' => '',
			'rounding' => '',
			'percent' => '',
			'field' => '',
			'table' => '',
			'stock' => '',
			'sku' => '',
		);

		$option_i18n_keys = array(  'note', 'description' );
		$option_data_keys = wpcb_get_choice_option_data_keys();
		foreach ( $option_data_keys as $option_key => $option_field_key ) {
			$option_val = '';

			if ( isset( $option[ $option_field_key ] ) ) {
				if ( 'gallery' == $option_key ) {
					$gallery_ids = array();
					if ( is_array( $option[ $option_field_key ] ) ) {
						foreach ( $option[ $option_field_key ] as $option_item ) {
							$gallery_ids[] = wp_get_attachment_image( $option_item['value'], 'full' );
						}
					}
					$option_val = implode('', $gallery_ids);
				} elseif ( 'template' == $option_key ) {
					$option_val = wpcb_get_value( $option[ $option_field_key ] );
				} else {
					$option_val = wpcb_get_value( $option[ $option_field_key ] );
				}

				if ( in_array( $option_field_key, $option_i18n_keys ) ) {
					$option_val = wpcb__( $option_val, $context . '_' . $option_field_key );
				}
			}

			$option_data[ $option_key ] = $option_val;
		}

		if ( 'font' === $option_type ) {
			if ( isset( $option['label_value'] ) && '' !== wpcb_get_value( $option['label_value'] ) ) {
				$font_properties = wpcb_get_value( $option['label_value'] );
				$formatted_value = implode(',', array_map( 'trim', explode(',', $font_properties)));
				$option_data['preview_value'] = $formatted_value;
			}
		}
		else if ( 'color' === $option_type ) {
			$option_data['color'] = wpcb_get_value( $option['color'] );
			$option_data['preview_value'] = wpcb_get_value( $option['color'] );
		}
		else if ( isset( $option['label_value'] ) && '' !== wpcb_get_value( $option['label_value'] ) ) {
			if ( 'url' === $option_type ) {
				$option_data['url'] = wpcb_get_value( $option['label_value'] );
			} else {
				$option_data['preview_value'] = wpcb_get_value( $option['label_value'] );
			}
		}

		if ( isset( $wpcb_field_meta['_key'][0] ) ) {
			$option_data['key'] = $wpcb_field_meta['_key'][0];
		}

		if ( '' !== $option_data['formula'] && function_exists('wpcb_get_formula_equasion') ) {
			$option_data['formula'] = wpcb_get_formula_equasion( $option_data['formula'] );
			$option_data['rounding'] = wpcb_get_formula_rounding( $option_data['formula'] );
		}

		return $option_data;
	}
}

if ( ! function_exists( 'wpcb_get_configurator_cart_totals' ) ) {
	/**
	 * Validates the posted price to check if there is not tampered with.
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_configurator_cart_totals( $configurator_id, $product_id, $configuration, $total_args, $new_qty = false ) {
		global $wpcb_price_rates;
		if ( ! $wpcb_price_rates ) {
			wpcb_define_price_rates();
		}

		// Recalculate totals to verify that posted totals are matching.
		$total_price = 0;
		$total_sale_price = 0;
		$total_tax = 0;
		$total_weight = 0;

		$field_prices = array();
		$field_values = array();
		$field_lengths = array();

		if ( $configuration && is_array( $configuration ) ) {
			foreach ( $configuration as $group_name => $group_details ) {
				if ( ! isset( $group_details['value'] ) ) {
					continue;
				}

				$group_value = $group_details['value'];

				if ( isset( $group_details['weight']) ) {
					$total_weight += floatval( $group_details['weight'] );
				}

				if ( ! isset( $group_details['values'] ) || ! is_array( $group_details['values'] ) || count( $group_details['values'] ) == 0 ) {
					continue;
				}
				if ( ! isset( $group_details['cost'] ) || 0 == $group_details['cost'] ) {
					continue;
				}

				if ( isset( $group_details['tax'] ) && $group_details['tax'] ) {
					$total_tax += floatval( $group_details['tax'] );
				}

				if ( $group_value || $group_details['values'] ) {
					$total_price += floatval( $group_details['cost'] );

					if ( isset( $group_details['sale_cost'] ) ) {
						$total_sale_price += floatval( $group_details['sale_cost'] );
					} else {
						$total_sale_price += floatval( $group_details['cost'] );
					}

					if ( isset( $group_details['sale_cost'] ) ) {	
						$field_prices[ $group_details['id'] ] = $group_details['sale_cost'];
					} else if ( isset( $field_details['cost'] ) ) {
						$field_prices[ $group_details['id'] ] = $group_details['cost'];
					}

					if ( is_array( $group_details['values'] ) && count( $group_details['values'] ) > 0 ) {
						$field_lengths[ $group_details['id'] ] = count( $group_details['values'] );

						foreach ( $group_details['values'] as $field_value ) {
							if ( isset( $field_value['type'] ) && 'input' === $field_value['type'] ) {
								if ( isset( $field_value['price'] ) ) {
									$field_prices[ $field_value['id'] ] = $field_value['price'];
								}
								if ( isset( $field_value['value'] ) ) {
									$field_values[ $field_value['id'] ] = $field_value['value'];
								}
							}
						}
					}
				}
			}
		}

		$product_details = wpcb_get_product_attributes( $configurator_id, $product_id );

		$price = $product_details['price'];
		$sale_price = $product_details['sale'];
		$product_base_total = 0;
		$use_product_price = wpcb_get_option( 'wpcb_exclude_product_base_prices' ) ? false : true;
		if ( $use_product_price ) {
			$product_base_total = $sale_price ? $sale_price : $price;
		}

		if ( is_numeric( $new_qty ) ) {
			// Update from cart line item.
			$total_args['table_qty'] = $new_qty;
		}

		if ( isset( $total_args['price_type'] ) && 'table' === $total_args['price_type'] && function_exists( 'wpcb_get_price_from_range_table' ) ) {
			// recalculate product table base price
			$table_qty = isset( $total_args['table_qty'] ) ? $total_args['table_qty'] : 1;
			$price_table = wpcb_get_price_from_range_table( $total_args['price_table_id'], $table_qty );
			$price = wpcb_get_field_price_details( array( 'price' => $price_table ), $total_args['product_tax_rate'] );

			$product_base_total = $price['price'];
			$total_args['product_price'] = $price['price']; 
		}

		$product_option_total = $total_sale_price ? $total_sale_price : $total_price;
		$product_grand_total  = ( $product_base_total + $total_price );
		$product_grand_sale   = ( $product_base_total + $total_sale_price );

		if ( $total_args['product_tax_rate'] > 0 ) {
			if ( 'incl' === $wpcb_price_rates['tax_display'] ) {
				$product_base_tax = $product_base_total / ( 1 + ($total_args['product_tax_rate'] / 100) );
			} else {
				$product_base_tax = $product_base_total * ( 1 + ($total_args['product_tax_rate'] / 100) );
			}
			
			$total_tax += abs( $product_base_tax - $product_base_total );
		}

		if ( isset( $totals['price_formula'] ) && '' !== $totals['price_formula'] ) {
			// recalculate formula price
			$formula = wpcb_get_formula_equasion( $totals['price_id'] );

			foreach ( $field_values as $field_key => $field_value ) {
				$formula = str_replace( '${' . $field_key . ':value}', $field_value, $formula);
			}
			foreach ( $field_lengths as $field_key => $field_length ) {
				$formula = str_replace( '${' . $field_key . ':length}', $field_length, $formula);
			}
			foreach ( $field_prices as $field_key => $field_cost ) {
				$formula = str_replace( '${' . $field_key . '}', $field_cost, $formula);
			}

			$formula = str_replace('${Product price}', $total_args['product_price'], $formula);
			$formula = str_replace('${Total price}', $total_args['total_price'], $formula);
			$formula = str_replace('${Total weight}', $total_args['total_weight'], $formula);

			if ( ! str_contains( $formula, '${' ) ) {
				$price = eval( 'return(' . $formula . ');' );

				$product_grand_total = ($product_base_total + $price);
				$product_grand_sale = ($product_base_total + $price);
				$product_option_total = $price;

				if ( $total_args['product_tax_rate'] > 0 ) {
					// Recalculate taxes based on total formula value
					if ( 'incl' === $wpcb_price_rates['tax_display'] ) {
						$product_grand_tax = $product_grand_sale / ( 1 + ($total_args['product_tax_rate'] / 100) );
					} else {
						$product_grand_tax = $product_grand_sale * ( 1 + ($total_args['product_tax_rate'] / 100) );
					}
					$total_tax = abs( $product_grand_tax - $product_grand_sale );
				}
			}
		}

		if ( $total_tax > 0 ) {
			$total_args['total_tax'] = $total_tax;
		}

		// $product_total_weight = $total_weight;
		// if ( $product_details['weight'] > 0 ) {
		// 	$product_total_weight += floatval($product_details['weight']);
		// }

		$grand_total = $product_grand_total;
		$grand_total_sale = $product_grand_sale;
		if ( 'incl' === $wpcb_price_rates['tax_display'] ) {
			$subtotal = $product_grand_total - $total_tax;
			$subtotal_sale = $product_grand_sale - $total_tax;
		} else {
			$subtotal = $product_grand_total + $total_tax;
			$subtotal_sale = $product_grand_sale + $total_tax;
		}

		// Revert values to base quantity prices.
		$total_args['option_price'] = $product_option_total;
		$total_args['subtotal_price'] = $subtotal;
		$total_args['subtotal_sale'] = $subtotal_sale;
		$total_args['sale_price'] = $grand_total_sale;
		$total_args['total_price'] = $grand_total;

		return $total_args;
	}
}

if ( ! function_exists( 'wpcb_get_price_html' ) ) {
	/**
	 * Returns html for given price
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_price_html( $price, $sale = '', $base_price = false ) {
		global $wpcb_price_settings;
		if ( ! $wpcb_price_settings ) {
			wpcb_define_price_settings();
		}

		$price_html = '';
		$sign = '';
		if ( isset( $wpcb_price_settings['wpcb_extra_price_plus_prefix'] ) && $wpcb_price_settings['wpcb_extra_price_plus_prefix'] ) {
			$sign = '<span class="wpcb-plus-sign">+</span>';
		}
		if ( $base_price ) {
			$sign = '';
		}

		$text = '';
		if ( isset( $wpcb_price_settings['wpcb_free_price_label'] ) && $wpcb_price_settings['wpcb_free_price_label'] ) {
			$text = $wpcb_price_settings['wpcb_free_price_label'];
		}

		if ( $sale && '' !== $sale ) {
			$price_html = $sign . '<del>' . wpcb_get_formatted_price( $price ) . '</del><ins>' . wpcb_get_formatted_price( $sale ) . '</ins>';
		} else if ( '' !== $price ) {
			$price_html = $sign . wpcb_get_formatted_price( $price );
		} else if ( '' !== $text ) {
			$price_html = $text; // Included.
		}

		return apply_filters( 'wpcb_get_configurator_price_html', $price_html, $price, $sale, $sign );
	}
}

if ( ! function_exists( 'wpcb_get_formatted_price' ) ) {
	/**
	 * Get formatted price value
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_formatted_price( $price ) {
		global $wpcb_price_settings;
		if ( ! $wpcb_price_settings ) {
			wpcb_define_price_settings();
		}

		$currency_symbol = $wpcb_price_settings['wpcb_price_currency_symbol'] ?: '';
		$currency_pos    = $wpcb_price_settings['wpcb_price_currency_pos'] ?: 'left_space';
		$thousand_sep    = $wpcb_price_settings['wpcb_price_thousand_sep'] ?: ',';
		$decimal_sep     = $wpcb_price_settings['wpcb_price_decimal_sep'] ?: '.';
		$decimal_num     = $wpcb_price_settings['wpcb_price_decimal_number'] ?: 2;

		// Convert to float to avoid issues on PHP 8.
		$price    = (float) $price;
		$negative = $price < 0;
		$price    = number_format( $price, $decimal_num, $decimal_sep, $thousand_sep );
	
		if ( '' !== $currency_symbol ) {
			if ( 'left_space' == $currency_pos ) {
				$currency_symbol .= ' ';
			}
			if ( 'right_space' == $currency_pos ) {
				$currency_symbol = ' ' . $currency_symbol;
			}
		}

		if ( 'left' == $currency_pos || 'left_space' == $currency_pos ) {
			$formatted_price = ( $negative ? '-' : '' ) . $currency_symbol . $price;
		} else {
			$formatted_price = ( $negative ? '-' : '' ) . $price . $currency_symbol;
		}

		return apply_filters( 'wpcb_get_formatted_configurator_price', $formatted_price, $price, $wpcb_price_settings );
	}
}

if ( ! function_exists( 'wpcb_get_configuration_price_min' ) ) {
	/**
	 * Dynamically calculate minimum configuration price based for configuration id
	 */
	function wpcb_get_configuration_price_min( $configuration_id ) {
		$min_price = 0;
		$max_price = 0;

		$selections = WPCB_Formatter::get_configurator_form_fields( $configuration_id );

		foreach ( $selections as $selection ) {
			if ( in_array( $selection['template'], array( 'html', 'shortcode', 'summary' ) ) ) {
				continue;
			}

			if ( in_array( $selection['template'], array( 'accordeon', 'tab', 'step', 'field-group', 'field-filter', 'repeater' ) ) ) {
				// Check sub field inputs
				if ( ! isset( $selection['subfields'] ) || ! is_array( $selection['subfields'] ) ) {
					continue;
				}

				foreach ( $selection['subfields'] as $wpcb_field ) {
					$min_price += wpcb_get_field_price_min( $wpcb_field );
					$max_price += wpcb_get_field_price_max( $wpcb_field );
				}
			} else {
				$min_price += wpcb_get_field_price_min( $selection );
				$max_price += wpcb_get_field_price_max( $selection );
			}
		}

		return $min_price;
	}
}

if ( ! function_exists( 'wpcb_get_configuration_price_max' ) ) {
	/**
	 * Dynamically calculate maximum configuration price based for configuration id
	 */
	function wpcb_get_configuration_price_max( $configuration_id ) {
		$max_price = 0;
		$selections = WPCB_Formatter::get_configurator_form_fields( $configuration_id );

		foreach ( $selections as $selection ) {
			if ( in_array( $selection['template'], array( 'html', 'shortcode', 'summary' ) ) ) {
				continue;
			}

			if ( in_array( $selection['template'], array( 'accordeon', 'tab', 'step', 'field-group', 'field-filter', 'repeater' ) ) ) {
				// Check sub field inputs
				if ( ! isset( $selection['subfields'] ) || ! is_array( $selection['subfields'] ) ) {
					continue;
				}

				foreach ( $selection['subfields'] as $wpcb_field ) {
					$max_price += wpcb_get_field_price_max( $wpcb_field );
				}
			} else {
				$max_price += wpcb_get_field_price_max( $selection );
			}
		}

		return $max_price;
	}
}

if ( ! function_exists( 'wpcb_get_field_price_min' ) ) {
	/**
	 * Dynamically calculate minimum field price
	 */
	function wpcb_get_field_price_min( $field ) {
		$min_price = 0;
	
		if ( isset( $selection['field']['required'] ) && 'no' === $selection['field']['required'] ) {
			return $min_price;
		}

		if ( $field['template'] === 'choice' ) {
			if ( ! isset( $field['options'] ) || count( $field['options'] ) === 0 ) {
				return $min_price;
			}

			$option_min_price = 99999;
			foreach ( $field['options'] as $option ) {
				if ( $option['sale_price'] && floatval( $option['sale_price'] ) < $option_min_price ) {
					$option_min_price = floatval( $option['sale_price'] );
				} else if ( $option['price'] && floatval( $option['price'] ) < $option_min_price ) {
					$option_min_price = floatval( $option['price'] );
				}
			}

			if ( $option_min_price != 99999 ) {
				$min_price = $option_min_price;
			}
		} else {
			if ( $field['field']['price_type'] === 'fixed' ) {
				if ( $field['field']['sale'] ) {
					$min_price = $field['field']['sale'];
				} else {
					$min_price = $field['field']['price'];
				}
			}
			else if ( $field['field']['price_type'] === 'price_unit' && isset( $field['field']['min'] ) && floatval( $field['field']['min'] ) > 0 ) {
				if ( isset( $field['field']['price_unit_sale'] ) && $field['field']['price_unit_sale'] ) {
					$min_price = $field['field']['min'] * $field['field']['price_unit_sale'];
				} else {
					$min_price = $field['field']['min'] * $field['field']['price_unit'];
				}
			}
		}

		return $min_price;
	}
}

if ( ! function_exists( 'wpcb_get_field_price_max' ) ) {
	/**
	 * Dynamically calculate maximum field price
	 */
	function wpcb_get_field_price_max( $field ) {
		$max_price = 0;

		if ( $field['template'] === 'choice' ) {
			if ( ! isset( $field['options'] ) || count( $field['options'] ) === 0 ) {
				return $max_price;
			}

			$option_max_price = 0;
			foreach ( $field['options'] as $option ) {
				if ( $option['price'] && floatval( $option['price'] ) > $option_max_price ) {
					$option_max_price = floatval( $option['price'] );
				}
			}

			$max_price = $option_max_price;
		} else {
			if ( $field['field']['price_type'] === 'fixed' ) {
				$max_price = $field['field']['price'];
			}

			if ( $field['field']['price_type'] === 'price_unit' && isset( $field['field']['max'] ) && floatval( $field['field']['max'] ) > 0 ) {
				$max_price += $field['field']['max'] * $field['field']['price_unit'];
			}
		}

		return $max_price;
	}
}

if ( ! function_exists( 'wpcb_get_price_including_tax' ) ) {
	/**
	 * Returns price including given tax rate
	 *
	 * @param    float  $price
	 * @param    array $tax_rates
	 * @since    1.0.0
	 */
	function wpcb_get_price_including_tax( $price, $tax_rate, $ignore = false ) {
		if ( ! wc_prices_include_tax() || $ignore ) {
			$return_price = $price * (1 + ($tax_rate / 100));
		} else {
			$return_price = $price;
		}
		return $return_price;
	}
}

if ( ! function_exists( 'wpcb_get_price_excluding_tax' ) ) {
	/**
	 * Returns price including given tax rate
	 *
	 * @param    float  $price
	 * @param    array $tax_rates
	 * @since    1.0.0
	 */
	function wpcb_get_price_excluding_tax( $price, $tax_rate, $ignore = false ) {
		if ( wc_prices_include_tax() || $ignore ) {
			$return_price = ($price / (100 + $tax_rate)) * 100;
		} else {
			$return_price = $price;
		}
		return $return_price;
	}
}

if ( ! function_exists( 'wpcb_round_price_to' ) ) {
	/**
	 * Round price value to arbitrairy float.
	 *
	 * @param    float $value Value to round.
	 * @param    int   $to    Float value to round up to.
	 * @since    1.0.0
	 */
	function wpcb_round_price_to($value, $to) {
		return round( $value / $to, 0 ) * $to;
	}
}

if ( ! function_exists( 'wpcb_round_price_up' ) ) {
	/**
	 * Round price value up.
	 *
	 * @param    float $value Value to round.
	 * @param    int   $places Places to round up.
	 * @since    1.0.0
	 */
	function wpcb_round_price_up($value, $places) {
		$mult = pow(10, abs($places)); 
		return $places < 0 ? ceil($value / $mult) * $mult : ceil($value * $mult) / $mult;
	}
}

if ( ! function_exists( 'wpcb_get_field_price_details' ) ) {
	/**
	 * Check and update price tax and currency display
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_field_price_details( $field, $tax_rate = 0, $price_key = 'price' ) {
		global $wpcb_price_rates;
		if ( ! $wpcb_price_rates ) {
			wpcb_define_price_rates();
		}

		if ( ! isset( $field[ $price_key ] ) ) {
			return $field;
		}

		if ( 0 == $tax_rate ) {
			return $field;
		}

		if ( 'no' === $wpcb_price_rates['include_tax'] && 'incl' === $wpcb_price_rates['tax_display'] ){ 
			$field[ $price_key ] = wpcb_get_price_including_tax( $field[ $price_key ], $tax_rate );
		} else if ( 'yes' === $wpcb_price_rates['include_tax'] && 'excl' === $wpcb_price_rates['tax_display'] ) {
			$field[ $price_key ] = wpcb_get_price_excluding_tax( $field[ $price_key ], $tax_rate );
		} else {
			$field[ $price_key ] = floatval( $field[ $price_key ] );
		}

		if ( ! is_array( $wpcb_price_rates['currencies'] ) || count( $wpcb_price_rates['currencies'] ) == 0 ) {
			return $field;
		}

		if ( ! function_exists( 'get_woocommerce_currency' ) ) {
			return $field;
		}

		$field[ $price_key ] = wpcb_get_converted_currency_price( $field[ $price_key ], $wpcb_price_rates['currencies'] );

		return $field;
	}
}

if ( ! function_exists( 'wpcb_get_converted_currency_price' ) ) {
	/**
	 * Returns an converted price for active currency
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_converted_currency_price( $price, $currencies = array() ) {
		if ( ! function_exists( 'get_woocommerce_currency' ) ) {
			return $price;
		}
		if ( ! is_array( $currencies ) ) {
			return $price;
		}

		$exchange_rate = '';
		$exchange_rounding = '';
		foreach ( $currencies as $currency ) {
			if ( $currency['currency_id'] == get_woocommerce_currency()) {
				$exchange_rate = $currency['exchange_rate'];
				$exchange_rounding = $currency['exchange_rounding'];
				break; // found. we can stop now.
			}
		}

		if ( $exchange_rate && $exchange_rounding ) {
			if ( in_array( $exchange_rounding, array( '0.25', '0.50' ) ) ) {
				$price = wpcb_round_price_to( ( $price * $exchange_rate ), $exchange_rounding );
			} else {
				$price = wpcb_round_price_up( ( $price * $exchange_rate ), $exchange_rounding );
			}
		}

		return $price;
	}
}

if ( ! function_exists( 'wpcb_trim_value' ) ) {
	/**
	 * Returns a trimmed value.
	 *
	 * @param    string $value Value to trim.
	 * @since    1.0.0
	 */
	function wpcb_trim_value( $value ) {
		if ( is_array( $value ) ) {
			foreach ( $value as $k => $v ) {
				if ( is_array( $v ) ) {
					array_filter( $v, 'wpcb_trim_value' );
				} else {
					$value[$k] = trim( $v );
				}
			}
		} else {
			$value = trim( $value );
		}
	}
}

if ( ! function_exists( 'wpcb_generate_configuration_name' ) ) {
	/**
	 * Generate new configuration file name.
	 *
	 * @since    1.0.0
	 */
	function wpcb_generate_configuration_name() {
		return substr(md5(openssl_random_pseudo_bytes(20)), -32);
	}
}

if ( ! function_exists( 'wpcb_store_configuration_image' ) ) {
	/**
	 * Render final product image
	 * 
	 * @since 1.3.1
	 */
	function wpcb_store_configuration_image( $imagename, $data, $temp = false ) {
		/**
		 * Validate and format image type
		 */
		if ( preg_match( '/^data:image\/(.*);base64,/', $data, $type ) ) {
			// Valid base64 image.
			$type = strtolower( $type[1] ); // jpg, png, gif

			$data = substr( $data, strpos($data, ',') + 1 );
			$data = str_replace( ' ', '+', $data );
			$data = base64_decode($data);

			if ( $data === false ) {
				throw new \Exception('Image decode failed. Try again.');
			}
		} else {
			// Can't validate base64 string somehow? Try conversion with png extension.
			$type = 'png';

			$data = substr( $data, strpos($data, ',') + 1 );
			$data = str_replace( ' ', '+', $data );
			$data = base64_decode($data);

			if ( $data === false ) {
				throw new \Exception('Invalid image data provided. Try again.');
			}
		}

		/**
		 * Validate type
		 */
		if ( ! in_array( $type, array( 'png', 'jpg', 'jpeg', 'gif', 'webp', 'tiff', 'avif') ) ) {
			return;
		}

		/**
		 * Store image
		 */
		$save_path = wpcb_get_configuration_save_dir( $temp );
		$filename  = $imagename . '.' . $type;

		if ( ! file_exists( trailingslashit( $save_path['path'] ) . $filename ) ) {
			file_put_contents( trailingslashit( $save_path['path'] ) . $filename , $data );
		}

		$image_path_url = trailingslashit( $save_path['url'] ) . $filename;
		return $image_path_url;
	}
}

if ( ! function_exists( 'wpcb_get_configuration_save_dir' ) ) {
	/**
	 * Get configuration file save directory path.
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_configuration_save_dir( $temp = false ) {
		$upload_dir = wp_get_upload_dir();

		if ( $temp ) {
			$details = array(
				'path' => $upload_dir['basedir'] . WPCB_UPLOAD_TEMP_DIR,
				'url'  => $upload_dir['baseurl'] . WPCB_UPLOAD_TEMP_DIR,
			);

			return apply_filters( 'wpcb_get_plugin_configuration_temp_save_dir', $details );
		}
	
		$details = array(
			'path' => $upload_dir['basedir'] . WPCB_UPLOAD_DIR,
			'url'  => $upload_dir['baseurl'] . WPCB_UPLOAD_DIR,
		);

		return apply_filters( 'wpcb_get_plugin_configuration_save_dir', $details );
	}
}

if ( ! function_exists( 'wpcb_get_configuration_temp_dir_path' ) ) {
	/**
	 * Get configuration file temp save directory path.
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_configuration_temp_dir_path() {
		$upload_dir = wp_get_upload_dir();
		$base_dir   = $upload_dir['basedir'];

		return apply_filters( 'wpcb_get_plugin_configuration_temp_dir_path', $base_dir . WPCB_UPLOAD_TEMP_DIR );
	}
}

if ( ! function_exists( 'wpcb_get_order_item_file_urls' ) ) {
	/**
	 * Retrieve all uploaded configurator file link urls from order item
	 */
	function wpcb_get_order_item_file_urls( $order_item ) {
		$file_links = [];
		if ( ! $order_item || ! is_object( $order_item ) ) {
			return $file_links;
		}

		$meta = $order_item->get_meta( '_wpcb_cart_meta' );
		if ( ! $meta || ! isset( $meta['wpcb_product_selections'] ) || ! is_array( $meta['wpcb_product_selections'] ) ) {
			return $file_links;
		}

		foreach ( $meta['wpcb_product_selections'] as $field_key => $field ) {
			if ( ! isset( $field['type'] ) ) {
				continue;
			}
			if ( $field['type'] != 'field' ) {
				continue;
			}

			foreach ( $field['values'] as $field_value ) {
				if ( ! isset( $field_value['input_type'] ) ) {
					continue;
				}
				if ( $field_value['input_type'] !== 'file' ) {
					continue;
				}

				$url = $field_value['value'];
				if ( str_contains( $field_value['value'], ';base64,' ) ) {
					$url = wpcb_store_configuration_image( $field_value['label'], $field_value['value'] );
				}

				$file_links[] = [
					'name' => $field_value['label'],
					'url' => $url
				];
			}
		}

		return $file_links;
	}
}

if ( ! function_exists( 'wpcb_get_field_id_by_label' ) ) {
    /**
     * Find WCB field post ID by label
     * 
     */
    function wpcb_get_field_id_by_label($label) {
        global $wpdb;

        $post_data = $wpdb->get_results(
            "SELECT ID, pm.meta_value FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id 
            WHERE pm.meta_key = '_label' AND post_status = 'publish' AND ( post_type = 'wpcb_choice' OR post_type = 'wpcb_input' )",
            ARRAY_A
        );
        
        $post_id = '';
        $post_results = array_values( array_filter( $post_data, fn($item) => strtolower( $item['meta_value'] ) == strtolower( $label ) ) );
        if ( isset( $post_results[0]['ID'] ) ) {
            $post_id = $post_results[0]['ID'];
        }

        return $post_id;
    }
}

if ( ! function_exists( 'wpcb_get_field_label_by_id' ) ) {
    /**
     * Find WCB field label by post ID
     * 
     */
    function wpcb_get_field_label_by_id($id) {
        global $wpdb;

        $post_data = $wpdb->get_results(
            "SELECT ID, pm.meta_value FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id 
            WHERE pm.meta_key = '_label' AND post_status = 'publish' AND ( post_type = 'wpcb_choice' OR post_type = 'wpcb_input' )",
            ARRAY_A
        );
        
        $label = '';
        $post_results = array_values( array_filter( $post_data, fn($item) => $item['ID'] === $id ) );
        if ( isset( $post_results[0]['meta_value'] ) ) {
            $label = $post_results[0]['meta_value'];
        }

        return $label;
    }
}

if ( ! function_exists( 'wpcb_get_field_option_label_by_id' ) ) {
    /**
     * Find WCB choice option ID by label
     * 
     */
    function wpcb_get_field_option_label_by_id($option_id) {
        $option_label = false;
		$field_parts = explode( '-', $option_id );
		$field_id = $field_parts[0];
		$option_key = isset( $field_parts[1] ) ? intval( $field_parts[1] ) : false;

		if ( ! isset( $field_parts[1] ) ) {
			return $option_label;
		}

        $options = maybe_unserialize( get_post_meta( $field_id, '_options', true ) );
        if ( is_array( $options ) ) {
            foreach ( $options as $key => $option ) {
                if ( $key === $option_key ) {
                    $option_label = $option['label'][0]['value'];
                }
            }
        }

        return $option_label;
    }
}

if ( ! function_exists( 'wpcb_get_field_option_by_label' ) ) {
    /**
     * Find WCB choice option ID by label
     * 
     */
    function wpcb_get_field_option_by_label($field_id, $label) {
        $option_id = false;
        $options = maybe_unserialize( get_post_meta( $field_id, '_options', true ) );

        if ( is_array( $options ) ) {
            foreach ( $options as $key => $option ) {
                if ( strtolower( $option['label'][0]['value'] ) == strtolower( $label ) ) {
                    $option_id = $field_id . '-' . $key;
                    break;
                }
            }
        }

        return $option_id;
    }
}

if ( ! function_exists( 'wpcb_get_choice_field_meta_keys' ) ) {
	/**
	 * Returns an array of registered choice field meta keys
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_choice_field_meta_keys() {
		return array(
			'model_type' => '_model_type',
			'label_hidden' => '_label_hidden',
			'price_hidden' => '_price_hidden',
			'placeholder' => '_placeholder',
			'note' => '_note',
			'note_position' => '_note_position',
			'description' => '_description',
			'gallery' => '_gallery',
			'class' => '_class',
			'sku' => '_sku',
			'key' => '_key',
			'show_summary' => '_summary',
			'summary_position' => '_summary_position',
			'show_thumbnail' =>'_show_thumbnail',
			'swatch_inline' => '_swatch_inline_display',
			'multiple_choice' => '_multiple',
			'image_zoom' => '_image_zoom',
			'items_per_row' => '_items_per_row',
			'items_per_row_tablet' => '_items_per_row_tablet',
			'items_per_row_mobile' => '_items_per_row_mobile',
			'product_template' => '_product_template',
			'dropdown_display' => '_dropdown_display',
			'thumbnail_position' => '_thumbnail_position',
			'multiple' => '_multiple',
			'required' => '_required',
			'min' => '_min',
			'max' => '_max',
			'slider' => '_slider',
			'slider_vertical' => '_vertical',
			'slider_vertical_rows' => '_vertical_rows',
			'slider_arrows' => '_arrows',
			'slider_scrollbar' => '_scrollbar',
		);
	}
}

if ( ! function_exists( 'wpcb_get_choice_visual_meta_keys' ) ) {
	/**
	 * Returns an array of registered choice visual meta keys
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_choice_visual_meta_keys() {
		return array(
			'slide_order' => '_order',
			'slide_positions' => '_index',
			'slide_active' => '_slide',
			'reference' => '_reference',
			'property' => '_property',
			'multiplier' => '_multiplier',
			'drag' => '_allow_drag',
			'drag_top' => '_drag_top',
			'drag_left' => '_drag_left',
			'resize' => '_allow_resize',
			'rotate' => '_allow_rotate',
			'thumbnail' => '_use_thumbnail',
			'model_group' => '_model_group',
			'model_type' => '_model_material',
			'model_target' => '_model_target',
			'model_orbit' => '_model_orbit',
			'model_view' => '_model_view',
		);
	}
}

if ( ! function_exists( 'wpcb_get_choice_option_data_keys' ) ) {
	/**
	 * Returns an array of registered choice visual meta keys
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_choice_option_data_keys() {
		return array(
			'label' => 'label',
			'note' => 'note',
			'description' => 'description',
			'gallery' => 'gallery',
			'thumbnail' => 'thumbnail',
			'template' => 'template_id',
			'url' => 'url',
			'preview_value' => 'preview_value',
			'product_id' => 'product_id',
			'sku' => 'sku',
			'color' => 'color',
			'price' => 'price',
			'sale_price' => 'sale_price',
			'formula' => 'formula',
			'price_table' => 'price_table',
			'table_field' => 'price_table_field',
			'percent' => 'percentage_price',
			'field' => 'percentage_field',
			'stock' => 'stock_qty',
			'weight' => 'weight',
			'min' => 'min',
			'max' => 'max',
		);
	}
}

if ( ! function_exists( 'wpcb_get_input_field_meta_keys' ) ) {
	/**
	 * Returns an array of registered input field meta keys
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_input_field_meta_keys() {
		return array(
			'label_hidden' => '_label_hidden',
			'price_hidden' => '_price_hidden',
			'note' => '_note',
			'description' => '_description',
			'gallery' => '_gallery',
			'placeholder' => '_placeholder',
			'value' => '_default_value',
			'checked' => '_default_checked',
			'required' => '_required',
			'class' => '_class',
			'sku' => '_sku',
			'unit' => '_unit',
			'step' =>  '_step_increments',
			'min' => '_min',
			'max' => '_max',
			'cols' => '_cols',
			'rows' => '_rows',
			'max_size' => '_file_max_size',
			'accept' => '_file_allowed_types',
			'file' => '_default_file',
			'min_height' => '_min_height',
			'max_height' => '_max_height',
			'date_format' => '_datepicker_format',
			'days_blocked' => '_limit_weekdays',
			'date_invalid' => '_limit_dates',
			'range_value' => '_range_bubble',
			'price_type' => '_price_type',
			'price' => '_price',
			'sale' => '_sale_price',
			'price_unit' => '_unit_price',
			'price_unit_sale' => '_unit_sale_price',
			'price_table' => '_price_table',
		);
	}
}

if ( ! function_exists( 'wpcb_get_input_visual_meta_keys' ) ) {
	/**
	 * Returns an array of registered input visual meta keys
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_input_visual_meta_keys() {
		return array(
			'slide_order' => '_order',
			'slide_positions' => '_index',
			'slide_active' => '_slide',
			'reference' => '_reference',
			'property' => '_property',
			'multiplier' => '_multiplier',
			'material_name' => '_material_name',
			'material_type' => '_material_type',
			'canvas_width' => '_canvas_width',
			'canvas_height' => '_canvas_height',
			'model_target' => '_model_target',
			'model_orbit' => '_model_orbit',
			'model_view' => '_model_view',
			'model_node' => '_model_node',
			// 'bundle' => '_bundle',
			// 'bundle_height' => '_bundle_height',
			'font_family' => '_font_family',
			'font_size' => '_font_size',
			'font_weight' => '_font_weight',
			'font_color' => '_font_color',
			'font_style' => '_font_style',
			'text_align' => '_text_align',
			'text_rotation' => '_text_rotation',
			'top' => '_top',
			'left' => '_left',
			'width' => '_width',
			'overflow' => '_overflow',
			'image_fill' => '_image_fill',
			'customize' => '_allow_customize',
			'drag' => '_allow_drag',
			'resize' => '_allow_resize',
			'rotate' => '_allow_rotate',
			'is_dim' => '_mark_dimension',
		);
	}
}

if ( ! function_exists( 'wpcb_get_configuration_meta_keys' ) ) {
	/**
	 * Returns an array of registered input field meta keys
	 *
	 * @since    1.0.0
	 */
	function wpcb_get_configuration_meta_keys() {
		return array(
			'display' => '_product_display',
			'display_rule' => '_product_display_rule',
			'products' => '_wpcb_products',
			'product_cats' => '_wpcb_product_cats',
			'product_tags' => '_wpcb_product_tags',
			'template' => '_template',
			'template_display' => '_template_display',
			'template_height' => '_template_height',
			'template_width' => '_template_width',
			'page_display' => '_page_display',
			'form_display' => '_default_form_display',
			'gallery_position' => '_gallery_position',
			'gallery_actions_position' => '_gallery_actions_position',
			'gallery_actions' => '_gallery_actions',
			'gallery_type' => '_gallery_type',
			'gallery_sticky' => '_gallery_sticky',
			'gallery_controls' => '_gallery_controls',
			'gallery_control_position' => '_gallery_control_position',
			'label_controls' => '_gallery_label_controls',
			'icon_controls' => '_gallery_icon_controls',
			'image_views' => '_image_views',
			'woo_gallery' => '_include_woo_gallery',
			'woo_gallery_order' => '_woo_gallery_order',
			'background' => '_background',
			'background_size' => '_background_size',
			'model_file' => '_wpcb_model_file',
			'model_file_url' => '_wpcb_model_file_url',
			'poster' => '_wpcb_model_poster',
			'poster_url' => '_wpcb_model_poster_url',
			'base_nodes' => '_wpcb_model_base_nodes',
			'base_hotspots' => '_wpcb_model_base_hotspots',
			'env_image' => '_wpcb_model_env_image',
			'env_image_url' => '_wpcb_model_env_image_url',
			'env_image_exposure' => '_wpcb_model_env_image_exposure',
			'env_image_height' => '_wpcb_model_env_image_height',
			'show_env_image' => '_wpcb_model_show_env_image',
			'controls' => '_wpcb_model_controls',
			'control_position' => '_wpcb_model_control_position',
			'label_controls' => '_wpcb_model_label_controls',
			'image_controls' => '_wpcb_model_image_controls',
			'scenes' => '_wpcb_model_scenes',
			'interaction_prompt' => '_wpcb_model_interaction_prompt',
			'custom_view' => '_wpcb_model_custom_view',
			'initial_view' => '_wpcb_model_initial_view',
			'initial_fov' => '_wpcb_model_initial_fov',
			'custom_target' => '_wpcb_model_custom_target',
			'initial_target' => '_wpcb_model_initial_target',
			'custom_shadow' => '_wpcb_model_custom_shadow',
			'shadow_intensity' => '_wpcb_model_shadow_intensity',
			'custom_view_limits' => '_wpcb_model_custom_view_limits',
			'counter_clockwise_limit' => '_wpcb_model_counter_clockwise_limit',
			'clockwise_limit' => '_wpcb_model_clockwise_limit',
			'topdown_limit' => '_wpcb_model_topdown_limit',
			'bottomup_limit' => '_wpcb_model_bottomup_limit',
			'auto_rotation' => '_wpcb_model_auto_rotation',
			'auto_rotation_delay' => '_wpcb_model_auto_rotation_delay',
			'show_dimensions' => '_wpcb_model_show_dimensions',
			'dimension_unit' => '_wpcb_model_dimension_unit',
			'disable_zoom' => '_wpcb_model_disable_zoom',
			'min_zoom' => '_wpcb_model_min_zoom',
			'camera_touch_action' => '_wpcb_model_camera_touch_action',
			'camera_speed' => '_wpcb_model_camera_speed',
			'display_ar_button' => '_wpcb_model_display_ar_button',
			'ar_placement' => '_wpcb_model_ar_placement',
			'fixed_ar' => '_wpcb_model_fixed_ar',
			'hotspots' => '_wpcb_model_hotspots',
			'ratio' => '_model_ratio',
			'ratio_mobile' => '_model_ratio_mobile',
			'form_actions' => '_form_actions',
			'button_action' => '_button_action',
			'button_alt_action' => '_button_alt_action',
			'woo_product_id' => '_woo_product_id',
			'show_quantity' => '_show_quantity',
			'quantity_input' => '_quantity_input',
			'quote_shortcode' => '_quote_shortcode',
			'quote_display' => '_quote_display',
			'email_prefix' => '_email_prefix',
			'email_postfix' => '_email_postfix',
			'pdf_collect_email' => '_pdf_collect_email',
			'pdf_force_email' => '_pdf_force_email',
			'totals_display' => '_totals_display',
			'totals_position' => '_totals_position',
			'totals_review_page' => '_totals_review_page',
			'short_description' => '_short_description',
			'price' => '_price',
			'sale_price' => '_sale_price',
			'show_price' => '_show_price',
			'show_price_totals' => '_show_price_totals',
			'price_type' => '_price_type',
			'calculation_type' => '_calculation_type',
			'formula' => '_formula',
			'price_table' => '_price_table',
			'price_table_display' => '_price_table_display',
			'change_price_display' => '_change_price_display',
			'max_price' => '_max_price',
			'price_display' => '_price_display',
			'sku' => '_configuration_sku_format',
			'change_dimensions' => '_change_dimensions',
			'length' => '_configurator_length',
			'width' => '_configurator_width',
			'height' => '_configurator_height',
		);
	}
}

