<?php
	/**
	 * The file contains a base class for all shortcodes.
	 *
	 * @author Alex Kovalev <alex.kovalevv@gmail.com>
	 * @copyright (c) 2018, Webcraftic Ltd
	 *
	 * @package core
	 * @since 1.0.0
	 */
	
	// Exit if accessed directly
	if( !defined('ABSPATH') ) {
		exit;
	}
	
	if( !class_exists('Wbcr_FactoryShortcodes324_Shortcode') ) {
		
		/**
		 * The base class for all shortcodes.
		 *
		 * @since 1.0.0
		 */
		abstract class Wbcr_FactoryShortcodes324_Shortcode {
			
			private static $meta_key_shorcode_assets_for_posts = 'factory_shortcodes_assets';
			
			/**
			 * Shortcode name.
			 *
			 * @since 1.0.0
			 * @var string
			 */
			public $shortcode_name = null;
			
			/**
			 * If true, the assets methods will be called in header.
			 *
			 * @since 3.0.0
			 * @var boolean
			 */
			public $assets_in_header = false;
			
			/**
			 * A manager that created and track this shortcode.
			 * @since 1.0.0
			 * @var Wbcr_FactoryShortcodes324_ShortcodeManager
			 */
			private $manager;
			
			/**
			 * Scripts to include on the same page.
			 *
			 * @since 1.0.0
			 * @var Wbcr_Factory404_ScriptList
			 */
			public $scripts;
			
			/**
			 * Styles to include on the same page.
			 *
			 * @since 1.0.0
			 * @var Wbcr_Factory404_StyleList
			 */
			public $styles;
			
			/**
			 * If set true, this shortcode will be tracked on altering post content.
			 *
			 * When this shortcode will be found in a post content, the method onTrack will be fired.
			 *
			 * @since 1.0.0
			 * @var bool
			 */
			public $track = false;
			
			/**
			 * If true, it means that shortcodes assets have been conected already.
			 *
			 * @since 1.0.0
			 * @var bool
			 */
			protected $connected = false;
			
			/**
			 * /**
			 * Creates a new instance of a shortcode objects.
			 *
			 * @since 1.0.0
			 * @param Wbcr_Factory404_Plugin $plugin
			 */
			public function __construct($plugin)
			{
				$this->plugin = $plugin;
				
				$this->scripts = $this->plugin->newScriptList();
				$this->styles = $this->plugin->newStyleList();
				
				if( !is_array($this->shortcode_name) ) {
					$this->shortcode_name = array($this->shortcode_name);
				}
				
				if( $this->assets_in_header ) {
					add_action('wp_enqueue_scripts', array($this, 'actionEnqueueScripts'));
				}
				
				if( is_admin() ) {
					add_action('save_post', array($this, 'actionSavePost'));
				}
			}
			
			/**
			 * Adds shortcode scripts and styles of it's nedded.
			 *
			 * Calls on the hook "wp_enqueue_scripts".
			 *
			 * @since 1.0.0
			 * @global Wp_post $post
			 * @return void
			 */
			public function actionEnqueueScripts()
			{
				global $post;
				if( empty($post) ) {
					return;
				}
				
				foreach($this->shortcode_name as $shortcode_name) {
					if( $this->connected ) {
						return;
					}
					
					$metaValue = get_post_meta($post->ID, self::$meta_key_shorcode_assets_for_posts, true);

					if( !isset($metaValue[$shortcode_name]) ) {
						continue;
					}
					
					$result = $this->assets($metaValue[$shortcode_name], false, true);

					if( !$result ) {
						continue;
					}
					
					$this->scripts->connect();
					$this->styles->connect();
					
					$this->connected = true;
				}
			}
			
			/**
			 * Adds shortcode scripts and styles of it's nedded.
			 *
			 * Calls on the hook "save_post".
			 *
			 * @return void|int
			 */
			public function actionSavePost($post_id)
			{
				if( wp_is_post_revision($post_id) ) {
					return $post_id;
				}
				
				$post = get_post($post_id);

				if( empty($post) ) {
					return;
				}
				
				$this->onPostSave($post);

				if( $this->track ) {
					$this->trackShortcode($post);
				}
			}
			
			/**
			 * Checks if a post contains a given shortcode and extract its atrributes and content on post saving.
			 *
			 * @since 1.0.0
			 * @param object $post A current post.
			 * @return void
			 */
			private function trackShortcode($post)
			{
				if( empty($this->shortcode_name) ) {
					return;
				}
				
				$matches = array();
				
				$shortcodes = $this->shortcode_name;
				
				if( !is_array($shortcodes) ) {
					$shortcodes = array($this->shortcode_name);
				}
				
				$tagregexp = join('|', $shortcodes);
				
				$start = '(\[(' . $tagregexp . ')([^\[\]]*)\])';
				$end = '\[\/\2\]';
				$pattern = '/' . $start . '(.*?)' . $end . '/is';
				
				$count = preg_match_all($pattern, $post->post_content, $matches, PREG_SET_ORDER);
				
				$found_shortcodes = get_post_meta($post->ID, self::$meta_key_shorcode_assets_for_posts, true);
				
				if( !is_array($found_shortcodes) ) {
					$found_shortcodes = array();
				}
				
				foreach($shortcodes as $shortcode)
					unset($found_shortcodes[$shortcode]);
				
				if( !$count ) {
					update_post_meta($post->ID, self::$meta_key_shorcode_assets_for_posts, $found_shortcodes);
					
					return;
				}
				
				// clears info about previously existing shortcodes
				
				foreach($matches as $order => $match) {
					
					$shortcode = $match[2];
					$attrContent = str_replace('\\', '', $match[3]);
					$innerContent = str_replace('\\', '', $match[4]);
					
					$attrs = shortcode_parse_atts($attrContent);
					
					if( !isset($found_shortcodes[$shortcode]) ) {
						$found_shortcodes[$shortcode] = array();
					}
					$found_shortcodes[$shortcode][] = $attrs;
					
					// allows to perfome custom actions
					
					do_action('factory_shortcode_found', $shortcode, $attrs, $innerContent);
					
					$this->onTrack($shortcode, $attrs, $innerContent, $post->ID);
				}
				
				// saves info about existing shortcodes for a given post
				
				delete_post_meta($post->ID, self::$meta_key_shorcode_assets_for_posts);
				update_post_meta($post->ID, self::$meta_key_shorcode_assets_for_posts, $found_shortcodes);
			}
			
			/**
			 * Returns a shortcode html markup.
			 *
			 * @since 1.0.0
			 * @return string
			 */
			public function render($attr, $content)
			{
				
				if( !$this->connected ) {
					$this->assets(array($attr), true, false);
					$this->scripts->connect(true);
					$this->styles->connect(true);
				}
				
				// fix for compability
				
				global $post;
				if( !empty($post) && !empty($this->shortcode_name) ) {
					$found_shortcodes = get_post_meta($post->ID, self::$meta_key_shorcode_assets_for_posts, true);

					if( $found_shortcodes && !is_array($found_shortcodes) ) {
						
						$shortcodes = $this->shortcode_name;
						if( !is_array($shortcodes) ) {
							$shortcodes = array($this->shortcode_name);
						}
						
						$found_shortcodes = array();
						$found_shortcodes[$shortcodes[0]] = array();
						$found_shortcodes[$shortcodes[0]][] = $attr;

						update_post_meta($post->ID, self::$meta_key_shorcode_assets_for_posts, $found_shortcodes);
					}
				}
				
				ob_start();
				$this->html($attr, $content);
				$html = ob_get_clean();
				
				return $html;
			}
			
			/**
			 * Configures assets (js and css) for the shortcodes.
			 *
			 * The method should be overwritten in a deferred class.
			 *
			 * @since 1.0.0
			 * @return void
			 */
			public function assets()
			{
			}
			

			public function onPostSave($post)
			{
			}
			
			public function onTrack($shortcode, $attrs, $innerContent, $postId)
			{
			}

			/**
			 * Renders shortcode html.
			 * @since 1.0.0
			 * @return void
			 */
			public abstract function html($attr, $content);
		}
	}