<?php
/**
 * Формирует блок метатегов для поисковой оптимизации.
 * 
 * Один из основных инструментов SEO.
 * Первоначально проверяется существование дополнительного поля записи под соответствующим номером.
 * Если записи не существует - содержание тега формируется из подручных средств.
 *
 *
 * @param object $semantic_kernel
 *
 *
 * @author veselka.ua
 * @version 2.0
 *
 * @package veselka_ua/themes
 */

class SeoMeta {

	/**
	 * Объект семантического ядра.
	 * @type object
	 */
	private $semantic_kernel;
	/**
	 * Дополнительные параметры SEO модуля.
	 * @type array
	 */
	private $options = [];
	/**
	 * Свойство для обработки данных в дочернем объекте.
	 * @type array
	 */
	private $seo_data = [];


	public function __construct( &$semantic_kernel ) {

		/**
		 * Объект семантического ядра.
		 */
		if( $semantic_kernel && is_object($semantic_kernel) ) {
			$this->semantic_kernel = $semantic_kernel;
		}

		/**
		 * Дополнительные параметры SEO модуля.
		 * 
		 * Включает в себя:
		 * Описание (Description);
		 * Ключевые слова (Keywords);
		 * Не индексировать (Robots);
		 * Каноническая ссылка (Canonical).
		 */
		$this->options = [
			'meta_description' => false, // Описание страницы.
			'meta_keywords' => false, // Ключевые слова.
			'meta_robots' => false, // Индексировать/не индексировать. Для этого тега нет автогенерации.
			'rel_canonical' => false, // Каноническая ссылка. Заменяет значения по умолчанию. На всех страницах пагинации ссылка на первую страницу.
		];
		// В крючке вызывается метод определенный в родительском объекте
		add_action( 'wp_head', [$this, 'get_data'], 1, 1 );
	}


	/**
	 * Обертка для получения результата работы объекта.
	 * 
	 * Обращение происходит в крючке ворпресс.
	 * Крючек вордпресс должен находится в конструкторе объекта.
	 *
	 * @param void
	 * @return string
	 */
	final public function get_data() {
		echo $this->construct_seo_data();
	}


	/**
	 * Получение результата.
	 *
	 * @param void
	 * @return string
	 */
	private function construct_seo_data() {
		// Текущее состояние семантического ядра и сбор данных
		$this->semantic_kernel->load();
		// Подготовка объекта к работе
		$this->load();
		// Сбор прописанных данных
		$this->seo_collect();
		// Генерация недостающих данных
		$this->seo_generate();
		// HTML оформление SEO данных
		return $this->seo_formalization();
	}


	/**
	 * Подготовка объекта к работе.
	 * 
	 * 1. Заполняет массив $seo_data - индексами метатегов и значениями NULL.
	 * 2. Переопределяет массив $this->options для дальнейшей работы.
	 * 3. Переопределяет массив $this->seo_category для дальнейшей работы. Перечисление в строке заменяется массивом.
	 *
	 * @param void
	 * @return void
	 */
	private function load() {
		foreach( $this->options as $option => $status ) {
			$tag_name = explode('_', $option);
			$type = $tag_name[0];
			$tag_name = $tag_name[1];
			// Получение параметра
			$this->options[$option] = (bool)get_option('metatags_'.$tag_name);
			if( $this->options[$option] ) {
				// Замена значений и индексов свойств на типы мета-данных.
				unset($this->options[$option]);
				$this->options[$tag_name] = $type;
				// Заполнение результирующего массива пустыми значениями.
				$this->seo_data[$tag_name] = NULL;
			}
		}
	}


	/**
	 * Поиск прописанных мета-тегов в дополнительных полях.
	 *
	 * @param void
	 * @return void
	 */
	private function seo_collect() {
		// Формирование имени метода в зависимости от состояния системы.
		$method_name = $this->semantic_kernel->registry['current_mode'] . '_meta_collect';
		// Вызов соответствующего метода для сбора прописанных метданнх.
		if( method_exists( $this, $method_name ) ) {
			$this->{$method_name}();
		}
	}


