<?php // phpcs:ignoreFile
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Utilities\Conditional;

/**
 * Advanced Visitor Conditions module.
 *
 * -TODO should use a constant for option key as it is shared at multiple positions
 */
class Advanced_Ads_Pro_Module_Advanced_Visitor_Conditions {

	protected $options = [];
	protected $is_ajax;

	// Note: hard-coded in JS
	const REFERRER_COOKIE_NAME = 'advanced_ads_pro_visitor_referrer';

	// page impression counter
	const PAGE_IMPRESSIONS_COOKIE_NAME = 'advanced_ads_page_impressions';

	// ad impression cookie name basis
	const AD_IMPRESSIONS_COOKIE_NAME = 'advanced_ads_ad_impressions';

	public function __construct() {
		// load options (and only execute when enabled)
		$options = Advanced_Ads_Pro::get_instance()->get_options();
		if ( isset( $options['advanced-visitor-conditions'] ) ) {
			$this->options = $options['advanced-visitor-conditions'];
		}

		// only execute when enabled
		if ( ! isset( $this->options['enabled'] ) || ! $this->options['enabled'] ) {
			return ;
		}

		$is_admin = is_admin();
		$this->is_ajax = wp_doing_ajax();

		add_filter( 'advanced-ads-visitor-conditions', [ $this, 'visitor_conditions' ] );
		// action after ad output is created; used for js injection
		add_filter( 'advanced-ads-ad-output', [ $this, 'after_ad_output' ], 10, 2 );
		if ( $is_admin ) {
			// add referrer check to visitor conditions
			// add_action( 'advanced-ads-visitor-conditions-after', array( $this, 'referrer_check_metabox' ), 10, 2 );

			/*if ( $this->is_ajax ) {
				add_action( 'advanced-ads-ajax-ad-select-init', array( $this, 'ajax_init_ad_select' ) );
			}*/
		// wp ajax is admin but this will allow other ajax callbacks to avoid setting the referrer
		} elseif ( ! $this->is_ajax ) {
			// save referrer url in session for visitor referrer url feature
			$this->save_first_referrer_url();
			// count page impression
			$this->count_page_impression();

			// register js script to set cookie for cached pages
			add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts']);

			// enable common frontend logic
			// $this->init_common_frontend();
		}
	}

	/**
	 * Specially prepare for ajax ad select calls.
	 *
	 */
	public function ajax_init_ad_select() {
		$this->init_common_frontend();
	}

	/**
	 * Init for any frontend action (including ajax ad select calls)
	 *
	 */
	public function init_common_frontend() {
		// check the url referrer condition
		// add_filter( 'advanced-ads-can-display', array( $this, 'can_display_by_url_referrer' ), 10, 2 );
	}

