<?php
/**
 * Конструктор сементического ядра.
 * 
 * Формирует список ключевых слов в соответствии с текущей страницей.
 * Определяет тип страницы.
 * 
 * Результаты работы данного класса - полуфабрикат для формирования SEO тегов текущей страницы.
 * Семантическое ядро используется в объектах отвечающих за конкретные SEO функции.
 * 
 * Обязательно использование permalinks.
 *
 *
 * @author veselka.ua
 * @version 2.0
 *
 * @package veselka_ua/themes
 */

class SeoSemantic {


	/**
	 * Регистр - данные необходимые для работы SEO инструментов.
	 * @type array
	 */
	public $registry = [];
	/**
	 * Данные сессий.
	 * @type array
	 */
	public $session = [];
	/**
	 * Данные замен для генераторов метаданных.
	 * @type array
	 */
	private $replacement = [];


	public function __construct() {
		
	}


//////////////////// Публичные методы ////////////////////


	/**
	 * Получение результата.
	 *
	 * @param void
	 * @return void
	 */
	final public function load() {
		if( isset($this->registry['instance']) ) {
			return true;
		}
		// Текущее состояние контроллера и сбор данных
		$this->registry_construct();
		// Запись флага
		$this->registry['instance'] = true;
		return true;
	}




	/**
	 * Обертка для генератора метатегов.
	 *
	 * @param string $type
	 * @return string
	 */
	final public function generator_load($type) {
		return $this->meta_generator($type);
	}


//////////////////// Приватные методы ////////////////////


	/**
	 * Cостояния контроллера, сбор данных.
	 *
	 * @param void
	 * @return void
	 */ 
	private function registry_construct() {
		// Главная
		$this->registry['front_page'] = false;
		if ( is_front_page() ) {
			$this->registry['front_page'] = true;
		}
		// Тип страницы
		if( is_home() ) {
			$this->registry['mode'] = 'home';
		} elseif( is_singular() ) {
			$this->registry['mode'] = 'singular';
		} elseif( is_archive() ) {
			$this->registry['mode'] = 'archive';
		} else {
			$this->registry['mode'] = 'other';
		}

		// Формирование имени метода в зависимости от состояния системы.
		$method_name = $this->registry['mode'] . '_registry';
		// Вызов соответствующего метода для сбора данных.
		if( method_exists( $this, $method_name ) ) {
			$this->{$method_name}();
		}
		$this->registry['current_mode'] = $this->registry[$this->registry['mode']];
		// Поиск страницы с лентой.
		$this->first_node();
		// Генерация H1
		$this->h1_construct();
	}



