<?php
/**
 * Please see wp-cookie-allow.php for more details.
 *
 * @author $Vincent Weber <weepie-plugins@outlook.com>$
 */

use WpieFw\Helpers\WpieMiscHelper;
use WpieFw\Helpers\WpieAjaxHelper;
use WpieFw\Templates\WpieTemplate;
use WpieFw\Exceptions\WpieExceptionLogger;
use WpieFw\Templates\Files\WpieTemplatesFileFinder;

if( ! defined( 'ABSPATH' ) ) exit;

if( !class_exists( 'WpcaAutomateReplace' ) ) {

	class WpcaModuleAutomateReplace extends WpieFw\Modules\Iterator\WpieModule {

		/**
		 * Template file name for the replacement html
		 *
		 * @since 2.3
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_REPLACE_ELEM = 'wpca-replacement-element.php';


		/**
		 * File name for the replace css
		 *
		 * @since 2.3
		 *
		 * @var string
		 */
		const FILE_NAME_REPLACE_CSS = 'wpca-automate-replace';


		/**
		 * File name for the blocked js src attribute
		 *
		 * @since 3.2.3
		 *
		 * @var string
		 */
		const FILE_NAME_BLOCKED_JS = 'blocked.js';


		/**
		 * File name for the blocked iFrame src attribute
		 *
		 * @since 3.2.3
		 *
		 * @var string
		 */
		const FILE_NAME_BLOCKED_IFRAME = 'about:blank';


		/**
		 * File name for the blocked img src attribute
		 *
		 * @since 3.2.3
		 *
		 * @var string
		 */
		const FILE_NAME_BLOCKED_IMG = 'blocked.jpg';

		/**
		 * HTML node names in the body that shouldnt have a replacement element
		 *
		 * @since 3.2
		 *
		 * @var array
		 */
		private $nodeNamesBodyNoReplacementElement = array( 'script', 'img' );


		/**
		 * Constructor
		 *
		 * @acces public
		 *
		 * @since 2.3
		 */
		public function __construct() {}


		/* (non-PHPdoc)
		 * @see WpiePluginModule::start()
		 */
		public function start()	{}


		/**
		 * Get HTML for the replacement block
		 *
		 * @access public
		 *
		 * @param string $txt
		 * @param string $type the 3rd party
		 * @param boolean $forceAcceptBtn, add the accept button if not present
		 * @param string $cc
		 *
		 * @uses WpieTemplate
		 * @uses do_shortcode()
		 *
		 * @since 2.3
		 *
		 * @return string
		 */
		public function getElement( $txt = '', $type = '', $forceAcceptBtn = false, $cc = '' )
		{
			try {
				$finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_REPLACE_ELEM, false );
				$block = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_REPLACE_ELEM );
			} catch( \Throwable $e ) {
				// log only when logged in
				WpieExceptionLogger::log( $e, true );
				// on frontend do not show message
				return '';
			}

			$txt = ( '' === $txt ) ? $this->settingsProcessor->get( 'content' )->get( 'content_automate_replace_txt' ) : $txt;
			$bgdColor = $this->settingsProcessor->get( 'style' )->get( 'style_placeholder_bgd_color' );
			$hideLogo = $this->settingsProcessor->get( 'content' )->get( 'content_automate_replace_hide_logo' );

			if ( WpieMiscHelper::isValidHexColor( "#$bgdColor") && false === strpos( $bgdColor , '#') ) {
				$bgdColor = "#$bgdColor";
			}

			if( $hasShortcode = has_shortcode( $txt, 'wpca_btn_accept' ) ) {
				$txt = do_shortcode( $txt );
			}
			if( $hasShortcode = has_shortcode( $txt, 'wpca_btn_decline' ) ) {
				$txt = do_shortcode( $txt );
			}
			if( $hasShortcode = has_shortcode( $txt, 'wpca_btn_accept_cc' ) ) {
			    $txt = do_shortcode( $txt );
			}
			if( $hasShortcode = has_shortcode( $txt, 'wpca_btn_cc_settings' ) ) {
			    $txt = do_shortcode( $txt );
			}

			if( !$hasShortcode && $forceAcceptBtn ) {
				$btnAcceptTxt = $this->settingsProcessor->get( 'content' )->get( 'content_button_accept_txt' );
				$txt .= sprintf( '[wpca_btn_accept txt="%s" cc="%s"]', $btnAcceptTxt, $cc );
				$txt = do_shortcode( $txt );
			}

			// ensure allowed html
			$txt = wp_kses( $txt, wp_kses_allowed_html( WpcaModuleFrontend::KSES_FRONTEND_CONTEXT ) );

			$class  = 'wpca-replacement-elem';
			$class .= ( '' !== $type ) ? ' wpca-replace-'.$type : '';
			$class .= ( '' !== $cc )   ? ' wpca-replace-'.$cc   : '';

			$block->setVar( 'class', $class );
			$block->setVar( 'img_uri', $this->globals->get( 'imgUri' ) );
			$block->setVar( 'txt', $txt );
			$block->setVar( 'bgd_color', $bgdColor );
			$block->setVar( 'hide_logo', $hideLogo );
			$block->setVar( 'type', $type );
			$block->setVar( 'cc', $cc );
			$block->setVar( 'plugin_name', $this->globals->get( 'pluginName' ) );
			// In case users use the %CC% or {CC} tag in the placeholder HTML, replace it
			$block->setTag( 'CC', $cc );

			return $block->render( false, true );
		}


		/**
		 * Callback for the wpca_styles_frontend hook
		 *
		 * Registers styles for the replacement blocks
		 *
		 * @access public
		 *
		 * @param array	$styles
		 *
		 * @since 2.3
		 */
		public function setStyles( $styles )
		{
			$relModulePath = parent::getIndex() . '/css/' . self::FILE_NAME_REPLACE_CSS;
			$path = $this->globals->get( 'moduleUri' ) . '/' . $relModulePath;

			$style = new stdClass();
			$style->path = $path;

			$styles[] = $style;

			return $styles;			
		}

		/**
		 * Callback for the wpca_script_frontend_vars_before hook
		 *
		 * @access public
		 *
		 * @param array $vars
		 *
		 * @since 3.2.3
		 * @since 3.4.5 renamed to setScriptVars
		 *
		 * @return array
		 */
		public function setScriptVars( $vars )
		{
			$settingsContent = $this->settingsProcessor->get( 'content' );
			$settingsStyle = $this->settingsProcessor->get( 'style' );
			$assetsUri = $this->globals->get('assetsUri') . '/';

			$vars['doPlaceholder'] = $settingsContent->get( 'content_automate_replace_enable' );
			$vars['doPlaceholderParentSizing'] = $settingsStyle->get( 'style_placeholder_parent_sizing' );
			$vars['minPlacehoderDim'] = array(
					'w' => (int)$settingsStyle->get( 'style_placeholder_min_w' ),
					'h' => (int)$settingsStyle->get( 'style_placeholder_min_h' )
			);
			$vars['blockedAssetsUri'] = array(
					'a' => '#',
					'js' => $assetsUri.'js/'.self::FILE_NAME_BLOCKED_JS,
					'img' => $assetsUri.'img/'.self::FILE_NAME_BLOCKED_IMG,
					'iframe' => self::FILE_NAME_BLOCKED_IFRAME
			);

			unset( $settingsContent );

			return $vars;
		}

		/**
		 * Mark the matched HTML
		 *
		 * @param string $type
		 * @param string $cc
		 * @param string $match
		 * @param array $matches
		 *
		 * @since 3.2.3
		 *
		 * @return string
		 */
		public function mark( $text = '', $type = '', $context = '', $match = '', $cc = '', $matches = [], $currentData = [] )
		{
			if( '' === $type ) {
				$type = __( '3rd party', 'wpca' );
			}

			$_match = $match;
			$markHtml = '';
			$hasElStart = ( isset( $matches['EL_START'] ) );
			$nodeName = ( $hasElStart ) ? ltrim( $matches['EL_START'], '<' ) : '';

			if( $hasElStart ) {
				$isScript = ( 'script' === $nodeName );
				$isIframe = ( 'iframe' === $nodeName );
				$isImg = ( 'img' === $nodeName );
				$isLink = ( 'a' === $nodeName );
				$src = false;
				$hasDep = false;

				$forcePlaceholder = ( isset( $matches['FP_1'] )  || isset( $matches['FP_0'] ) );
				$forcePlaceholderTrue = isset( $matches['FP_1'] );
				$forcePlaceholderFalse = isset( $matches['FP_0'] );

				// remove type="text/javascript"
				if( $isScript ) {
					if( false !== strpos( $_match, 'type="text/javascript"') ) {
						$_match = str_replace( 'type="text/javascript"', 'type="text/template"', $_match );
					} elseif( false !== strpos( $_match, 'type=\'text/javascript\'') ) {
						$_match = str_replace( 'type=\'text/javascript\'', 'type=\'text/template\'', $_match );
					} else {
						$_match = str_replace( '<script', '<script type="text/template"', $_match );
					}

					// check for dependency info
					$depInfo = array_filter( $matches, function( $k ) {
						return 0 === strpos( $k, 'DEPJS' );
					}, ARRAY_FILTER_USE_KEY );

					if( count( $depInfo ) > 0 ) {
						$depInfoKeys = array_keys( $depInfo );
						$depInfoKey = array_shift( $depInfoKeys );
						$isJsNeedleDep = 0 === strpos( $depInfoKey, 'DEPJSN_' );
						$pos = (int)strstr( strrev( $depInfoKey ), '_', true );
						$currentDataKey = $isJsNeedleDep ? 'js_needle' : 'js';

						if( $currentData[$currentDataKey][$pos] ) {
							$hasDep = true;
							$depData = [];
							$depData['name'] = $currentData[$currentDataKey][$pos]['dep'];
							$depData['dt'] = $currentData[$currentDataKey][$pos]['dt'];
							$depData['r'] = $currentData[$currentDataKey][$pos]['r'];
						}
					}
				}

				// get the source attribute
				if( isset( $matches['EL_SRC'] ) ) {
					$src = $matches['EL_SRC'];
				} elseif( $isImg && isset( $matches['ATTR_V'] ) ) {
					$src = $matches['ATTR_V'];
				} elseif( $isLink && isset( $matches['ATTR_V'] ) ) {
					$src = $matches['ATTR_V'];
				}

				$data = array();
				$data['id'] = 0;
				$data['cc'] = $cc;
				$data['txt'] = $text;
				$data['type'] = $type;
				$data['context'] = $context;
				$data['blocked'] = 1;
				$data['placeholder'] = false;
				$data['nodeName'] = $nodeName;
				if( $hasDep ) {
					$data['dep'] = $depData;
				}

                // capture the srcset attribute for images
                if( $isImg && false !== strpos($_match, 'srcset')) {
                    $pattern = '#' .
                               'srcset=' .
                                 '(?P<Q>"|\')'.
                                   '('.
                                     '?P<SRCSET_V>[^Q]*?'.
                                   ')'.
                                 '(?P=Q)'.
                               '#';

                    if( preg_match( $pattern, $_match, $m ) ) {
                        $srcset = $m['SRCSET_V'];
                        $data['srcset'] = $srcset;
                        $_match = str_replace( $srcset, '', $_match );
                    }
				}

				// capture the href attribute for hyperlinks
				// if the match was already made for the href, skip this step
                if( $isLink && false !== strpos($_match, 'href')) {
					if( false === strpos( $_match, 'href="' . $src ) &&
					    false === strpos( $_match, "href='$src" ) ) {
						$pattern = '#' .
								'href=' .
									'(?P<Q>"|\')'.
									'('.
										'?P<HREF_V>[^Q]*?'.
									')'.
									'(?P=Q)'.
								'#';

						if( preg_match( $pattern, $_match, $m ) ) {
							$src = $m['HREF_V'];
					}
					}
                }

				/**
				 * Replace iframe node if requested
				 *
				 * @param string $iframeReplaceNode
				 * @since 3.3
				 * @return string
				 */
				$iframeReplaceNode = apply_filters( 'wpca_automate_replace_iframe_tag', 'iframe' );
				if( $isIframe && 'iframe' !== $iframeReplaceNode ) {
					$nodeName = $iframeReplaceNode;
					$_match = str_replace( '<iframe', "<$nodeName", $_match );
					$_match = str_replace( '</iframe>', "</$nodeName>", $_match );
				}

				$markHtml .= ' data-wpca-marked="1" data-wpca-marked-auto="1"';

				if( $src ) {
					$_src = $src;
					$srcBlocked = $this->globals->get('assetsUri') . '/';
					if( $isScript ) {
						$srcBlocked .= 'js/'.self::FILE_NAME_BLOCKED_JS;
					} elseif( $isIframe ) {
						$srcBlocked = self::FILE_NAME_BLOCKED_IFRAME;
					} elseif( $isImg ) {
						$srcBlocked .= 'img/'.self::FILE_NAME_BLOCKED_IMG;
					} elseif( $isLink ) {
						$srcBlocked = '#';
					} else {
						$srcBlocked = '';
					}

					/**
					 * Flag to fix URL's with &amp; resulting in &amp;amp
					 * 
					 * Some API URL's have the "&" passed in decoded, encode back to avoid &amp;amp
					 * 
					 * @since 3.4.9
					 */
					$fixAmp = apply_filters( 'wpca_fix_src_ampersand', true, $type, $src );
					if( $fixAmp && strpos( $src, '&amp;' ) !== false ) {
						$src = str_replace( '&amp;', '&', $src );
					}					

					$data['src'] = $src;
					$_match = str_replace( $_src, $srcBlocked, $_match );
				}

				if( 'body' === $context ) {
					$data['placeholder'] = true;

					/**
					 * Let others filter the node names
					 *
					 * @param array $nodeNamesBodyNoReplacementElement
					 * @param string $type
					 *
					 * @since 3.2
					 */
					$nodeNamesBodyNoReplacementElement = apply_filters( 'wpca_node_names_body_no_replacement_element', $this->nodeNamesBodyNoReplacementElement, $type );

					if( count( $nodeNamesBodyNoReplacementElement ) && preg_match( '#^('.join( '|', $nodeNamesBodyNoReplacementElement ).')#', substr( trim( $match ), 1 ) ) ) {
						$data['placeholder'] = false;
					}

					if( $forcePlaceholder && $forcePlaceholderTrue ) {
						$data['placeholder'] = true;
					} elseif( $forcePlaceholder && $forcePlaceholderFalse ) {
						$data['placeholder'] = false;
					}
				}

				$markHtml .= sprintf( " data-wpca-marked-data='%s'", htmlspecialchars( str_replace( '&#038;', '&', wp_json_encode( $data ) ), ENT_QUOTES, 'UTF-8' ) );
				$markHtml = str_replace( '<'.$nodeName, '<'.$nodeName.$markHtml.' ', $_match );
			}

			return  $markHtml . '<!--[wpca_mrkd]-->';
		}

		/**
		 * Callback for wpca_validated_data hook
		 *
		 * Validate the form fields for the replacement module
		 *
		 * @access public
		 *
		 * @param array $data
		 * @param string $page
		 * @param bool	$hasWarnings
		 *
		 * @since 2.3
		 *
		 * @param array $data
		 */
		public function validate( $data = array(), $page = '', $hasWarnings = false )
		{
			if( $page === $this->settingsProcessor->getName( 'style' ) )
			{
				if( isset( $data['style_placeholder_bgd_color'] ) ) {
					$color = $data['style_placeholder_bgd_color'];
					if ( WpieMiscHelper::isValidHexColor( "#$color") && false === strpos( $color , '#') ) {
						$data['style_placeholder_bgd_color'] = "#$color";
					}
				}
			}

			return $data;
		}


		/**
		 * Callback for the wpca_settings_skip_fields hook
		 *
		 * Skip the "content_automate_replace_enable" setting when satinizing
		 *
		 * @acces public
		 *
		 * @param array	$skip
		 * @param string $setting
		 *
		 * @since 2.3
		 *
		 * @return array the filtered array
		 */
		public function skipSatinizing( $skip, $setting )
		{
			if( $this->settingsProcessor->getName( 'content' ) !== $setting )
				return $skip;

			$skip[] = 'content_automate_replace_enable';

			return $skip;
		}


		/**
		 * Callback for the wpca_update_settings hook
		 *
		 * @access public
		 *
		 * @param array $updates
		 * @param array $data
		 * @param string $setting the current settings DB name
		 *
		 * @since 2.3
		 *
		 * @return array with updated setting form fields
		 */
		public function updateSetting( $updates, $data, $setting )
		{
			if( $this->settingsProcessor->getName( 'style' ) === $setting ) {
				if( isset( $data['style_placeholder_bgd_color'] ) ) {
					$updates['style_placeholder_bgd_color'] = $data['style_placeholder_bgd_color'];
				}
			} elseif( $this->settingsProcessor->getName( 'content' ) === $setting ) {
				$updates['content_automate_replace_enable'] = ( isset( $data['content_automate_replace_enable'] ) ) ? true : false;

				if( isset( $data['content_automate_replace_txt'] ) ) {
					$updates['content_automate_replace_txt'] = $data['content_automate_replace_txt'];
				}
			}

			return $updates;
		}

		/**
		 * Callback for the wp_footer hook
		 *
		 * Add the placeholder HTML
		 *
		 * @uses self::getElement()
		 *
		 * @since 3.2.3
		 */
		public function addHtml( $html )
		{
			$placeHolder = $this->getElement( '', '%TYPE%', false, '%CC%' );
			$html .= "<template id='wpca-placeholer-html'>$placeHolder</template>";

			return $html;
		}

		/**
		 * Init hooks for frontend AJAX calls
		 *
		 * @since 3.4.4
		 */
		public function initAjaxHooksFrontend()
		{
			add_action( 'wpca_frontend_html', array( $this, 'addHtml' ), 1 );
		}

		/**
		 * {@inheritDoc}
		 * @see \WpieFw\Modules\Iterator\WpieModuleInterface::init()
		 *
		 * @since 2.3
		 */
		public function init()
		{
			// return to prevent fatal errors
			// @see https://github.com/webRtistik/wp-cookie-allow/issues/79
			if( false === ( $moduleCore = $this->getModule( 'core' ) ) ) {
				return;
			}

			/** @var WpcaModuleCore $moduleCore */
			if( is_admin() && !WpieAjaxHelper::doingAjax()  ) {
				add_filter( 'wpca_validated_data', array( $this, 'validate' ), 10, 3 );
				add_filter( 'wpca_settings_skip_fields', array( $this, 'skipSatinizing' ), 10, 2 );
				add_filter( 'wpca_update_settings', array( $this, 'updateSetting' ), 10, 3 );
			}

			if( !is_admin() ) {
				if( !defined( 'WPCA_ACTIVE' ) ) {
					return;
				}

				add_action( 'wpca_styles_frontend', array( $this, 'setStyles' ) );
				add_filter( 'wpca_script_frontend_vars_before', array( $this, 'setScriptVars' ) );
				add_filter( 'wpca_replacement_text', array( $this, 'mark' ), 10, 7 );
			}
		}
	}

	if( !function_exists( 'wpca_do_cookies_block' ) )
	{
		/**
		 * Show a replacement block if cookies are not accepted
		 *
		 * @uses WpcaAutomateReplace::getElement() to render the block
		 *
		 * @param string $txt the text to display inside the replacement block
		 * @param string $type
		 * @param boolean $forceAcceptBtn
		 * @param string $cc
		 *
		 * @return bool
		 */
		function wpca_do_cookies_block( $txt = '', $type = '', $forceAcceptBtn = false, $cc = '' )
		{
			if( false !== ( $module = \WpieFw\modules\WpieModuleProcessor::getModule( 'wpca', 'automate-replace' ) ) )
			{
				/** @var WpcaModuleAutomateReplace $module */
				echo $module->getElement( $txt, $type, $forceAcceptBtn, $cc );
				unset( $module );
			}
		}
	}
}