	/**
	 * Add scripts to non-ajax frontend calls.
	 */
	public function enqueue_scripts() {
		if ( Conditional::is_amp() ) {
			return;
		}

		wp_register_script(
			'advanced_ads_pro/visitor_conditions',
			sprintf( '%sinc/conditions%s.js', plugin_dir_url( __FILE__ ), defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min' ),
			[ ADVADS_SLUG . '-advanced-js' ],
			AAP_VERSION
		);

		// 1 year by default
		$referrer_exdays = ( defined( 'ADVANCED_ADS_PRO_REFERRER_EXDAYS' ) && absint( ADVANCED_ADS_PRO_REFERRER_EXDAYS ) > 0 ) ? absint( ADVANCED_ADS_PRO_REFERRER_EXDAYS ) : 365;
		// 10 years by default
		$page_impressions_exdays = ( defined( 'ADVANCED_ADS_PRO_PAGE_IMPR_EXDAYS' ) && absint( ADVANCED_ADS_PRO_PAGE_IMPR_EXDAYS ) > 0 ) ? absint( ADVANCED_ADS_PRO_PAGE_IMPR_EXDAYS ) : 3650;

		wp_localize_script( 'advanced_ads_pro/visitor_conditions', 'advanced_ads_pro_visitor_conditions', [
			'referrer_cookie_name' => self::REFERRER_COOKIE_NAME,
			'referrer_exdays' => $referrer_exdays,
			'page_impr_cookie_name' => self::PAGE_IMPRESSIONS_COOKIE_NAME,
			'page_impr_exdays' => $page_impressions_exdays
		]);

		wp_enqueue_script( 'advanced_ads_pro/visitor_conditions' );
	}

	/**
	 * Add visitor conditions
	 *
	 * @since 1.0.1
	 *
	 * @param array $conditions visitor conditions of the main plugin.
	 *
	 * @return array $conditions new global visitor conditions.
	 */
	public function visitor_conditions( $conditions ) {
		$conditions['referrer_url'] = [
			'label'       => __( 'referrer url', 'advanced-ads-pro' ),
			'description' => __( 'Display ads based on the referrer URL.', 'advanced-ads-pro' ),
			'metabox'     => [ 'Advanced_Ads_Visitor_Conditions', 'metabox_string' ],
			'check'       => [ $this, 'check_referrer_url' ],
			'helplink'    => 'https://wpadvancedads.com/manual/referrer-url/?utm_source=advanced-ads&utm_medium=link&utm_campaign=condition-referrer-url',
		];

		$conditions['user_agent'] = [
			'label'       => __( 'user agent', 'advanced-ads-pro' ),
			'description' => __( 'Display ads based on the user agent.', 'advanced-ads-pro' ),
			'metabox'     => [ 'Advanced_Ads_Visitor_Conditions', 'metabox_string' ],
			'check'       => [ $this, 'check_user_agent' ],
			'helplink'    => 'https://wpadvancedads.com/manual/display-ads-based-on-browser-or-device/?utm_source=advanced-ads&utm_medium=link&utm_campaign=condition-user-agent',
		];

		$conditions['capability'] = [
			'label'        => __( 'user can (capabilities)', 'advanced-ads-pro' ),
			'description'  => __( ' Display ads based on the user’s capabilities.', 'advanced-ads-pro' ),
			'metabox'      => [ $this, 'metabox_capabilities' ],
			'check'        => [ $this, 'check_capabilities' ],
			'passive_info' => [
				'hash_fields' => 'value',
				'remove'      => 'login',
				'function'    => [ $this, 'get_passive_capability' ],
			],
		];

		$conditions['role'] = [
			'label'        => __( 'user role', 'advanced-ads-pro' ),
			'description'  => sprintf(
				/* translators: %s is a placeholder for the URL. */
				__( 'Display ads based on the user\'s roles. See <a href="%s" target="_blank">List of roles in WordPress</a>.', 'advanced-ads-pro' ),
				'https://codex.wordpress.org/Roles_and_Capabilities'
			),
			'metabox'      => [ $this, 'metabox_roles' ],
			'check'        => [ $this, 'check_roles' ],
			'passive_info' => [ 'hash_fields' => 'value', 'remove' => 'login', 'function' => [ 'Advanced_Ads_Pro_Module_Advanced_Visitor_Conditions', 'get_passive_role' ] ],
			'helplink'    => 'https://wpadvancedads.com/manual/user-role/?utm_source=advanced-ads&utm_medium=link&utm_campaign=condition-user-role',
		];

		$conditions['browser_lang'] = [
			'label'       => __( 'browser language', 'advanced-ads-pro' ),
			'description' => __( "Display ads based on the visitor's browser language.", 'advanced-ads-pro' ),
			'metabox'     => [ $this, 'metabox_browser_lang' ],
			'check'       => [ $this, 'check_browser_lang' ],
		];

		$conditions['cookie'] = [
			'label'       => __( 'cookie', 'advanced-ads-pro' ),
			'description' => __( 'Display ads based on the value of a cookie. Set the operator to “matches/does not match” and leave the value empty to check only the existence of the cookie.', 'advanced-ads-pro' ),
			'metabox'     => [ $this, 'metabox_cookie' ],
			'check'       => [ $this, 'check_cookie' ],
		];

		$conditions['page_impressions'] = [
			'label'       => __( 'page impressions', 'advanced-ads-pro' ),
			'description' => __( 'Display ads based on the number of page impressions the user already made before the current one.', 'advanced-ads-pro' ),
			'metabox'     => [ 'Advanced_Ads_Visitor_Conditions', 'metabox_number' ],
			'check'       => [ $this, 'check_page_impressions' ],
			'helplink'    => 'https://wpadvancedads.com/manual/ads-by-page-impressions/?utm_source=advanced-ads&utm_medium=link&utm_campaign=condition-page-impressions',
		];

		$conditions['ad_impressions'] = [
			'label'       => __( 'max. ad impressions', 'advanced-ads-pro' ),
			'description' => __( 'Display the ad only for a few impressions in a given period per user.', 'advanced-ads-pro' ),
			'metabox'     => [ $this, 'metabox_ad_impressions' ],
			'check'       => [ $this, 'check_ad_impressions' ],
		];

		$conditions['new_visitor'] = [
			'label'       => __( 'new visitor', 'advanced-ads-pro' ),
			'description' => __( 'Display or hide ads for new visitors.', 'advanced-ads-pro' ),
			'metabox'     => [ 'Advanced_Ads_Visitor_Conditions', 'metabox_is_or_not' ],
			'check'       => [ $this, 'check_new_visitor' ],
			'helplink'    => 'https://wpadvancedads.com/manual/ads-for-new-visitors/?utm_source=advanced-ads&utm_medium=link&utm_campaign=condition-new-visitors',
		];

		$conditions['adblocker'] = [
			'label'       => __( 'Adblocker', 'advanced-ads-pro' ),
			'description' => __( 'Display or hide ad when user use adblocker.', 'advanced-ads-pro' ),
			'metabox'     => [ 'Advanced_Ads_Visitor_Conditions', 'metabox_is_or_not' ],
			'check'       => [ 'Advanced_Ads_Pro_Module_Advanced_Visitor_Conditions', 'check_adblocker' ],
			'helplink'    => 'https://wpadvancedads.com/manual/ad-blockers/',
		];

		$conditions['ip_address'] = [
			'label'        => __( 'User IP Address', 'advanced-ads-pro' ),
			'description'  => __( 'Display ads based on the user IP address. Enter one IP address per line.', 'advanced-ads-pro' ),
			'metabox'      => [ $this, 'metabox_ip_address' ],
			'check'        => [ $this, 'check_ip_address' ],
			'passive_info' => [
				'hash_fields' => 'value,operator', // operator for uniqueness
				'remove'      => 'login',
				'function'    => [ $this, 'get_passive_ip_address' ],
			],
		];

		return $conditions;
	}

	/**
	 * Save the first referrer url submitted. Cookies is set using JavaScript
	 *
	 * @since 1.1.0
	 */
	protected function save_first_referrer_url() {
		$cookie = Params::cookie( self::REFERRER_COOKIE_NAME );
		if ( ! $cookie && ! empty( Params::server( 'HTTP_REFERER' ) ) ) {
			// make cookies directly available to current request.
			$_COOKIE[ self::REFERRER_COOKIE_NAME ] = Params::server( 'HTTP_REFERER' );
		}
	}


	/**
	 * save page impressions in cookie. Cookies is set using JavaScript
	 *
	 * @since 1.1.0
	 */
	protected function count_page_impression(){
		if ( $this->is_ajax ) {
			return;
		}

		// Make cookies directly available to current request.
		$impressions = Params::cookie( self::PAGE_IMPRESSIONS_COOKIE_NAME );
		$impressions = $impressions ? absint( self::extract_cookie_data( $impressions ) ) : 0;
		$_COOKIE[ self::PAGE_IMPRESSIONS_COOKIE_NAME ] = $impressions + 1;
	}

	/**
	 * callback to display the "capabilities" condition
	 *
	 * @param arr $options options of the condition
	 * @param int $index index of the condition
	 */
	static function metabox_capabilities( $options, $index = 0, $form_name = '' ) {

	    if ( ! isset ( $options['type'] ) || '' === $options['type'] ) { return; }

	    $type_options = Advanced_Ads_Visitor_Conditions::get_instance()->conditions;

	    if ( ! isset( $type_options[ $options['type'] ] ) ) {
		    return;
	    }

	    // form name basis
		$name = self::get_form_name_with_index( $form_name, $index );

	    // options
	    $value = isset( $options['value'] ) ? $options['value'] : '';
	    $operator = isset( $options['operator'] ) ? $options['operator'] : 'can';

	    // load capabilities
	    global $wp_roles;
	    $roles = $wp_roles->roles;

	    // loop through all roles in order to get registered capabilities
	    $capabilities = [];
	    foreach ( $roles as $_role ){
		    if( isset( $_role['capabilities'] )){
			$capabilities += $_role['capabilities'];
		    }
	    }

	    // sort keys by alphabet
	    ksort( $capabilities );

	    ?><input type="hidden" name="<?php echo $name; ?>[type]" value="<?php echo $options['type']; ?>"/>
		<select name="<?php echo $name; ?>[operator]">
		    <option value="can" <?php selected( 'can', $operator ); ?>><?php _e( 'can', 'advanced-ads-pro' ); ?></option>
		    <option value="can_not" <?php selected( 'can_not', $operator ); ?>><?php _e( 'can not', 'advanced-ads-pro' ); ?></option>
		</select>
		<div class="advads-conditions-select-wrap"><select name="<?php echo $name; ?>[value]">
			<option><?php _e( '-- choose one --', 'advanced-ads-pro' ); ?></option>
			<?php foreach( $capabilities as $cap => $_val ) : ?>
				<option value="<?php echo $cap; ?>" <?php selected( $cap, $value ); ?>><?php echo $cap; ?></option>
			<?php endforeach; ?>
		</select></div>
		<p class="description">
			<?php echo esc_html( $type_options[ $options['type'] ]['description'] ); ?>
			<a href="https://wpadvancedads.com/manual/display-ads-based-on-user-capabilities/?utm_source=advanced-ads&utm_medium=link&utm_campaign=condition-user-can" class="advads-manual-link" target="_blank">
				<?php esc_html_e( 'Manual', 'advanced-ads-pro' ); ?>
			</a>
		</p>
		<?php
	}

	/**
	 * Callback to display the "roles" condition.
	 *
	 * @param arr $options options of the condition
	 * @param int $index   index of the condition
	 */
	static function metabox_roles( $options, $index = 0, $form_name = '' ) {

		if ( ! isset ( $options['type'] ) || '' === $options['type'] ) {
			return;
		}

		$type_options = Advanced_Ads_Visitor_Conditions::get_instance()->conditions;

		if ( ! isset( $type_options[ $options['type'] ] ) ) {
			return;
		}

		// form name basis
		$name = self::get_form_name_with_index( $form_name, $index );

		// options
		$value    = $options['value'] ?? '';
		$operator = $options['operator'] ?? 'is';

		global $wp_roles;
		$roles = $wp_roles->get_names();

		?><input type="hidden" name="<?php echo $name; ?>[type]" value="<?php echo $options['type']; ?>"/>
		<select name="<?php echo $name; ?>[operator]">
			<option value="is" <?php selected( 'is', $operator ); ?>><?php _e( 'is', 'advanced-ads-pro' ); ?></option>
			<option value="is_not" <?php selected( 'is_not', $operator ); ?>><?php _e( 'is not', 'advanced-ads-pro' ); ?></option>
		</select>
		<div class="advads-conditions-select-wrap"><select name="<?php echo $name; ?>[value]">
				<option><?php _e( '-- choose one --', 'advanced-ads-pro' ); ?></option>
				<?php foreach ( $roles as $_role => $_display_name ) : ?>
					<option value="<?php echo $_role; ?>" <?php selected( $_role, $value ); ?>><?php echo $_display_name; ?></option>
				<?php endforeach; ?>
			</select></div>
		<p class="description">
			<?php echo $type_options[ $options['type'] ]['description']; ?>
			<?php if ( isset( $type_options[ $options['type'] ]['helplink'] ) ) : ?>
				<a href="<?php echo esc_url( $type_options[ $options['type'] ]['helplink'] ); ?>" class="advads-manual-link" target="_blank"><?php esc_html_e( 'Manual', 'advanced-ads' ); ?></a>
			<?php endif; ?>
		</p>
		<?php
	}

	/**
	 * callback to display the "browser language" condition
	 *
	 * @param arr $options options of the condition
	 * @param int $index index of the condition
	 */
	static function metabox_browser_lang( $options, $index = 0, $form_name = '' ) {

	    if ( ! isset ( $options['type'] ) || '' === $options['type'] ) { return; }

	    $type_options = Advanced_Ads_Visitor_Conditions::get_instance()->conditions;

	    if ( ! isset( $type_options[ $options['type'] ] ) ) {
		    return;
	    }

	    // form name basis
		$name = self::get_form_name_with_index( $form_name, $index );

	    // options
	    $operator = isset( $options['operator'] ) ? $options['operator'] : 'is';
	    $value = isset( $options['value'] ) ? $options['value'] : '';

	    // load browser languages
	    include plugin_dir_path( __FILE__ ) . 'inc/browser_langs.php';
	    if( isset( $advads_browser_langs )){
		asort( $advads_browser_langs );
	    }

	    ?><input type="hidden" name="<?php echo $name; ?>[type]" value="<?php echo $options['type']; ?>"/>
		<select name="<?php echo $name; ?>[operator]">
			<option value="is" <?php selected( 'is', $operator ); ?>><?php _e( 'is', 'advanced-ads-pro' ); ?></option>
			<option value="is_not" <?php selected( 'is_not', $operator ); ?>><?php _e( 'is not', 'advanced-ads-pro' ); ?></option>
	    </select>
		<select name="<?php echo $name; ?>[value]">
			<option><?php _e( '-- choose one --', 'advanced-ads-pro' ); ?></option>
			<?php if( isset( $advads_browser_langs )) :
			    foreach( $advads_browser_langs as $_key => $_title ) : ?>
				<option value="<?php echo $_key; ?>" <?php selected( $_key, $value ); ?>><?php echo $_title; ?></option>
			<?php endforeach;
			endif; ?>
		</select>
		<p class="description">
			<?php echo esc_html( $type_options[ $options['type'] ]['description'] ); ?>
			<a href="https://wpadvancedads.com/manual/browser-language/?utm_source=advanced-ads&utm_medium=link&utm_campaign=condition-browser-language" class="advads-manual-link" target="_blank">
				<?php esc_html_e( 'Manual', 'advanced-ads-pro' ); ?>
			</a>
		</p>
		<?php
	}


	/**
	 * callback to display the "cookie" condition
	 *
	 * @param arr    $options Options of the condition.
	 * @param int    $index Index of the conditionA.
	 * @param string $form_name Name of the form, falls back to class constant.
	 */
	static function metabox_cookie( $options, $index = 0, $form_name = '' ) {

	    if ( ! isset ( $options['type'] ) || '' === $options['type'] ) { return; }

	    $type_options = Advanced_Ads_Visitor_Conditions::get_instance()->conditions;

	    if ( ! isset( $type_options[ $options['type'] ] ) ) {
		    return;
	    }

	    // form name basis
		$name = self::get_form_name_with_index( $form_name, $index );

		$operator = isset( $options['operator'] ) ? self::maybe_replace_cookie_operator( $options['operator'] ) : 'contain';

	    // options
		$cookie = isset( $options['cookie'] ) ? $options['cookie'] : ''; // Cookie name.
		// the value may be slashed if displayed in placement, we also need to convert to htmlentities to display `"`
		$value = isset( $options['value'] ) ? htmlentities( wp_unslash( $options['value'] ) ) : '';

		ob_start();
		if ( 0 <= version_compare( ADVADS_VERSION, '1.9.1' ) ) {
			include( ADVADS_ABSPATH . 'admin/views/ad-conditions-string-operators.php' );
		}
		$operatoroption = ob_get_clean();

		$cookieoption = '<input type="text" name="' . $name . '[cookie]" value="' . $cookie . '" placeholder="' . __( 'Cookie Name', 'advanced-ads-pro' ) . '"/>';
		$valueoption = '<input type="text" name="' . $name . '[value]" value="' . $value . '" placeholder="' . __( 'Cookie Value', 'advanced-ads-pro' ) . '"/>';

		?>
		<input type="hidden" name="<?php echo esc_attr( $name ); ?>[type]" value="<?php echo esc_attr( $options['type'] ); ?>"/>
		<div class="advads-condition-line-wrap">
		<?php
		// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
		/* 1: Cookie Name, 2: Operator, 3: Cookie Value */
		printf( '%1$s %2$s %3$s', $cookieoption, $operatoroption, $valueoption );
		// phpcs:enable
		?>
		</div>
		<div class="clear"></div>
		<p class="description">
			<?php echo esc_html( $type_options[ $options['type'] ]['description'] ); ?>
			<a href="https://wpadvancedads.com/manual/cookie-condition/?utm_source=advanced-ads&utm_medium=link&utm_campaign=condition-cookie" class="advads-manual-link" target="_blank">
				<?php esc_html_e( 'Manual', 'advanced-ads-pro' ); ?>
			</a>
		</p>
		<?php
	}

	/**
	 * callback to display the condition for ad impressions in a specific time frame
	 *
	 * @param arr $options options of the condition
	 * @param int $index index of the condition
	 */
	static function metabox_ad_impressions( $options, $index = 0, $form_name = '' ) {

	    if ( ! isset ( $options['type'] ) || '' === $options['type'] ) { return; }

	    $type_options = Advanced_Ads_Visitor_Conditions::get_instance()->conditions;

	    if ( ! isset( $type_options[ $options['type'] ] ) ) {
		    return;
	    }

	    // form name basis
		$name = self::get_form_name_with_index( $form_name, $index );

	    // options
	    $value = isset( $options['value'] ) ? absint( $options['value'] ) : 0;
	    $timeout = isset( $options['timeout'] ) ? absint( $options['timeout'] ) : 0;

	    ?><input type="hidden" name="<?php echo $name; ?>[type]" value="<?php echo $options['type']; ?>"/>
		<input type="number" required="required" min="0" name="<?php echo $name; ?>[value]" value="<?php echo absint( $value ); ?>"/>
		<?php
		$impressions_field = '<input type="number" required="required" min="0" name="' . $name . '[timeout]" value="' . $timeout . '"/>';
		/* translators: %s is a placeholder for the impressions field */
		printf( __( 'within %s seconds', 'advanced-ads-pro' ), $impressions_field ); ?>
		<p class="description">
			<?php echo esc_html( $type_options[ $options['type'] ]['description'] ); ?>
			<a href="https://wpadvancedads.com/manual/max-ad-impressions/?utm_source=advanced-ads&utm_medium=link&utm_campaign=condition-max-ad-impressions" class="advads-manual-link" target="_blank">
				<?php esc_html_e( 'Manual', 'advanced-ads-pro' ); ?>
			</a>
		</p>
		<?php
	}

	/**
	 * IP Address Metabox.
	 *
	 * @param array  $options options of the condition.
	 * @param int    $index index of the condition.
	 * @param string $form_name Name of the form, falls back to class constant.
	 *
	 * @return void
	 */
	public static function metabox_ip_address( $options, $index = 0, $form_name = '' ) {
		// Early bail!!
		$type_options = Advanced_Ads_Visitor_Conditions::get_instance()->conditions;
		if ( empty( $options['type'] ) || empty( $type_options[ $options['type'] ] ) ) {
			return;
		}

		$name     = self::get_form_name_with_index( $form_name, $index );
		$operator = $options['operator'] ?? 'is';

		printf( '<input type="hidden" name="%s[type]" value="%s"/>', esc_attr( $name ), esc_attr( $options['type'] ) );
		printf(
			'<select name="%s[operator]">
				<option value="is" %s>%s</option>
				<option value="is_not" %s>%s</option>
			</select>',
			esc_attr( $name ),
			selected( 'is', $operator, false ),
			esc_html__( 'is', 'advanced-ads-pro' ),
			selected( 'is_not', $operator, false ),
			esc_html__( 'is not', 'advanced-ads-pro' )
		);
		printf(
			'<textarea name="%s">%s</textarea>',
			esc_attr( $name . '[value]' ),
			esc_textarea( $options['value'] ?? '' )
		);
		printf( '<p class="description">%s</p>', esc_html( $type_options[ $options['type'] ]['description'] ) );
	}

	/**
	 * check referrer url in frontend
	 *
	 * @since 1.0.1
	 * @param array $options Options of the condition.
	 * @return bool true if ad can be displayed
	 */
	static function check_referrer_url( $options = [] ){
		// Check if session variable is set.
		$cookie = Params::cookie( self::REFERRER_COOKIE_NAME );
		if ( ! $cookie ) {
			return false;
		}
		$referrer = self::extract_cookie_data( $cookie );

		return Advanced_Ads_Visitor_Conditions::helper_check_string( $referrer, $options );
	}

	/**
	 * check user agent in frontend
	 *
	 * @since 1.0.1
	 * @param arr $options options of the condition
	 * @return bool true if ad can be displayed
	 */
	static function check_user_agent( $options = [] ){
		$user_agent = Params::server( 'HTTP_USER_AGENT', '' );

		return Advanced_Ads_Visitor_Conditions::helper_check_string( $user_agent, $options );
	}

	/**
	 * check user capabilities in frontend
	 *
	 * @since 1.0.1
	 * @param arr $options options of the condition
	 * @return bool true if ad can be displayed
	 */
	static function check_capabilities( $options = [] ){

		if ( ! isset( $options['value'] ) || '' === $options['value'] || ! isset( $options['operator'] ) ){
			return true;
		}

		switch ( $options['operator'] ){
		    case 'can' :
			    return ( current_user_can( $options['value'] ) );
			    break;
		    case 'can_not' :
			    return ( ! current_user_can( $options['value'] ) );
		}

		return true;
	}

	/**
	 * Check user roles in frontend.
	 *
	 * @param arr $options options of the condition
	 * @return bool true if ad can be displayed
	 */
	static function check_roles( $options = [] ){
		if ( ! isset( $options['value'] ) || '' === $options['value'] || ! isset( $options['operator'] ) ){
			return true;
		}

		$user = wp_get_current_user();
		if ( ! is_array( $user->roles ) ) {
			return false;
		}

		switch ( $options['operator'] ) {
			case 'is' :
				return ( in_array( $options['value'], $user->roles, true ) );
				break;
			case 'is_not' :
				return ! ( in_array( $options['value'], $user->roles, true ) );
		}

		return true;
	}

	/**
	 * check browser language
	 *
	 * @since 1.0.1
	 * @param arr $options options of the condition
	 * @return bool true if ad can be displayed
	 */
	static function check_browser_lang( $options = [] ){

		if ( ! isset( $options['value'] ) || '' === $options['value'] ){
			return true;
		}

		$language = Params::server( 'HTTP_ACCEPT_LANGUAGE', '' );
		if ( '' === $language ) {
			return false;
		}

		// check if the browser lang is within the accepted language string
		$regex = "@\b" . $options['value'] . "\b@i"; // \b checks for "whole words"
		$result = preg_match( $regex, $language ) === 1;

		if ( isset( $options['operator'] ) && $options['operator'] === 'is_not' ) {
			return ! $result;
		} else {
			return $result;
		}
	}

	/**
	 * check cookie value in frontend
	 *
	 * @param array $options Options of the condition.
	 *
	 * @return bool true if ad can be displayed
	 * @since 1.1.1
	 */
	public static function check_cookie( $options = [] ) {
		if ( isset( $options['operator'] ) ) {
			$options['operator'] = self::maybe_replace_cookie_operator( $options['operator'] );
		}

		// do not manipulate the cookie values and the comparison for RegEx.
		$encode = ! in_array($options['operator'], ['regex', 'regex_not'], true);

		$must_be_set = ! isset( $options['operator'] ) || 'match_not' !== $options['operator'];

		// Check if cookie option exists.
		if ( empty( $options['cookie'] ) || empty( $options['value'] ) ) {
			return $must_be_set;
		}

		// check if there are cookies.
		$cookie = Params::server( 'HTTP_COOKIE', '' );
		if ( empty( $cookie ) ) {
			return ! $must_be_set;
		}

		// Get the raw cookie keys and values; the superglobal $_COOKIE holds manipulated keys and values.
		$raw_cookies = array_reduce( explode( ';', $cookie ), static function( $carry, $item ) use($encode) {
			$cookie_pair = explode( '=', $item, 2 );
			if ( count( $cookie_pair ) !== 2 ) {
				return $carry;
			}
			$carry[ trim( $cookie_pair[0] ) ] = $encode
				? urlencode( urldecode( wp_unslash( trim( $cookie_pair[1] ) ) ) )
				: $cookie_pair[1];

			return $carry;
		}, [] );

		// check if the cookie exists.
		if ( ! isset( $raw_cookies[ $options['cookie'] ] ) ) {
			return ! $must_be_set;
		}

		if ($encode) {
			// de- and then encode the value, this catches values the user entered decoded and encoded.
			$options['value'] = urlencode( urldecode( wp_unslash( $options['value'] ) ) );
		}

		return Advanced_Ads_Visitor_Conditions::helper_check_string( $raw_cookies[ $options['cookie'] ], $options );
	}

	/**
	 * check page_impressions in frontend
	 *
	 * @since 1.1.1
	 * @param array $options Options of the condition.
	 * @return bool true if ad can be displayed
	 */
	static function check_page_impressions( $options = [] ){
	    if ( ! isset( $options['operator'] ) || ! isset( $options['value'] ) ) {
			return true;
	    }

	    $impressions = Params::cookie( self::PAGE_IMPRESSIONS_COOKIE_NAME, 0 );
	    if ( ! $impressions ) {
			return false;
	    }

		$value       = absint( $options['value'] );
		$impressions = absint( self::extract_cookie_data( $impressions ) );

	    switch ( $options['operator'] ){
		    case 'is_equal' :
			    if ( $value !== $impressions ) { return false; }
			    break;
		    case 'is_higher' :
			    if ( $value > $impressions ) { return false; }
			    break;
		    case 'is_lower' :
			    if ( $value < $impressions ) { return false; }
			    break;
	    }

	    return true;
	}

	/**
	 * Check IP address in frontend.
	 *
	 * @param array $options options of the condition.
	 *
	 * @return bool true if ad can be displayed.
	 */
	public static function check_ip_address( $options = [] ) {
		if ( empty( $options['value'] ) || empty( $options['operator'] ) ) {
			return true;
		}

		$user_ip      = get_user_ip_address();
		$ip_addresses = explode( "\n", $options['value'] );

		switch ( $options['operator'] ) {
			case 'is':
				return in_array( $user_ip, $ip_addresses, true );
			case 'is_not':
				return ! in_array( $user_ip, $ip_addresses, true );
		}

		return true;
	}

	/**
	 * Get IP address information to use in passive cache-busting.
	 */
	public static function get_passive_ip_address( $options = [] ) {
		return get_user_ip_address();
	}

	/**
	 * check ad impressions limit for the ad in frontend
	 *
	 * @since 1.2.4
	 * @param arr $options options of the condition
	 * @param obj $ad Ad
	 * @return bool true if ad can be displayed
	 */
	static function check_ad_impressions( $options = [], $ad = false ){

	    if ( ! is_an_ad( $ad ) || ! isset( $options['value'] ) || ! isset( $options['timeout'] ) ) {
			return true;
	    }

	    $value = absint( $options['value'] );
	    $impressions = 0;
	    $cookie_name = self::AD_IMPRESSIONS_COOKIE_NAME . '_' . $ad->get_id();
	    $cookie_timeout_name = $cookie_name . '_timeout';

	    $cookie_value = Params::cookie( $cookie_name );
		if ( $cookie_value && Params::cookie( $cookie_timeout_name ) ) {
			$impressions = absint( $cookie_value );
			if ( $value <= $impressions ) {
				return false;
			}
	    }

	    return true;
	}

	/**
	 * check new_visitor in frontend
	 *
	 * @since 1.1.1
	 * @param array $options Options of the condition.
	 * @return bool true if ad can be displayed
	 */
	static function check_new_visitor( $options = [] ){
	    if ( ! isset( $options['operator'] ) ) {
			return true;
	    }

	    $impressions = Params::cookie( self::PAGE_IMPRESSIONS_COOKIE_NAME, 0 );
	    if ( $impressions ) {
			$impressions = absint( self::extract_cookie_data( $impressions ) );
	    }

	    switch ( $options['operator'] ){
		    case 'is' :
			    return 1 === $impressions;
			    break;
		    case 'is_not' :
			    return 1 < $impressions;
			    break;
	    }

	    return true;
	}

	/**
	 * Get capability information to use in passive cache-busting.
	 */
	static function get_passive_capability( $options = [] ) {
		if ( ! isset( $options['value'] ) ) {
			return;
		}
		$userdata = get_userdata( get_current_user_id() );
		if ( ! empty( $userdata->allcaps ) && is_array( $userdata->allcaps ) && ! empty( $userdata->allcaps[ $options['value'] ] ) ) {
			return $options['value'];
		}
	}

	/**
	 * Get role information to use in passive cache-busting.
	 */
	static function get_passive_role( $options = [] ) {
		if ( ! isset( $options['value'] ) ) {
			return;
		}
		$user = wp_get_current_user();
		if ( ! empty( $user->roles ) && is_array( $user->roles ) && in_array( $options['value'], $user->roles ) ) {
			return $options['value'];
		}
	}

	/**
	 * Inject ad output and js code.
	 *
	 * @since 1.2.4
	 * @param string          $content Ad content.
	 * @param Ad $ad Ad object.
	 *
	 * @return string
	 */
	public function after_ad_output( $content, Ad $ad ) {
		// Do not enqueue on AMP pages.
		if ( Conditional::is_amp() ) {
			return $content;
		}
		$options = $ad->get_visitor_conditions();
		if( is_array( $options )) foreach( $options as $_visitor_condition ){
			if( isset( $_visitor_condition['type'] )){
				switch( $_visitor_condition['type'] ){
					// set limit and timeout for max_ad_impressions visitor condition
					case 'ad_impressions' :
					    $limit = isset( $_visitor_condition['value'] ) ? $_visitor_condition['value'] : '';
					    $timeout = isset( $_visitor_condition['timeout'] ) ? $_visitor_condition['timeout'] : '';
					    $timeout = ( $timeout ) ? $timeout : '""';
					    // cookie names
					    $cookie_name = self::AD_IMPRESSIONS_COOKIE_NAME . '_' . $ad->get_id();
					    $cookie_timeout_name = $cookie_name . '_timeout';
					    // get current count, if timeout not reached yet
					    $count = ( isset( $_COOKIE[ $cookie_name ] ) && isset( $_COOKIE[ $cookie_timeout_name ] ) ) ? $_COOKIE[ $cookie_name ] : 1;

					    $content .= '<script>( window.advanced_ads_ready || jQuery( document ).ready ).call( null, function() {'
						    . 'if( advads.get_cookie( "' . $cookie_timeout_name . '" ) ) {'
						    . 'if( advads.get_cookie( "' . $cookie_name . '" ) ) {'
						    . 'var ' . $cookie_name . ' = parseInt( advads.get_cookie( "' . $cookie_name . '" ) ) + 1;'
						    . '} else { var ' . $cookie_name . ' = 1; }'
						    . '} else {'
						    . 'var ' . $cookie_name . ' = 1;'
						    . 'advads.set_cookie_sec("' . $cookie_timeout_name . '", "true", ' . $timeout . ' );'
						    . '}'
						    . 'advads.set_cookie_sec("' . $cookie_name . '", ' . $cookie_name . ', ' . $timeout . ' );';
					    $content .= '});</script>';
					    break;
				}
			}
		}
		return $content;
	}

	/**
	 * Helper function to the name of a form field.
	 * falls back to default
	 *
	 * @param string $form_name form name if submitted.
	 * @param int    $index index of the condition.
	 *
	 * @return string
	 */
	public static function get_form_name_with_index( $form_name = '', $index = 0 ) {
		// form name basis
		if ( method_exists( 'Advanced_Ads_Visitor_Conditions', 'get_form_name_with_index' ) ) {
			return Advanced_Ads_Visitor_Conditions::get_form_name_with_index( $form_name, $index );
		} else {
			return Advanced_Ads_Visitor_Conditions::FORM_NAME . '[' . $index . ']';
		}
	}

	/**
	 * Replace operator name to ensure backward compatibility.
	 *
	 * @param string $operator Operator name.
	 * @return string $operator Operator name.
	 */
	private static function maybe_replace_cookie_operator( $operator ) {
		$replace = [
			'show' => 'match',
			'hide' => 'match_not'
		];
		return isset( $replace[ $operator ] ) ? $replace[ $operator ] : $operator;
	}

	/**
	 * Extract cookie data from a stringified cookie.
	 *
	 * @param string $cookie {
	 *     A stringified cookie.
	 *
	 *     @type string $data Cookie data.
	 *     @type string $expire Expiration time.
	 * }
	 * @return mixed The data field on success, original stringified cookie on error.
	 */
	private static function extract_cookie_data( $cookie ) {
		$cookie_array = json_decode( wp_unslash( $cookie ), true );

		if (
			! is_array( $cookie_array )
			|| ! array_key_exists( 'data', $cookie_array )
		) {
			return $cookie;
		}

		return $cookie_array['data'];
	}
}