	/**
	 * Формирование данных для архивных страниц.
	 *
	 * @param void
	 * @return void
	 */
	private function archive_registry() {
		// Если присутствует разбивка ленты на страницы
		if( is_paged() ) {
			$this->registry['paged'] = get_query_var('paged');
		}

		// Категория
		if( is_category() ) {
			$this->registry['archive'] = 'category';
			$this->registry['id'] = get_query_var('cat');
			$cat = get_category( $this->registry['id'] );
			$this->registry['title'] = $cat->cat_name;
			$this->archive_ancestors_list();
			// Текущая ссылка
			$this->registry['link'] = get_category_link( $this->registry['id'] );

		// Тег
		} elseif( is_tag() ) {
			$this->registry['archive'] = 'tag';
			$tag = get_term_by( 'slug', get_query_var('tag'), 'post_tag' );
			$this->registry['id'] = $tag->term_id;
			$this->registry['title'] = $tag->name;
			// Текущая ссылка
			$this->registry['link'] = get_tag_link( $this->registry['id'] );

		// Лента Автора
		} elseif( is_author() ) {
			$this->registry['archive'] = 'author';
			$author = get_user_by( 'slug', get_query_var( 'author_name' ) );
			$this->registry['title'] = $author->display_name;
			$this->registry['id'] = $author->ID;
			// Текущая ссылка
			$this->registry['link'] = get_author_posts_url($author->ID, $author->user_nicename);

		// День
		} elseif( is_day() ) {
			$this->registry['archive'] = 'day';
			$this->registry['title'] = get_the_time('d') . ' ' . get_the_time('F') . ' ' . get_the_time('Y') ;
			$this->registry['crumb'] = get_the_time('d');
			// Предки
			$this->registry['ancestors'][] = array(
				'title' => get_the_time('Y'),
				'link' => get_year_link( get_the_time('Y') ),
			);
			$this->registry['ancestors'][] = array(
				'title' => get_the_time('F'),
				'link' => get_month_link( get_the_time('Y'), get_the_time('m') ),
			);
			// Текущая ссылка
			$this->registry['link'] = get_day_link(
											get_query_var('year'),
											get_query_var('monthnum'),
											get_query_var('day')
										);

		// Месяц
		} elseif( is_month() ) {
			$this->registry['archive'] = 'month';
			$this->registry['title'] = get_the_time('F') . ' ' . get_the_time('Y');
			$this->registry['crumb'] = get_the_time('F');
			// Предок
			$this->registry['ancestors'][] = array(
				'title' => get_the_time('Y'),
				'link' => get_year_link( get_the_time('Y') ),
			);
			// Текущая ссылка
			$this->registry['link'] = get_month_link(
											get_query_var('year'),
											get_query_var('monthnum')
										);

		// Год
		} elseif( is_year() ) {
			$this->registry['archive'] = 'year';
			$this->registry['title'] = get_the_time('Y');
			// Текущая ссылка
			$this->registry['link'] = get_year_link( get_query_var('year') );

		// Таксономия
		} elseif( is_tax() ) {
			$this->registry['archive'] = get_query_var('taxonomy');
			$tax_name = get_query_var( $this->registry['archive'] );
			$tax = get_term_by( 'slug', $tax_name, $this->registry['archive'] );
			//var_dump($tax);
			$this->registry['id'] = $tax->term_id;
			$this->registry['title'] = $tax->name;
			$this->registry['link'] = get_term_link( $tax->term_id, $this->registry['archive'] );
			$this->archive_ancestors_list();

		// Исключение (страница постов с произвольном типом записей - товары)
		} elseif( is_post_type_archive() ) { 
			//var_dump($test);
			$this->registry['archive'] = get_post_type();
			$this->registry['title'] = post_type_archive_title('', false);
			// Текущая ссылка
			$this->registry['link'] = get_post_type_archive_link($this->registry['archive']);
		} else {
			$this->registry['archive'] = 'custom';
			// new in version 4.1 to get all other archive titles
			if( function_exists( 'get_the_archive_title' ) && get_the_archive_title() ) {
				$this->registry['title'] = get_the_archive_title();
			}
		}
	}


	/**
	 * Формирование данных для одиночных страниц.
	 *
	 * @param void
	 * @return void
	 */
	private function singular_registry() {
		// ID
		$this->registry['id'] = get_the_ID();
		// Заголовок
		$this->registry['title'] = get_the_title($this->registry['id']);
		// Текущая ссылка
		$this->registry['link'] = get_permalink($this->registry['id']);
		/*if( $this->registry['front_page'] ) {
			$this->registry['link'] = trailingslashit($this->registry['link']);
		}*/
		// Медиафайл
		if( is_attachment() ) {
			$this->registry['singular'] = 'attachment';

		// Страница
		} elseif( is_page() ) {
			$this->registry['singular'] = 'page';
			$ancestors = get_ancestors( $this->registry['id'], 'page');
			$this->pages_ancestors_list($ancestors);
		// Продукт
		} elseif( is_singular( 'products' ) ) {
			$this->registry['singular'] = 'products';
			// Получение списка родительский категорий
			$this->archive_ancestors_list();
			// Получение списка тегов
			$this->tag_list();
		// Недвижимость
		} elseif( is_singular( 'estate' ) ) {
			$this->registry['singular'] = 'estate';
			// Получение списка родительский категорий
			$this->archive_ancestors_list();
			// Получение списка тегов
			$this->tag_list();
		// Номера
		} elseif( is_singular( 'rooms' ) ) {
			$this->registry['singular'] = 'rooms';
			// Получение списка родительский категорий
			$this->archive_ancestors_list();
			// Получение списка тегов
			$this->tag_list();
		// Запись
		} elseif( is_single() ) {
			$this->registry['singular'] = 'single';
			// Получение списка родительский категорий
			$this->archive_ancestors_list();
			// Получение списка тегов
			$this->tag_list();
		// Исключение
		} else {
			$this->registry['singular'] = 'exception';
		}
	}


	/**
	 * Формирование данных для страницы блога.
	 *
	 * @param void
	 * @return void
	 */
	private function home_registry() {
		$page_id = get_queried_object_id();
		$this->registry['title'] = $this->registry['crumb'] = get_the_title($page_id);
	}


	/**
	 * Формирование данных для одиночных страниц.
	 *
	 * @param void
	 * @return void
	 */
	private function other_registry() {
		if( is_search() ) {
			$this->registry['other'] = 'search';
			$this->registry['title'] = get_search_query();
		} elseif( is_404() ) {
			$this->registry['other'] = '404';
		} else {
			$this->registry['title'] = get_the_title();
			$this->registry['other'] = 'null';
		}
	}