	/**
	 * Сбор данных произвольных полей страницы.
	 * -- Вспомогательный метод. --
	 *
	 * @param void
	 * @return void
	 */
	private function page_meta_collect() {
		// Поиск соотвествующих метатегов в дополнительных полях записи.	
		foreach( $this->seo_data as $key => $value) {
			$meta_tag = get_post_meta( $this->semantic_kernel->registry['id'], $key, true );
			if( $meta_tag ) {
				// Дополнительное поле robots равное 0 означает "закрыто от индексации".
				if( $key == 'robots' && $meta_tag === 0 ) {
					$meta_tag = "noindex,nofollow";
				}
				$this->seo_data[$key] = $meta_tag;
			}
		}
	}


	/**
	 * Сбор данных произвольных полей записи.
	 * -- Вспомогательный метод. --
	 *
	 * @param void
	 * @return void
	 */
	private function single_meta_collect() {
		$this->page_meta_collect();
	}


	/**
	 * Сбор данных произвольных полей категории.
	 * -- Вспомогательный метод. --
	 *
	 * @param void
	 * @return void
	 */
	private function category_meta_collect() {
		// Объект категории
		$term_id = $this->semantic_kernel->registry['id'];
		$this->taxonomy_meta_collect($term_id);
	}


	/**
	 * Сбор данных произвольных полей недвижимости.
	 * -- Вспомогательный метод. --
	 *
	 * @param void
	 * @return void
	 */
	private function realty_meta_collect() {
		// Объект недвижимости
		$term_id = $this->semantic_kernel->registry['id'];
		$this->taxonomy_meta_collect($term_id);
	}


	/**
	 * Сбор данных произвольных полей товаров.
	 * -- Вспомогательный метод. --
	 *
	 * @param void
	 * @return void
	 */
	private function catalog_meta_collect() {
		// Объект недвижимости
		$term_id = $this->semantic_kernel->registry['id'];
		$this->taxonomy_meta_collect($term_id);
	}


	/**
	 * Сбор данных произвольных полей таксономии.
	 * Общий метод для всех таксономий.
	 * -- Вспомогательный метод. --
	 *
	 * @param int $term_id
	 * @return void
	 */
	private function taxonomy_meta_collect($term_id) {
		// Мета данные
		$tax_data = get_term_meta( $term_id );
		// Поиск соотвествующих метатегов в дополнительных полях категории.		
		foreach( $this->seo_data as $key => $value) {
			if( isset($tax_data['cat_' . $key][0]) && $tax_data['cat_' . $key][0] !== '' ) {
				$tax_data_item = $tax_data['cat_' . $key][0];
				// Дополнительное поле robots равное 0 означает "закрыто от индексации".
				if( $key == 'robots' && $tax_data_item === 0 ) {
					$tax_data_item = "noindex,nofollow";
				}
				$this->seo_data[$key] = $tax_data_item;
			}
		}

	}


	/**
	 * Генерация SEO тегов.
	 *
	 * @param void
	 * @return void
	 */
	private function seo_generate() {
		foreach( $this->seo_data as $key => $value ) {
			$method_name = $key . '_generate';
			if( $value == NULL && method_exists( $this, $method_name ) ) {
				// формирование HTML кода мета-тега.
				$this->{$method_name}();
			}
		}
	}


	/**
	 * Генерация description.
	 * -- Вспомогательный метод. --
	 *
	 * @param void
	 * @return void
	 */
	private function description_generate() {
		// Генератор вынесен в общий объект
		$this->seo_data['description'] = $this->semantic_kernel->generator_load('metatags');
	}


	/**
	 * Генерация keywords.
	 * -- Вспомогательный метод. --
	 *
	 * @param void
	 * @return void
	 */
	private function keywords_generate() {
		if( $this->semantic_kernel->registry['mode'] != 'other' ) {
			$this->seo_data['keywords'] = $this->semantic_kernel->registry['title'];
			if( $this->semantic_kernel->registry['h1'] && $this->semantic_kernel->registry['h1'] != $this->semantic_kernel->registry['title'] ) {
				$this->seo_data['keywords'] .= ',' . $this->semantic_kernel->registry['h1'];
			}
			if( isset($this->semantic_kernel->registry['ancestors'])  ) {
				foreach( $this->semantic_kernel->registry['ancestors'] as $key => $ancestor ) {
					$this->seo_data['keywords'] .= ',' . $ancestor['title'];
				}
			}
			if( isset($this->semantic_kernel->registry['tags']) ) {
				foreach( $this->semantic_kernel->registry['tags'] as $key => $tag_name ) {
					$this->seo_data['keywords'] .= ',' . $tag_name;
				}
			}
		}
	}


