<?php
/*
* @package LabCMS
* @copyright (c) 2006 Alexander Levin aka Loki (loki_angel@mail.ru)
* @support http://labcms.ru
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/

class search_search
{
	public $using_modules=false;
	public $string='';
	public $page=1;

	protected $type='all';
	protected $sort='rel';
	protected $query_string='';
	protected $words_array=array();
	protected $pager=array();
	protected $possible_modules=null;
	protected $search_result=array();
	
	public function setSort($sort)
	{
		$this->sort=$sort;
	}
	
	public function getSort()
	{
		$this->sort;
	}
	
	public function setType($type)
	{
		$this->type=$type;
	}

	public function getType()
	{
		return $this->type;
	}

	public function getResult($string, $page=1)
	{
		$this->string=trim($string);
		$this->page=intval($page);
		if (!$this->string) return false;
		if (!$this->type) $type='all';
		if (!$this->sort) $type='rel';
		
		
		//TODO тут место поднятия кэша
		
		$this->_search();
		//TODO тут можно закэшировать $this->search_result
		return $this->_getByPage();
	}
	
	public function setModulesToSearch($modules)
	{
		$this->using_modules=$modules;
	}
	
	protected function _getByPage()
	{
		if ($total=count($this->search_result)) {
			$pg=Factory::create('Paginator', $total, config::get('search_results_at_page'), $this->page);
			$this->pager=$pg->getPages();
			return array_slice($this->search_result, $this->pager['sql_start'], $this->pager['sql_limit']);
		}
	}
	
	protected function _search()
	{
		$this->_getSearchString();
		if (!count($this->words_array)) return;
		foreach ($this->getSearchModules() as $module) {
			if (!is_array($this->using_modules)
				|| (is_array($this->using_modules)
				&& in_array($module['module_id'], $this->using_modules))
			) {
				$classname=$module['module'].'_api';
				Factory::set($classname, $classname, LAB_PATH_MODULES.'/'.$module['module']."/class.api.php");
				$obj=Factory::create($classname);
				
				if ($result=$obj->search($this->query_string)) {
					$result=$this->_setResultHighlights($result, $module['name']);
					$this->search_result=array_merge($this->search_result, $result);
				}
			}
		}
		if ($this->search_result) $this->_sort();
	}
	
	protected function _sort()
	{
		switch($this->sort)
		{
			case('date'):
				usort($this->search_result, array($this, 'sortByDate'));
				break;
			case('module'):
				break;
			default:
				usort($this->search_result, array($this, 'sortByRel'));
		}
	}
	
	public function getPager()
	{
		return $this->pager;
	}
	
	public function getSearchModules()
	{
		if (!$this->possible_modules) 
			$this->possible_modules=Db::i()->select(
				"SELECT module, name, module_id
				FROM ?_modules
				WHERE module IN (?a) AND disable=0",
				config::get('search_modules')
			);
		return $this->possible_modules;
	}
	
	public function getUrlParams()
	{
		$params=array(
            'search'=>$this->string,
			'type'=>$this->type,
			'sort'=>$this->sort
        );
		if (!is_array($this->using_modules)) $params['all']='all';
		else
		{
			foreach ($this->using_modules as $module) {
				$params['module'][]=intval($module);
			}
		}
		
		return $params;
	}
	
	protected function _getWordsArray($string)
	{
		//разделяем фразу на отдельные слова, выделяем у них корень и составляем поисковый запрос
		$text_array=explode(" ", $string);
		$stemmer = Factory::create('Stem_Ru');
		$text_array=array_map('trim', $text_array);
		foreach ($text_array as $val) {
			$val=str_replace(array('+','-','<','>','(',')','"','\''), '', $val);
			if ($val && mb_strlen($val)>2) $sval=$stemmer->stem_word($val);
			if ($sval && mb_strlen($sval)>2) $words[]=$sval;
			$words[]=$val;
		}
		return $words;
	}

	function sortByRel($a, $b)
	{
		return ($a['rel']>$b['rel'])?-1:($a['rel']<$b['rel'])?1:0;
	}

	function sortByDate($a, $b)
	{
		return ($a['date']>$b['date'])?-1:($a['date']<$b['date'])?1:0;
	}
	
	protected function _getSearchString()
	{
		switch($this->type)
		{
			case('strict'):
				$this->words_array=$this->_getWordsArray($this->string);
				$this->query_string='"'.$this->string.'"';
				break;
			case('spec'):
				$this->words_array=$this->_getWordsArray($this->string);
				$this->query_string=$this->string;
				break;
			case('any'):
				if ($this->words_array=$this->_getWordsArray($this->string)) {
					foreach ($this->words_array as $word) {
						if ($word) $this->query_string.=$word."* ";
					}
				}
				break;
			default:
				$this->query_string='>("'.$this->string.'")';
				if ($this->words_array=$this->_getWordsArray($this->string)) {
					$this->query_string.=' <(';
					foreach ($this->words_array as $word) {
						if ($word) $this->query_string.="+".$word."* ";
					}
					$this->query_string.=')';
				}
		}
	}

	protected function _setResultHighlights($result, $module_name)
	{
		foreach ($result as $k=>$v) {
			$result[$k]['module']=$module_name;
			$result[$k]['text_html']=strip_tags($v['text']);
			$places=array();
			foreach ($this->words_array as $word) {
				$places[]=mb_stripos($result[$k]['text_html'], $word);
			}
			$blocks=array();
			foreach ($places as $pos) {
				$start=(($pos-100)<0)?0:$pos-100;
				$end=$pos+100;

				if (count($blocks)) {
					foreach ($blocks as $bk=>$block) {
						if ($block['start']<=$start && $block['end']>=$start && $block['end']<$end) {
							$blocks[$bk]['end']=$end;
							continue;
						}
						if ($block['start']<=$end && $block['end']>=$end && $block['start']>$start) {
							$blocks[$bk]['start']=$start;
							continue;
						}
						$blocks[]=array('start'=>$start, 'end'=>$end);
					}
				}
				else $blocks[]=array('start'=>$start, 'end'=>$end);
			}
			
			$id=0;
			$max=0;
			if ($blocks) foreach ($blocks as $i=>$block) {
				if (($block['end']-$block['start'])>$max) {
					$id=$i;
					$max=$block['end']-$block['start'];
				}
			}
			
			$result[$k]['text_html']=mb_substr(
				$result[$k]['text_html'], $blocks[$id]['start'],
				$blocks[$id]['end']-$blocks[$id]['start']
			);
			$result[$k]['text_html']=(($blocks[$id]['start'])?'...':'').$result[$k]['text_html'].'...';
			
			$result[$k]['title_html']=strip_tags($result[$k]['title']);
			
			foreach ($this->words_array as $word) {
				$result[$k]['text_html'] = preg_replace(
					'/([^а-яa-z0-9]|^)('
					.preg_quote($word, "/")
					.'[а-яa-z0-9]*)/iu', '\\1<mark>\\2</mark>',
					$result[$k]['text_html']
				);
				$result[$k]['title_html'] = preg_replace(
					'/([^а-яa-z0-9]|^)('
					.preg_quote($word, "/")
					.'[а-яa-z0-9]*)/iu', '\\1<mark>\\2</mark>',
					$result[$k]['title_html']
				);
			}
		}
		return $result;
	}
}