	/**
	 * Формирование данных для архивных записей.
	 *
	 * @param void
	 * @return void
	 */
	private function archive_ancestors_list() {
		if( $this->registry['mode'] == 'singular' ) {
			if( $this->registry['singular'] == 'single' ) {
				$taxonomy = 'category';
			} elseif( $this->registry['singular'] == 'products' ) {
				$taxonomy = 'catalog';
			} elseif( $this->registry['singular'] == 'estate' ) {
				$taxonomy = 'realty';
			} elseif( $this->registry['singular'] == 'rooms' ) {
				$taxonomy = 'hotels';
			} else {
				return false;
			}
			// Получение списка родительских категорий записи
			$tax = get_the_terms($this->registry['id'], $taxonomy);
			if( !is_array($tax) ) {
				return false;
			}
			sort($tax);
			$tax = $tax[0];
			if( !isset($tax) || empty($tax) || $tax == NULL ) {
				return false;
			}
			$archive_parent = $tax->term_id;
		} elseif( $this->registry['mode'] == 'archive' ) {
			//$cat = get_category( $this->registry['id'] );
			$term = get_term_by( 'id', $this->registry['id'], $this->registry['archive'] );
			$archive_parent = $term->parent;
			$taxonomy = $this->registry['archive'];
		}
		$this->load_archive_ancestors_list( $archive_parent, $taxonomy );
	}


	/**
	 * Поиск предков архивных записей.
	 *
	 * @param string(int) $archive_parent
	 * @param string $taxonomy
	 * @return void
	 */
	private function load_archive_ancestors_list( $archive_parent, $taxonomy ) {
		while( $archive_parent > 0 ) {
			$tax = get_term_by( 'id', $archive_parent, $taxonomy );
			$archive['title'][] = $tax->name;
			$archive['link'][] = get_term_link( $tax->term_id, $taxonomy );
			$archive['id'][] = $tax->term_id;
			$archive_parent = $tax->parent;
		}
		if( isset($archive['title']) ) {
			foreach( $archive['title'] as $key => $value ) {
				$this->registry['ancestors'][] = [
					'title' => array_pop($archive['title']),
					'link' => array_pop($archive['link']),
					'id' => array_pop($archive['id']),
				];
			}
		}
	}


	/**
	 * Формирование данных для страниц.
	 *
	 * @param array $ancestors
	 * @return void
	 */
	private function pages_ancestors_list( $ancestors ) {
		if( !empty( $ancestors ) ) {
			foreach( array_reverse($ancestors) as $key => $parent_id ) {
				$ancestors[$key] = [
					'title' => get_the_title($parent_id),
					'link' => get_permalink($parent_id),
					'id' => $parent_id
				];
			}
			$this->registry['ancestors'] = $ancestors;
		}
	}


	/**
	 * Получение списка тегов.
	 *
	 * @param void
	 * @return void
	 */
	private function tag_list() {
		if( $this->registry['singular'] == 'single' ) {
			$taxonomy = 'post_tag';
		} elseif( $this->registry['singular'] == 'products' ) {
			$taxonomy = 'manufacturers';
		} else {
			return false;
		}
		// Получение списка родительских тегов записи
		$tax = get_the_terms($this->registry['id'], $taxonomy);
		if( !empty($tax) ){
			foreach( $tax as $key => $taxonomy ) {
				$this->registry['tags'][] = $taxonomy->name;
			}
		}
		if( !empty($this->registry['tags']) ){
			$this->registry['tags'] = array_filter($this->registry['tags']);
		} else {
			unset($this->registry['tags']);
		}
	}


	/**
	 * Архивный узел.
	 * Проверяется наличие страницы с лентой всей таксономии.
	 *
	 * @param void
	 * @return void
	 */
	private function first_node() {
		if( (isset($this->registry['singular']) && $this->registry['singular'] == 'single') || ( isset($this->registry['archive']) && in_array($this->registry['archive'],['category','tag','author','day','month','year']) ) ) {
			$page = get_option( 'page_for_posts' );
			if($page == 0) {
				$page = NULL;
			} else {
				$page = get_post($page);
			}
		} elseif( (isset($this->registry['singular']) && $this->registry['singular'] == 'estate') || ( isset($this->registry['archive']) && $this->registry['archive'] == 'realty' ) ) {
			$tax = get_taxonomy( 'realty' );
			$page = get_page_by_path($tax->rewrite['slug']);
		} elseif( (isset($this->registry['singular']) && $this->registry['singular'] == 'rooms') ||  ( isset($this->registry['archive']) && $this->registry['archive'] == 'hotels' ) ) {
			$tax = get_taxonomy( 'hotels' );
			$page = get_page_by_path($tax->rewrite['slug']);
		} elseif( (isset($this->registry['singular']) && $this->registry['singular'] == 'products') || ( isset($this->registry['archive']) && in_array($this->registry['archive'],['catalog','manufacturers','products_class']) ) ) {
			$tax = get_taxonomy( 'catalog' );
			$page = get_page_by_path($tax->rewrite['slug']);
		}
		if( isset($page) && is_object($page) ) {
			$node = [
					'title' => $page->post_title,
					'link' => get_permalink($page->ID),
					'id' => $page->ID,
			];
			if( isset($this->registry['ancestors']) && is_array($this->registry['ancestors']) ) {
				array_unshift($this->registry['ancestors'], $node);
			} else {
				$this->registry['ancestors'][] = $node;
			}
		}
	}


