<?php

/**
 * Conditional Rendering
 *
 * @author     P-THEMES
 * @package    Porto
 * @subpackage Core
 * @since      2.3.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	die();
}

use Elementor\Repeater;
use Elementor\Controls_Manager;
use Automattic\Jetpack\Device_Detection;

/**
 * Porto Conditional Rendering Class
 *
 * @since 2.3.0
 */
class Porto_Conditional_Rendering {
	/**
	 * The Instance Object.
	 *
	 * @since 2.3.0
	 */
	public static $instance;

	/**
	 * The device object
	 *
	 * @since 2.3.0
	 */
	public $device;

	/**
	 * Get Post Types
	 * 
	 * @since 2.6.0
	 */
	public static $post_types;

	/**
	 * Get Shop page id
	 * 
	 * @since 2.6.0
	 */
	public static $shop_id = -2;

	/**
	 * Get the instance.
	 *
	 * @since 2.3.0
	 */
	public static function get_instance() {
		if ( ! self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * The Constructor.
	 *
	 * @since 2.3.0
	 */
	public function __construct() {

		if ( class_exists( 'WooCommerce' ) ) {
			self::$shop_id  = (int) get_option( 'woocommerce_shop_page_id' );
		}
		add_action( 'admin_init', array( $this, 'get_post_type' ) );
		add_action( 'elementor/element/section/section_section_additional/after_section_end', array( $this, 'add_condition_system' ), 10, 2 );
		add_action( 'elementor/element/column/section_column_additional/after_section_end', array( $this, 'add_condition_system' ), 10, 2 );

		add_action( 'elementor/element/container/section_layout_container/after_section_end', array( $this, 'add_condition_system' ), 10, 2 );
		add_filter( 'elementor/frontend/container/should_render', array( $this, 'should_render' ), 10, 2 );

		add_filter( 'elementor/frontend/section/should_render', array( $this, 'should_render' ), 10, 2 );
		add_filter( 'elementor/frontend/column/should_render', array( $this, 'should_render' ), 10, 2 );

		add_action( 'vc_after_init', array( $this, 'add_wpb_condition' ), 25 );
		add_filter( 'porto_wpb_should_render', array( $this, 'wpb_should_render' ), 10, 2 );

		add_filter( 'elementor/element/is_dynamic_content', array( $this, 'disable_cache_condition_renderer' ), 10, 2 );
	}

	/**
	 * Get post types.
	 * 
	 * @since 2.6.0
	 */
	public function get_post_type() {
		self::$post_types          = get_post_types(
			array(
				'public'            => true,
				'show_in_nav_menus' => true,
			),
			'objects',
			'and'
		);
		$disabled_post_types = array( 'attachment' );
		foreach ( $disabled_post_types as $disabled ) {
			unset( self::$post_types[ $disabled ] );
		}
		foreach ( self::$post_types as $key => $p_type ) {
			self::$post_types[ $key ] = esc_html( $p_type->label );
		}
	}

	/**
	 * Add WPBakery Options
	 *
	 * @since 2.4.0
	 */
	public function add_wpb_condition() {
		if ( function_exists( 'vc_map' ) ) {
			$add_params = array(
				array(
					'param_name'  => 'condition_a',
					'heading'     => esc_html__( 'Condition A', 'porto-functionality' ),
					'description' => esc_html__( 'Select condition type.', 'porto-functionality' ),
					'type'        => 'dropdown',
					'admin_label' => true,
					'group'       => esc_html__( 'Porto Conditional System', 'porto-functionality' ),
					'value'       => array(
						'' => '',
						esc_html__( 'Device', 'porto-functionality' ) => 'device',
						esc_html__( 'Login Status', 'porto-functionality' ) => 'login_status',
						esc_html__( 'User Role', 'porto-functionality' ) => 'user_role',
						esc_html__( 'Post / Page', 'porto-functionality' ) => 'post_page',
						esc_html__( 'Current URL', 'porto-functionality' ) => 'current_url',
					),
					'std'         => '',
				),
				array(
					'param_name' => 'comparative_operator',
					'heading'    => esc_html__( 'Comparative Operator', 'porto-functionality' ),
					'type'       => 'dropdown',
					'admin_label' => true,
					'group'      => esc_html__( 'Porto Conditional System', 'porto-functionality' ),
					'value'      => array(
						'' => '',
						esc_html__( '==', 'porto-functionality' ) => 'equal',
						esc_html__( '!=', 'porto-functionality' ) => 'not_equal',
					),
					'dependency' => array(
						'element' => 'condition_a',
						'value'  => array( 'device', 'login_status', 'user_role', 'post_page' ),
					),
					'std'        => '',
				),
				array(
					'param_name' => 'comparative_operator_url',
					'heading'    => esc_html__( 'Comparative Operator', 'porto-functionality' ),
					'type'       => 'dropdown',
					'admin_label' => true,
					'group'      => esc_html__( 'Porto Conditional System', 'porto-functionality' ),
					'value'      => array(
						'' => '',
						esc_html__( '==', 'porto-functionality' ) => 'equal',
						esc_html__( '!=', 'porto-functionality' ) => 'not_equal',
						esc_html__( 'Contains', 'porto-functionality' ) => 'contains',
						esc_html__( 'Not Contains', 'porto-functionality' ) => 'not_contains',
					),
					'dependency' => array(
						'element' => 'condition_a',
						'value'   => 'current_url',
					),
					'std'        => '',
				),
				array(
					'param_name' => 'value_device',
					'heading'    => esc_html__( 'Device', 'porto-functionality' ),
					'type'       => 'dropdown',
					'group'      => esc_html__( 'Porto Conditional System', 'porto-functionality' ),
					'value'      => array(
						'' => '',
						__( 'Desktop', 'porto-functionality' ) => 'desktop',
						__( 'Tablet & Mobile', 'porto-functionality' ) => 'tablet_mobile',
						__( 'Tablet', 'porto-functionality' ) => 'tablet',
						__( 'Mobile', 'porto-functionality' ) => 'mobile',
					),
					'dependency' => array(
						'element' => 'condition_a',
						'value'   => 'device',
					),
					'std'        => '',
				),
				array(
					'param_name' => 'value_login',
					'heading'    => esc_html__( 'Status', 'porto-functionality' ),
					'type'       => 'dropdown',
					'group'      => esc_html__( 'Porto Conditional System', 'porto-functionality' ),
					'value'      => array(
						'' => '',
						esc_html__( 'Logged In', 'porto-functionality' ) => 'login',
						esc_html__( 'Logged Out', 'porto-functionality' ) => 'logout',
					),
					'dependency' => array(
						'element' => 'condition_a',
						'value'   => 'login_status',
					),
					'std'        => '',
				),
				array(
					'param_name' => 'value_role',
					'heading'    => esc_html__( 'Role', 'porto-functionality' ),
					'type'       => 'dropdown',
					'group'      => esc_html__( 'Porto Conditional System', 'porto-functionality' ),
					'value'      => array_flip( $this->get_roles() ),
					'dependency' => array(
						'element' => 'condition_a',
						'value'   => 'user_role',
					),
					'std'        => '',
				),
				array(
					'param_name'  => 'value_page_ids',
					'type'        => 'textfield',
					'heading'     => __( 'Page/Post', 'porto-functionality' ),
					'description' => __( 'Please input comma separated post, page, product or portfolio ids.', 'porto-functionality' ),
					'dependency'  => array(
						'element' => 'condition_a',
						'value'   => 'post_page',
					),
				),
				array(
					'param_name'  => 'value_current_url',
					'type'        => 'textfield',
					'heading'     => __( 'Value', 'porto-functionality' ),
					'dependency'  => array(
						'element' => 'condition_a',
						'value'   => 'current_url',
					),
				),
				array(
					'param_name'  => 'condition_operator',
					'heading'     => esc_html__( 'Operator', 'porto-functionality' ),
					'description' => esc_html__( 'The selected value is used to operate on the conditions below.', 'porto-functionality' ),
					'type'        => 'dropdown',
					'group'       => esc_html__( 'Porto Conditional System', 'porto-functionality' ),
					'value'       => array(
						'' => '',
						esc_html__( 'And', 'porto-functionality' ) => 'and',
						esc_html__( 'Or', 'porto-functionality' ) => 'or',
					),
					'std'         => '',
				),
			);
			vc_add_param(
				'vc_section',
				array(
					'type'       => 'param_group',
					'param_name' => 'conditional_render',
					'heading'    => esc_html__( 'Conditional Render', 'porto-functionality' ),
					'params'     => $add_params,
					'group'      => esc_html__( 'Porto Conditional System', 'porto-functionality' ),
				)
			);
			vc_add_param(
				'vc_row',
				array(
					'type'       => 'param_group',
					'param_name' => 'conditional_render',
					'heading'    => esc_html__( 'Conditional Render', 'porto-functionality' ),
					'params'     => $add_params,
					'group'      => esc_html__( 'Porto Conditional System', 'porto-functionality' ),
				)
			);
			vc_add_param(
				'vc_column',
				array(
					'type'       => 'param_group',
					'param_name' => 'conditional_render',
					'heading'    => esc_html__( 'Conditional Render', 'porto-functionality' ),
					'params'     => $add_params,
					'group'      => esc_html__( 'Porto Conditional System', 'porto-functionality' ),
				)
			);
		}
	}

	/**
	 * Add Control
	 *
	 * @since 2.3.0
	 */
	public function add_condition_system( $self ) {
		$self->start_controls_section(
			'section_conditional',
			array(
				'label' => esc_html__( 'Porto Conditional System', 'porto-functionality' ),
				'tab'   => Controls_Manager::TAB_LAYOUT,
			)
		);

		$repeater = new Repeater();

		$repeater->add_control(
			'condition_a',
			array(
				'label'       => esc_html__( 'Condition A', 'porto-functionality' ),
				'description' => esc_html__( 'Select condition type.', 'porto-functionality' ),
				'type'        => Controls_Manager::SELECT,
				'options'     => array(
					'device'       => esc_html__( 'Device', 'porto-functionality' ),
					'login_status' => esc_html__( 'Login Status', 'porto-functionality' ),
					'user_role'    => esc_html__( 'User Role', 'porto-functionality' ),
					'post_page'    => esc_html__( 'Post / Page', 'porto-functionality' ),
					'current_url'  => esc_html__( 'Current URL', 'porto-functionality' ),
				),
			)
		);
		$repeater->add_control(
			'comparative_operator',
			array(
				'label'   => esc_html__( 'Comparative Operator', 'porto-functionality' ),
				'type'    => Controls_Manager::SELECT,
				'options' => array(
					'equal'     => esc_html__( '==', 'porto-functionality' ),
					'not_equal' => esc_html__( '!=', 'porto-functionality' ),
				),
				'condition' => array(
					'condition_a!' => 'current_url',
				),
			)
		);
		$repeater->add_control(
			'comparative_operator_url',
			array(
				'label'   => esc_html__( 'Comparative Operator', 'porto-functionality' ),
				'type'    => Controls_Manager::SELECT,
				'options' => array(
					'equal'        => esc_html__( '==', 'porto-functionality' ),
					'not_equal'    => esc_html__( '!=', 'porto-functionality' ),
					'contains'     => esc_html__( 'Contains', 'porto-functionality' ),
					'not_contains' => esc_html__( 'Not Contains', 'porto-functionality' ),
				),
				'condition' => array(
					'condition_a' => 'current_url',
				),
			)
		);
		$repeater->add_control(
			'value_device',
			array(
				'label'     => esc_html__( 'Device', 'porto-functionality' ),
				'type'      => Controls_Manager::SELECT,
				'options'   => array(
					'desktop'       => esc_html__( 'Desktop', 'porto-functionality' ),
					'tablet_mobile' => esc_html__( 'Tablet & Mobile', 'porto-functionality' ),
					'tablet'        => esc_html__( 'Tablet', 'porto-functionality' ),
					'mobile'        => esc_html__( 'Mobile', 'porto-functionality' ),
				),
				'condition' => array(
					'condition_a' => 'device',
				),
			)
		);
		$repeater->add_control(
			'value_login',
			array(
				'label'     => esc_html__( 'Status', 'porto-functionality' ),
				'type'      => Controls_Manager::SELECT,
				'options'   => array(
					'login'  => esc_html__( 'Logged In', 'porto-functionality' ),
					'logout' => esc_html__( 'Logged Out', 'porto-functionality' ),
				),
				'condition' => array(
					'condition_a' => 'login_status',
				),
			)
		);
		$repeater->add_control(
			'value_role',
			array(
				'label'     => esc_html__( 'Role', 'porto-functionality' ),
				'type'      => Controls_Manager::SELECT,
				'options'   => $this->get_roles(),
				'condition' => array(
					'condition_a' => 'user_role',
				),
			)
		);

		$repeater->add_control(
			'post_type',
			array(
				'type'        => Controls_Manager::SELECT,
				'label'       => __( 'Post Type', 'porto-functionality' ),
				'options'     => self::$post_types,
				'condition'   => array(
					'condition_a' => 'post_page',
				),
			)
		);

		$repeater->add_control(
			'value_page_ids',
			array(
				'type'        => 'porto_ajaxselect2',
				'label'       => __( 'Page/Post', 'porto-functionality' ),
				'options'     => '%post_type%_particularpage',
				'label_block' => true,
				'multiple'    => true,
				'condition'   => array(
					'condition_a' => 'post_page',
				),
			)
		);

		$repeater->add_control(
			'value_current_url',
			array(
				'type'        => Controls_Manager::TEXT,
				'label'       => __( 'Value', 'porto-functionality' ),
				'condition'   => array(
					'condition_a' => 'current_url',
				),
			)
		);

		$repeater->add_control(
			'condition_operator',
			array(
				'label'       => esc_html__( 'Operator', 'porto-functionality' ),
				'description' => esc_html__( 'The selected value is used to operate on the conditions below.', 'porto-functionality' ),
				'type'        => Controls_Manager::SELECT,
				'options'     => array(
					'and' => esc_html__( 'And', 'porto-functionality' ),
					'or'  => esc_html__( 'Or', 'porto-functionality' ),
				),
			)
		);

		$self->add_control(
			'description_conditional_render',
			array(
				'type'            => Controls_Manager::RAW_HTML,
				'raw'             => esc_html__( 'Only when these conditions are matched, will this section be rendered.', 'porto-functionality' ),
				'content_classes' => 'elementor-panel-alert elementor-panel-alert-info',
			)
		);

		$self->add_control(
			'conditional_render',
			array(
				'label'         => esc_html__( 'Conditional Render', 'porto-functionality' ),
				'type'          => Controls_Manager::REPEATER,
				'fields'        => $repeater->get_controls(),
				'prevent_empty' => false,
				'title_field'   => '{{{ condition_a }}}',
			)
		);

		$self->end_controls_section();
	}

	/**
	 * Returns the roles.
	 *
	 * @since 2.3.0
	 */
	public function get_roles() {
		global $wp_roles;
		$roles = array();
		if ( is_array( $wp_roles->roles ) ) {
			foreach ( $wp_roles->roles as $key => $role ) {
				$roles[ $key ] = $role['name'];
			}
		}
		return $roles;
	}

	/**
	 * Get the device
	 *
	 * @since 2.3.0
	 */
	public function get_device( $is_tablet_mobile = false ) {
		if ( ! class_exists( 'Automattic\Jetpack\Device_Detection' ) && ! defined( 'JETPACK__VERSION' ) ) {
			require_once 'jetpack-device-detection/class-device-detection.php';
			require_once 'jetpack-device-detection/class-user-agent-info.php';
		}
		$critical_mobile = ! empty( $_REQUEST['mobile_url'] );
		if ( ( $critical_mobile || Device_Detection::is_phone() ) && ! $is_tablet_mobile ) {
			return 'mobile';
		} elseif ( Device_Detection::is_tablet() && ! $is_tablet_mobile ) {
			return 'tablet';
		} elseif ( ! wp_is_mobile() ) {
			return 'desktop';
		} elseif ( wp_is_mobile() ) {
			return 'tablet_mobile';
		}
		return '';
	}

	/**
	 * Check if the element should be rendered or not in WPBakery.
	 *
	 * @since 2.4.0
	 */
	public function wpb_should_render( $should_render, $conditional_render ) {
		global $pagenow;
		if ( function_exists( 'vc_is_inline' ) && ! ( in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) || vc_is_inline() ) && ! $this->is_render( array( 'conditional_render' => $conditional_render ) ) ) {
			return false;
		}
		return $should_render;
	}

	/**
	 * Check if the element should be rendered or not.
	 *
	 * @since 2.3.0
	 */
	public function should_render( $should_render, $self ) {
		$atts = $self->get_settings_for_display();
		if ( function_exists( 'porto_is_elementor_preview' ) && ! porto_is_elementor_preview() && ! $this->is_render( $atts ) ) {
			return false;
		}
		return $should_render;
	}

	/**
	 * Is rendering?
	 *
	 * @since 2.3.0
	 */
	public function is_render( $atts ) {
		
		if ( ! empty( $atts['conditional_render'] ) && is_array( $atts['conditional_render'] ) ) {
			foreach ( $atts['conditional_render'] as $condition ) {
				if ( ! empty( $condition['condition_a'] ) ) {
					switch ( $condition['condition_a'] ) {
						case 'device':
							if ( ! empty( $condition['value_device'] ) ) {
								$right = $condition['value_device'];
							}
							$left = $this->get_device( isset( $right ) && 'tablet_mobile' == $right ? true : false );
							break;
						case 'login_status':
							$left = is_user_logged_in();
							if ( ! empty( $condition['value_login'] ) ) {
								$right = ( 'login' == $condition['value_login'] ? true : false );
							}
							break;
						case 'user_role':
							$left = wp_get_current_user();
							$left = ( 0 !== $left->ID ) ? $left->roles : array();
							if ( ! empty( $condition['value_role'] ) ) {
								$right = $condition['value_role'];
							}
							break;
						case 'post_page':
							if ( ! empty( $condition['value_page_ids'] ) ) {
								$left = is_array( $condition['value_page_ids'] ) ? $condition['value_page_ids'] : explode( ',', $condition['value_page_ids'] );
							}
							$right = get_the_ID();
							if ( is_home() || is_archive() ) {
								$right = get_queried_object_id();
							}
							if ( class_exists( 'WooCommerce' ) && is_shop() ) {
								$right = self::$shop_id;
							}
							if ( is_category() || is_tax() || is_tag() ) {
								$right = -1;
							}
							break;
						case 'current_url':
							if ( wp_doing_ajax() ) {
								$left = wp_get_raw_referer();
							} else {
								$url_parts = wp_parse_url( home_url() );

								if ( isset( $url_parts['scheme'], $url_parts['host'] ) ) {
									$left = $url_parts['scheme'] . '://' . $url_parts['host'] . add_query_arg( null, null );
								} else {
									$left = get_pagenum_link();
								}
							}
							if ( ! empty( $condition['value_current_url'] ) ) {
								$right = trim( $condition['value_current_url'] );
							}

							if ( ! empty( $condition['comparative_operator_url'] ) ) {
								$operator = $condition['comparative_operator_url'];
							}
							break;
					}
					if ( ! isset( $operator ) && ! empty( $condition['comparative_operator'] ) ) {
						$operator = $condition['comparative_operator'];
					}
					if ( ! empty( $condition['condition_operator'] ) ) {
						$condition_operator = $condition['condition_operator'];
					}
					if ( isset( $left ) && isset( $right ) && isset( $operator ) ) {
						if ( 'equal' == $operator ) {
							if ( is_array( $left ) ) {
								$res = in_array( $right, $left );
							} else {
								$res = ( $left == $right );
							}
						} elseif ( 'contains' == $operator ) {
							$res = ( false !== strpos( $left, $right ) );
						} elseif ( 'not_contains' == $operator ) {
							$res = ( false === strpos( $left, $right ) );
						} else {
							if ( is_array( $left ) ) {
								$res = ! in_array( $right, $left );
							} else {
								$res = ( $left != $right );
							}
						}
						if ( isset( $render ) ) {
							if ( isset( $prev_operator ) && 'or' == $prev_operator ) {
								$render = $render || $res;
							} else {
								$render = $render && $res;
							}
						} else {
							$render = $res;
						}
						if ( isset( $condition_operator ) ) {
							$prev_operator = $condition_operator;
						} else { // not select
							$prev_operator = 'and';
						}
					}
					unset( $left, $right, $operator );
				}
			}
		}

		return isset( $render ) ? $render : true;
	}

	/**
	 * Fix compatibility issue with Elementor cache & Conditional rendering
	 * 
	 * @since 7.2.2
	 */
	public function disable_cache_condition_renderer( $flag, $raw_data ) {
		if ( isset( $raw_data['settings'] ) && ! empty( $raw_data['settings']['conditional_render'] ) && is_array( $raw_data['settings']['conditional_render'] ) ) {
			return true;
		}

		return $flag;
	}
}

Porto_Conditional_Rendering::get_instance();