	/**
	 * Генерация robots.
	 * -- Вспомогательный метод. --
	 *
	 * @param void
	 * @return void
	 */
	private function robots_generate() {

		if( $this->semantic_kernel->registry['mode'] != 'other' ) {
			// Если установлено в опциях не индексировать текущий архив
			if( isset($this->semantic_kernel->registry['archive']) ) {
				if( in_array( $this->semantic_kernel->registry['archive'], ['day', 'month', 'year'] ) ) {
					$noindex = (bool)get_option('seotaxonomy_noindex_date');
				} else {
					$noindex = (bool)get_option('seotaxonomy_noindex_' . $this->semantic_kernel->registry['archive']);
				}
				// noindex
				if( isset($noindex) && $noindex ) {
					$this->seo_data['robots'] = "noindex,follow";
				}
			}

			// Если установлено в опциях не индексировать пагинацию текущего архива
			$noindex = (bool)get_option('seotaxonomy_noindex_paged_tax');
			$paged = isset($this->semantic_kernel->registry['paged'])?true:false;
			if( $paged && $noindex ) {
				$this->seo_data['robots'] = "noindex,follow";
			}
		} else {
			$this->seo_data['robots'] = "noindex,follow";
		}

	}


	/**
	 * Генерация canonical.
	 * -- Вспомогательный метод. --
	 *
	 * @param void
	 * @return void
	 */
	private function canonical_generate() {
		if( $this->semantic_kernel->registry['mode'] != 'other' ) {
			if( !(bool)get_option('seotaxonomy_identical_canonical') && 
				$this->semantic_kernel->registry['archive'] && 
				$this->semantic_kernel->registry['paged'] && 
				$this->semantic_kernel->registry['paged'] > 1 
			) {
				$this->semantic_kernel->registry['link'] = trailingslashit( $this->semantic_kernel->registry['link'] ) . "page/" . $this->semantic_kernel->registry['paged'];
				$this->seo_data['canonical'] = user_trailingslashit( $this->semantic_kernel->registry['link'], 'paged' );
			} elseif( $this->seo_data['canonical'] == '' ) {
				$this->seo_data['canonical'] = $this->semantic_kernel->registry['link'];
			}
		}
	}


	/**
	 * Формирование HTML кода мета-тегов.
	 *
	 * @param void
	 * @return string
	 */
	private function seo_formalization() {
		$tag['meta']['before'] = '	<meta name="';
		$tag['meta']['middle'] = '" content="';
		$tag['meta']['after'] = '" />' . "\n";
		$tag['rel']['before'] = '	<link rel="';
		$tag['rel']['middle'] = '" href="';
		$tag['rel']['after'] = '" />' . "\n";

		$result_set = '';
		foreach( $this->seo_data as $key => $value ) {
			if( $value ) {
				// формирование HTML кода мета-тега.
				$result_set .= $tag[$this->options[$key]]['before'] . $key . $tag[$this->options[$key]]['middle'] . $value . $tag[$this->options[$key]]['after'];
			}
		}
		// Комментарий (маркер) если результат не пустой.
		if ($result_set != '') {
			// Убирает стандартный тег <link rel='canonical' href='' /> из заголовка если параметр активен.
			if( is_string( $this->seo_data['canonical'] ) ) {
				remove_action( 'wp_head', 'rel_canonical' );
			}
			// Убирает стандартный тег <meta name='robots' content='' /> из заголовка если параметр активен.
			if( is_string( $this->seo_data['robots'] ) ) {
				remove_action( 'wp_head', 'noindex', 1 );
			}
			return "\n" . '	<!-- SEO -->' . "\n" . $result_set;
		}

	}

} //end SeoMeta
?>