	/**
	 * Формирование H1.
	 *
	 * @param void
	 * @return void
	 */
	private function h1_construct() {

		// Добавление элемента заголовка (префикс архива)
		$page_title = '';
		$title_prefix = get_option('titleprefix_'.$this->registry['current_mode']);
		if( (bool)$title_prefix && $title_prefix != '' ) {
			$page_title .= $title_prefix;
		}
		// Добавление заголовка
		if( $this->registry['title'] && $this->registry['title'] != '' ) {
			if( $page_title != '' ) {
				$page_title .= ' ';
			}
			$page_title .= $this->registry['title'];
		}
		// Результат
		$this->registry['h1'] = $this->registry['page_title'] = $page_title;
	
		// H1 из дополнительных полей
		$h1 = '';
		if( $this->registry['mode'] == 'singular' ) {
			$h1 = get_post_meta($this->registry['id'], 'h1', true);
		} elseif( $this->registry['mode'] == 'archive' ) {
			$term_id = $this->registry['id'];
			$tax_data = get_term_meta( $term_id );
			if( (bool)$tax_data ) {
				foreach( $tax_data as $key => $value ) {
					$tax_data[$key] = $value[0];
				}
			}
			if( isset($tax_data['cat_h1']) ) {
				$h1 = $tax_data['cat_h1'];
			}

		}
		// Перезапись результата
		if( $h1 != '' ) {
			// Если мета поле прописано - генерировать preix + title нет необходимости. H1 - только то, что прописно
			$this->registry['h1'] = $h1;
		}
	}



	/**
	 * Генерация метатегов.
	 *
	 * @param string $type
	 * @return string $result_set
	 */
	private function meta_generator($type) {

		// Исключения, на страницах архивов не генерировать теги
		if( in_array( $this->registry['current_mode'], ['404', 'search', 'day', 'month', 'year', 'author', 'tag'] ) ) {
			return $this->registry['page_title']; // Возвращется только заголовок
		}

		// Поиск шаблона для генерации
		$incubator = get_option( $type.'_'.$this->registry['current_mode'].'_rule' );
		if( !(bool)$incubator || $incubator == '' ) {
			$incubator = get_option($type.'_default_rule');
		}
		if( !(bool)$incubator || $incubator == '' ) {
			return $this->registry['page_title']; // Возвращется только заголовок
		}

		// Масив замен
		if( empty($this->replacement) ) {
			$this->replacement['blog_name'] = get_bloginfo( 'name' );
			$this->replacement['page_title'] = $this->registry['page_title'];
			$this->replacement['h1'] = $this->registry['h1'];
			$this->replacement['blog_description'] = get_bloginfo( 'description' );
			$this->replacement['url'] = preg_replace('#^https?://#', '', rtrim(get_bloginfo( 'url' ),'/'));
			$this->replacement['tel'] = apply_filters('seo_phone_construct', false);
			// Предки
			$this->replacement['ancestors'] = '';
			if( isset($this->registry['ancestors']) && is_array($this->registry['ancestors']) ) {
				foreach( $this->registry['ancestors'] as $key => $ancestor ) {
					if( $this->replacement['ancestors'] != '' ) {
						$this->replacement['ancestors'] .= ', ';
					}
					$this->replacement['ancestors'] .= $ancestor['title'];
				}
				if( $this->replacement['ancestors'] != '' ) {
					$this->replacement['ancestors'] .= '. ';
				}
			}
		}

		// Замена маркеров значениями
		foreach( $this->replacement as $key => $value ) {
			// Маркер
			$marker = '%' . $key . '%';
			// Замена текущего маркера
			$incubator = str_replace($marker, $value, $incubator);
		}
		if($incubator != '') {
			$result_set = $incubator;
		}

		return $result_set;
	}

} //end SeoSemantic
?>