<?php

/******************************************************************************
 *
 *	PROJECT: Flynax Classifieds Software
 *	VERSION: 4.0
 *	LISENSE: http://www.flynax.com/license-agreement.html
 *	PRODUCT: General Classifieds
 *	
 *	FILE: RLSEARCH.CLASS.PHP
 *
 *	This script is a commercial software and any kind of using it must be 
 *	coordinate with Flynax Owners Team and be agree to Flynax License Agreement
 *
 *	This block may not be removed from this file or any other files with out 
 *	permission of Flynax respective owners.
 *
 *	Copyrights Flynax Classifieds Software | 2012
 *	http://www.flynax.com/
 *
 ******************************************************************************/

class rlSearch extends reefless
{
	/**
	* @var language class object
	**/
	var $rlLang;
	
	/**
	* @var validator class object
	**/
	var $rlValid;
	
	/**
	* @var common class object
	**/
	var $rlCommon;
	
	/**
	* @var current form fields
	**/
	var $fields;
	
	/**
	* class constructor
	**/
	function rlSearch()
	{
		global $rlValid, $rlCommon, $rlLang;
		
		$this -> rlValid = $rlValid;
		$this -> rlCommon = $rlCommon;
		$this -> rlLang = $rlLang;
	}

	/**
	* build search form
	*
	* @param string $key - search form key
	* @param string $type - listing type
	*
	* @return array - form information
	**/
	function buildSearch( $key = false, $type = false )
	{
		global $rlCache, $config;
		
		if ( !$key || !$type )
		{
			return false;
		}
		
		/* get form from cache */
		if ( $config['cache'] )
		{
			return $rlCache -> get('cache_search_forms', $key);
		}
		
		$sql = "SELECT `T1`.`Category_ID`, `T1`.`Group_ID`, `T1`.`Fields`, ";
		$sql .= "`T2`.`Key` AS `Group_key`, `T2`.`Display`, ";
		$sql .= "`T3`.`Type` AS `Listing_type`, `T3`.`Key` AS `Form_key` ";
		$sql .= "FROM `". RL_DBPREFIX ."search_forms_relations` AS `T1` ";
		$sql .= "LEFT JOIN `". RL_DBPREFIX ."listing_groups` AS `T2` ON `T1`.`Group_ID` = `T2`.`ID` ";
		$sql .= "LEFT JOIN `". RL_DBPREFIX ."search_forms` AS `T3` ON `T1`.`Category_ID` = `T3`.`ID` ";
		$sql .= "WHERE `T3`.`Key` = '{$key}' AND `T3`.`Status` = 'active' ";
		$sql .= "ORDER BY `Position` ";
		$relations = $this -> getAll($sql);
		
		if ( !$relations )
		{
			return false;
		}
		
		/* populate field information */
		foreach ($relations as $key => $value)
		{
			$sql = "SELECT `ID`, `Key`, `Type`, `Default`, `Values`, `Condition`, CONCAT('listing_fields+name+', `Key`) AS `pName`, ";
			$sql .= "FIELD(`ID`, {$value['Fields']}) AS `Order` ";
			$sql .= "FROM `". RL_DBPREFIX ."listing_fields` ";
			$sql .= "WHERE FIND_IN_SET(`ID`, '{$value['Fields']}' ) > 0 AND `Status` = 'active' ";
			$sql .= "ORDER BY `Order`";
			$fields = $this -> getAll($sql);

			$relations[$key]['pName'] = 'listing_groups+name+'. $value['Group_key'];
			$relations[$key]['Fields'] = empty($fields) ? false : $this -> rlCommon -> fieldValuesAdaptation($fields, 'listing_fields', $value['Listing_type']);
		}

		return $relations;
	}
	
	/**
	* get general data of search form
	*
	* @param string $key  - search form key
	* @param string $listing_type_key - listing type key
	* @param bool $tab_form - is form splitted by tabs
	*
	* @todo array - form fields list
	**/
	function getFields( $key = false, $listing_type_key = false, $tab_form = false )
	{
		global $rlCache, $config, $rlListingTypes;
		
		if ( !$key )
		{
			return false;
		}
		
		$arrange_field = $rlListingTypes -> types[$listing_type_key]['Arrange_field'];
		
		/* get form from cache */
		if ( $config['cache'] )
		{
			$fields = $rlCache -> get('cache_search_fields', $key);
			$this -> fields = $this -> rlLang -> replaceLangKeys( $fields, 'listing_fields', array( 'name', 'default' ) );
			
			/* add additional field */
			if ( $tab_form && $arrange_field )
			{
				$a_field = $this -> fetch(array('ID', 'Key', 'Type'), array('Key' => $arrange_field), null, 1, 'listing_fields', 'row');
				if ( $a_field )
				{
					$this -> fields[$arrange_field] = $a_field;
				}
			}
			
			return true;
		}
		
		$sql = "SELECT `T1`.`Category_ID`, `T1`.`ID`, `T1`.`Fields`, `T2`.`Key` AS `Form_key` ";
		$sql .= "FROM `". RL_DBPREFIX ."search_forms_relations` AS `T1` ";
		$sql .= "LEFT JOIN `". RL_DBPREFIX ."search_forms` AS `T2` ON `T1`.`Category_ID` = `T2`.`ID` ";
		$sql .= "WHERE `T2`.`Key` = '{$key}' AND `T2`.`Status` = 'active' ";
		$sql .= "ORDER BY `Position` ";
		$relations = $this -> getAll($sql);
		
		if ( !$relations )
		{
			return false;
		}
				
		foreach ($relations as $key => $value)
		{
			$sql = "SELECT `ID`, `Key`, `Type`, `Default`, `Values`, `Condition`, `Details_page`, ";
			$sql .= "FIELD(`ID`, {$value['Fields']}) AS `Order` ";
			$sql .= "FROM `". RL_DBPREFIX ."listing_fields` ";
			$sql .= "WHERE FIND_IN_SET(`ID`, '{$value['Fields']}' ) > 0 AND `Status` = 'active' ";
			$sql .= "ORDER BY `Order`";
			$fields = $this -> getAll($sql);
			
			$fields = $this -> rlLang -> replaceLangKeys( $fields, 'listing_fields', array( 'name', 'default' ) );
			
			foreach ( $fields as $fKey => $fValue )
			{
				$out[$fValue['Key']] = $fValue;
			}
			
			unset($fields);
		}
		
		$this -> fields = $out;
		
		/* add additional field */
		if ( $tab_form && $arrange_field )
		{
			$a_field = $this -> fetch(array('ID', 'Key', 'Type'), array('Key' => $arrange_field), null, 1, 'listing_fields', 'row');
			if ( $a_field )
			{
				$this -> fields[$arrange_field] = $a_field;
			}
		}
	}
	
	/**
	* search listings
	*
	* @param array $data - form data
	* @param string $type - listing type
	* @param int $start - start DB position
	* @param int $limit - listing number per request
	*
	* @return array - listings information
	**/
	function search( $data = false, $type = false, $start = 0, $limit = false )
	{
		global $sql, $custom_order, $config, $rlListings;
		
		$form = $this -> fields;
		
		if ( !$form )
			return false;
		
		$start = $start > 1 ? ($start - 1) * $limit : 0;
		$zip_mode = false;
		$hook = '';
		
		$this -> loadClass('Listings');
		$this -> loadClass('Common');

		$sql = "SELECT SQL_CALC_FOUND_ROWS DISTINCT {hook} SUBSTRING_INDEX(GROUP_CONCAT(DISTINCT `T4`.`Thumbnail` ORDER BY `T4`.`Type` DESC), ',', 1) AS `Main_photo`, ";
		$sql .= "`T1`.*, `T3`.`Path`, `T3`.`Parent_ID`, `T3`.`Key` AS `Cat_key`, `T3`.`Key` AS `Key`, `T3`.`Type` AS `Listing_type`, ";
		$sql .= $config['grid_photos_count'] ? "COUNT(`T4`.`Thumbnail`) AS `Photos_count`, " : "";
		
		$GLOBALS['rlHook'] -> load('listingsModifyFieldSearch');
		
		$sql .= "DATE_ADD(`T1`.`Featured_date`, INTERVAL `T2`.`Listing_period` DAY) AS `Featured_expire`, ";
		$sql .= "IF(UNIX_TIMESTAMP(DATE_ADD(`T1`.`Featured_date`, INTERVAL `T2`.`Listing_period` DAY)) > UNIX_TIMESTAMP(NOW()), '1', '0') `Featured` ";
		$sql .= "FROM `" . RL_DBPREFIX . "listings` AS `T1` ";
		$sql .= "LEFT JOIN `" . RL_DBPREFIX . "listing_plans` AS `T2` ON `T1`.`Plan_ID` = `T2`.`ID` ";
		$sql .= "LEFT JOIN `" . RL_DBPREFIX . "categories` AS `T3` ON `T1`.`Category_ID` = `T3`.`ID` ";
		$sql .= "LEFT JOIN `" . RL_DBPREFIX . "listing_photos` AS `T4` ON `T1`.`ID` = `T4`.`Listing_ID` ";
		
		if ( $zip_mode )
		{
			$sql .= "LEFT JOIN `" . RL_DBPREFIX . "zip_code` AS `ZIP` ON `T1`.`{$zip_field}` LIKE `ZIP`.`Zip` ";
		}
		
		$sql .= "LEFT JOIN `" . RL_DBPREFIX . "accounts` AS `T7` ON `T1`.`Account_ID` = `T7`.`ID` ";
		
		$GLOBALS['rlHook'] -> load('listingsModifyJoinSearch');

		$sql .= "WHERE (UNIX_TIMESTAMP(DATE_ADD(`T1`.`Pay_date`, INTERVAL `T2`.`Listing_period` DAY)) > UNIX_TIMESTAMP(NOW()) OR `T2`.`Listing_period` = 0) ";
		
		foreach ($form as $fKey => $fVal)
		{
			$f = $this -> rlValid -> xSql($data[$fKey]);

			if( !empty($f) )
			{
				switch ($form[$fKey]['Type']){
					case 'mixed':
						if ( $f['df'] )
						{
							$sql .= "AND LOCATE('{$f['df']}', `T1`.`". $fKey ."`) > 0 ";
						}
					case 'price':
						if ( $f['currency'] )
						{
							$sql .= "AND LOCATE('{$f['currency']}', `T1`.`". $fKey ."`) > 0 ";
						}
					case 'unit':
						if ( $f['unit'] )
						{
							$sql .= "AND LOCATE('{$f['unit']}', `T1`.`". $fKey."`) > 0 ";
						}
					case 'number':
						if ( false !== strpos($form[$fKey]['Key'], 'zip') && $GLOBALS['aHooks']['search_by_distance'] )
						{
							if ( $f['distance'] && $f['zip'] )
							{
								$zip_code = $f['zip'];
								$miles = $f['distance'];
								
								$zip_info = $this ->getRow("SELECT `Latitude`, `Longitude` FROM `" . RL_DBPREFIX . "zip_code` WHERE `Zip` = '{$zip_code}'");
								
								if ($zip_info['Latitude'] && $zip_info['Longitude'])
								{
									$sql .= "AND (3956 * 2 * ASIN(SQRT( POWER(SIN(({$zip_info['Latitude']} - `ZIP`.`Latitude`) * 0.0174532925 / 2), 2) + 	COS({$zip_info['Latitude']} * 0.0174532925) * COS(`ZIP`.`Latitude` * 0.0174532925) * POWER(SIN(({$zip_info['Longitude']} - `ZIP`.`Longitude`) * 0.0174532925 / 2), 2))) <= {$miles}) ";
								}
							}
						}
						else
						{
							if( (int)$f['from'] )
							{
								$sql .= "AND ROUND(`T1`.`{$fKey}`) >= '".(int)$f['from']."' ";
							}
							if( (int)$f['to'] )
							{
								$sql .= "AND ROUND(`T1`.`{$fKey}`) <= '".(int)$f['to']."' ";
							}
						}
						break;
					
					case 'text':
						if ( $fKey == 'keyword_search' )
						{
							if ( !$this -> keywordSearch($f, $data['keyword_search_type'], $type) )
							{
								return false;
							}
						}
						else
						{
							if ( is_numeric($f) )
							{
								$sql .= "AND `T1`.`{$fKey}` LIKE '%". $f ."%' ";
							}
							else
							{
								$sql .= "AND (MATCH (`T1`.`{$fKey}`) AGAINST('". $f ."' IN BOOLEAN MODE)) ";
							}
						}
						break;
					
					case 'date':
						if ( $form[$fKey]['Default'] == 'single')
						{
							if( $f['from'] )
							{
								$sql .= "AND UNIX_TIMESTAMP(`T1`.`{$fKey}`) >= UNIX_TIMESTAMP('". $f['from'] ."') ";
							}
							if( $f['to'] )
							{
								$sql .= "AND UNIX_TIMESTAMP(`T1`.`{$fKey}`) <= UNIX_TIMESTAMP('". $f['to'] ."') ";
							}
						}
						elseif ( $form[$fKey]['Default'] == 'multi')
						{
							$sql .= "AND UNIX_TIMESTAMP(`T1`.`{$fKey}`) <= UNIX_TIMESTAMP('". $f ."') ";
							$sql .= "AND UNIX_TIMESTAMP(`T1`.`{$fKey}_multi`) >= UNIX_TIMESTAMP('". $f ."') ";
						}
						break;
					
					case 'select':
						if ( $form[$fKey]['Condition'] == 'years' )
						{
							if( $f['from'] )
							{
								$sql .= "AND `T1`.`{$fKey}` >= '".(int)$f['from']."' ";
							}
							if( $f['to'] )
							{
								$sql .= "AND `T1`.`{$fKey}` <= '".(int)$f['to']."' ";
							}
						}
						elseif ( $form[$fKey]['Key'] == 'Category_ID' )
						{
							$children = $GLOBALS['rlCommon'] -> getChildren($f, 'categories');

							if ( $children )
							{
								$sql .= "AND ((`T1`.`{$fKey}` = '{$f}' OR `T1`.`{$fKey}` = '". implode("' OR `T1`.`{$fKey}` = '", $children) ."') OR (FIND_IN_SET('{$f}', `T1`.`Crossed`) > 0 OR FIND_IN_SET('". implode("', `T1`.`Crossed`) > 0 OR FIND_IN_SET('", $children) . "', `T1`.`Crossed`) > 0))";
								$hook = "IF ((FIND_IN_SET('{$f}', `T1`.`Crossed`) > 0 OR FIND_IN_SET('". implode("', `T1`.`Crossed`) > 0 OR FIND_IN_SET('", $children) . "', `T1`.`Crossed`) > 0), 1, 0) AS `Crossed_listing`, ";
							}
							else
							{
								$sql .= "AND ((`T1`.`{$fKey}` = '{$f}' OR `T3`.`Parent_ID` = '{$f}') OR (FIND_IN_SET('{$f}', `T1`.`Crossed`) > 0)) ";
								$hook = "IF ((FIND_IN_SET('{$f}', `T1`.`Crossed`) > 0), 1, 0) AS `Crossed_listing`, ";
							}
						}
						elseif ( $form[$fKey]['Key'] == 'posted_by' )
						{
							$sql .= "AND `T7`.`Type` = '". $f ."' ";
						}
						else
						{
							$sql .= "AND `T1`.`{$fKey}` = '". $f ."' ";
						}
						break;
					
					case 'bool':
						if ( $f == 'on' )
						{
							$sql .= "AND `T1`.`{$fKey}` = '1' ";
						}
						else
						{
							$sql .= "AND `T1`.`{$fKey}` = '0' ";
						}
						break;

					case 'radio':
						$sql .= "AND `T1`.`{$fKey}` = '".$f."' ";
						break;
					
					case 'checkbox':
						unset($f[0]);
						if ( !empty($f) )
						{
							$sql .= "AND (";
							foreach ( $f as $fI => $fV )
							{
								$sql .= "FIND_IN_SET('". $f[$fI] ."', `T1`.`{$fKey}`) > 0 OR ";
							}
							$sql = substr( $sql, 0, -3 );
							$sql .= ") ";
						}
						break;
				}
				
				$GLOBALS['rlHook'] -> load('searchSelectArea', $f, $fVal);
			}
		}

		$sql .= "AND `T1`.`Status` = 'active' AND `T3`.`Status` = 'active' AND `T7`.`Status` = 'active' ";
		if ( $type )
		{
			$sql .= "AND `T3`.`Type` = '{$type}' ";
		}

		if( $data['with_photo'] )
		{
			$sql .= "AND `T4`.`Thumbnail` <> '' ";
		}
		
		$GLOBALS['rlHook'] -> load('listingsModifyWhereSearch');
		$GLOBALS['rlHook'] -> load('listingsModifyGroupSearch');
		
		if ( false === strpos($sql, 'GROUP BY') )
		{
			$sql .= " GROUP BY `T1`.`ID` ";
		}
		
		$sql .= "ORDER BY `Featured` DESC ";

		$data['sort_type'] = in_array( $data['sort_type'], array('asc', 'desc' )) ?  $data['sort_type'] : 'asc' ;

		if ( $custom_order )
		{
			$sql .= ", `{$custom_order}` ". strtoupper($data['sort_type']) . " ";
		}
		elseif ( $form[$data['sort_by']] )
		{
			switch ($form[$data['sort_by']]['Type']){
				case 'price':
				case 'unit':
				case 'mixed':
					$sql .= ", ROUND(`T1`.`{$form[$data['sort_by']]['Key']}`) " . strtoupper($data['sort_type']) . " ";
					break;
				
				case 'select':
					if ( $form[$data['sort_by']]['Key'] == 'Category_ID' )
					{
						$sql .= ", `T3`.`Key` " . strtoupper($data['sort_type']) . " ";
					}
					elseif ( $form[$data['sort_by']]['Key'] == 'Listing_type' )
					{
						$sql .= ", `T3`.`Type` " . strtoupper($data['sort_type']) . " ";
					}
					else
					{
						$sql .= ", `T1`.`{$form[$data['sort_by']]['Key']}` " . strtoupper($data['sort_type']) . " ";
					}
					break;
				
				default:
					$sql .= ", `T1`.`{$form[$data['sort_by']]['Key']}` " . strtoupper($data['sort_type']) . " ";
					break;
			}
		}
		else
		{
			$sql .= ", `Date` DESC ";
		}
		
		$sql .= "LIMIT {$start}, {$limit} ";
		
		/* replace hook */
		$sql = str_replace('{hook}', $hook, $sql);

		$listings = $this -> getAll( $sql );
		$listings = $this -> rlLang -> replaceLangKeys( $listings, 'categories', 'name' );

		$calc = $this -> getRow( "SELECT FOUND_ROWS() AS `calc`" );
		$this -> calc = $calc['calc'];

		foreach ( $listings as $key => $value )
		{
			$fields = $rlListings -> getFormFields($value['Category_ID'], 'short_forms', $value['Listing_type']);
			
			foreach ( $fields as $fKey => $fValue )
			{
				if ( $first )
				{
					$fields[$fKey]['value'] = $GLOBALS['rlCommon'] -> adaptValue( $fValue, $value[$fKey], 'listing', $value['ID'] );
				}
				else
				{
					if ( $field['Condition'] == 'isUrl' || $field['Condition'] == 'isEmail' )
					{
						$fields[$fKey]['value'] = $listings[$key][$item];
					}
					else
					{
						$fields[$fKey]['value'] = $GLOBALS['rlCommon'] -> adaptValue( $fValue, $value[$fKey], 'listing', $value['ID'] );
					}
				}
				$first++;
			}
			
			$listings[$key]['fields'] = $fields;
			$listings[$key]['listing_title'] = $rlListings -> getListingTitle( $value['Category_ID'], $value, $value['Listing_type'] );
		}

		return $listings;
	}
	
	/**
	* build keyword search mysql request by requested kewywords string
	*
	* @param string $request - listing type
	* @param int $mode - search mode, 1 - All words, any order; 2 - Any words, any order; 3-  Exact words, exact order; 4 - Exact words, any order
	* @param string $type - listing type key
	*
	* @return array - listings information
	**/
	function keywordSearch( $query = false, $mode = 2, $type = false )
	{
		global $sql, $rlCommon;
		
		$mode = !$mode ? 2 : $mode;
		$query = trim($query);
		$query = preg_replace('/(\\s)\\1+/', ' ', $query);
		$query = str_replace('%', '', $query);
		$query_exploded = explode(' ', $query);
		
		/* remove short words from the query */
		foreach ( $query_exploded as $wi => $word )
		{
			if ( $this -> rlCommon -> strLen( $word, '<', 3 ) )
			{
				unset( $query_exploded[$wi] );
			}
		}
		
		if ( !$query || empty($query_exploded) )
			return;
		
		/* MYSQL regexp is not case sensetive, but for some reason it doesn't search upper case content */
//		foreach ($query_exploded as $qexp_key => $qexp)
//		{
//			$query_exploded[$qexp_key] = $query_exploded[$qexp_key] .'|'. strtoupper($query_exploded[$qexp_key]);
//		}
			
		/* save the query to highlight it on the listing details */
		$_SESSION['keyword_search_data']['keyword_search'] = $query;
			
		/* get system fields */
		$this -> setTable('listing_fields');
		$fields = $this -> fetch(array('Key', 'Type', 'Condition'), array('Status' => 'active'), "AND FIND_IN_SET(`Type`, 'bool,file,accept,image') = 0 AND `Key` != 'keyword_search' ");
		
		if ( !$fields )
			return;
			
		foreach ($fields as $field)
		{
			if ( in_array($field['Type'], array('text', 'textarea', 'number', 'date')) || $field['Key'] == 'years' )
			{
				if ( in_array($field['Type'], array('text', 'textarea')) )
				{
					$direct_fields[] = $field['Key'];
				}
			}
			else
			{
				$indirect_fields[$field['Key']] = $field;
			}
		}
		
		$sql .= "AND (";
		
		/* search by direct fields */
		if ( $direct_fields )
		{
			switch ($mode){
				case 1:
					$sql .= "(CONCAT_WS(' ', `T1`.`". implode("`, `T1`.`", $direct_fields) ."`) RLIKE '". implode("?' AND CONCAT_WS(' ', `T1`.`". implode("`, `T1`.`", $direct_fields) ."`) RLIKE '", $query_exploded) ."?') ";
					break;
				
				case 2:
					$sql .= "(CONCAT_WS(' ', `T1`.`". implode("`, `T1`.`", $direct_fields) ."`) RLIKE '". implode("?' OR CONCAT_WS(' ', `T1`.`". implode("`, `T1`.`", $direct_fields) ."`) RLIKE '", $query_exploded) ."?') ";
					break;
					
				case 3;
					$sql .= "CONCAT_WS(' ', `T1`.`". implode("`, `T1`.`", $direct_fields) ."`) LIKE '%{$query}%' ";
					break;
			}
		}

		/* search by indirect fields */
		if ( $indirect_fields )
		{
			/* get system listing table fields */
			$tmp_s_fields = $this -> getAll("SHOW FIELDS FROM `". RL_DBPREFIX ."listings`");
			foreach ( $tmp_s_fields as $s_field )
			{
				$s_fields[] = $s_field['Field'];
			}
			unset($tmp_s_fields);
			
			$ind_sql = "SELECT DISTINCT `Key`, IF(`Key` LIKE 'listing_fields+name+%', 0, 1) AS `Df` FROM `". RL_DBPREFIX ."lang_keys` ";
			$ind_sql .= "WHERE (`Key` LIKE 'listing_fields+name+%' OR `Key` LIKE 'data_formats+name+%') AND ( ";
			switch ($mode){
				case 1:
//					$ind_sql .= "`Value` RLIKE '". implode("?' AND `Value` RLIKE '", $query_exploded) ."?'";
//					break;
				
				case 2:
					$ind_sql .= "`Value` RLIKE '". implode("?' OR `Value` RLIKE '", $query_exploded) ."?'";
					break;
					
				case 3;
					$ind_sql .= "`Value` LIKE '%{$query}%'";
					break;
			}
			$ind_sql .= ") AND `Status` = 'active' AND `Code` = '". RL_LANG_CODE ."'";
			$indirect_keys = $this -> getAll($ind_sql);

			if ( $indirect_keys )
			{
				$sql .= ") OR (";
				foreach ($indirect_keys as $ind_key)
				{
					$item = str_replace(array('listing_fields+name+', 'data_formats+name+'), '', $ind_key);
					if ( $ind_key['Df'] )
					{
						if ( !$df_fields )
						{
							$df_fields_tmp = $this -> fetch(array('Key'), array('Status' => 'active'), "AND `Type` IN ('select', 'radio', 'mixed', 'checkbox') AND `Condition` <> ''", null, 'listing_fields');
							foreach ($df_fields_tmp as $df_field)
							{
								if ( in_array($df_field['Key'], $s_fields) )
								{
									$df_fields[] = $df_field['Key'];
								}
							}
							unset($df_fields_tmp);
						}
						$value = $item['Key'];
						$sql .= '`T1`.`'. implode("` = '{$value}' OR `T1`.`", $df_fields) ."` = '{$value}' OR ";
					}
					else
					{
						$item = explode('_', $item['Key']);
						$value = array_pop($item);
						$t_field = implode('_', $item);
						
						if ( !in_array($t_field, $s_fields) )
						{
							continue;
						}
						
						$t_field = '`T1`.`'. $t_field .'`';
						$sql .= "{$t_field} = '{$value}' OR ";
					}
				}

				$sql = preg_replace('/(AND|OR)\s$/', '', $sql);
				$sql = preg_replace('/\)\sOR\s\($/', '', $sql);
//				$sql = rtrim($sql, 'AND ');
//				$sql = rtrim($sql, 'OR (1) ');
			}
		}
		
		/* search by category */
		$cat_sql = "SELECT `T2`.`ID` FROM `". RL_DBPREFIX ."lang_keys` AS `T1` ";
		$cat_sql .= "LEFT JOIN `". RL_DBPREFIX ."categories` AS `T2` ON `T1`.`Key` = CONCAT('categories+name+', `T2`.`Key`) ";
		$cat_sql .= "WHERE `T1`.`Key` LIKE 'categories+name+%' AND ( ";
		switch ($mode){
			case 1:
				$cat_sql .= "`T1`.`Value` RLIKE '". implode("?' AND `T1`.`Value` RLIKE '", $query_exploded) ."?'";
				break;
			
			case 2:
				$cat_sql .= "`T1`.`Value` RLIKE '". implode("?' OR `T1`.`Value` RLIKE '", $query_exploded) ."?'";
				break;
				
			case 3;
				$cat_sql .= "`T1`.`Value` LIKE '%{$query}%'";
				break;
		}
		$cat_sql .= ") AND `T1`.`Status` = 'active' AND `T2`.`Status` = 'active' AND `T1`.`Code` = '". RL_LANG_CODE ."'";
		$cat_ids = $this -> getAll($cat_sql);

		if ( $cat_ids )
		{
			$sql .= ") OR (";
			foreach ($cat_ids as $cat_id)
			{
				$child_ids = $rlCommon -> getChildren($cat_id['ID'], 'categories', null, null, null, null, $type);
				$sql .= "(`T1`.`Category_ID` = '". implode("' OR `T1`.`Category_ID` = '", $child_ids) ."') AND ";
			}
			$sql = rtrim($sql, 'AND ');
		}
		
		$sql .= ") ";
		
		return true;
	}
	
	/**
	* save search
	*
	* @package xAjax
	* @param string $type - listing type
	*
	**/
	function ajaxSaveSearch( $type = false )
	{
		global $_response, $account_info, $lang, $post_form_key;
		
		if ( !$type )
		{
			return $_response;
		}

		if ( defined( 'IS_LOGIN' ) )
		{
			$content = $_SESSION[$type .'_post'];

			unset($content['sort_type']);
			unset($content['sort_by']);

			foreach ($content as $key => $value)
			{
				if ( empty($content[$key]) )
				{
					unset($content[$key]);
				}
				if ( isset($content[$key]['from']) && ( empty($content[$key]['from']) && empty($content[$key]['to']) ) )
				{
					unset($content[$key]);
				}

				if ( isset($content[$key][0]) && is_array($content[$key]) )
				{
					unset($content[$key][0]);
					if ( empty($content[$key]) )
					{
						unset($content[$key]);
					}
				}
			}

			if ( !empty($content) )
			{
				$content = serialize( $content );
				$form_key = $_POST['form'];

				$exist = $this -> fetch( array('ID'), array( 'Content' => $content, 'Account_ID' => $account_info['ID'] ), null, 1, 'saved_search', 'row' );

				if ( empty( $exist ) )
				{
					$insert = array(
						'Account_ID' => $account_info['ID'],
						'Form_key' => $post_form_key,
						'Listing_type' => $type,
						'Content' => $content,
						'Date' => 'NOW()'
					);
					
					$this -> loadClass('Actions');
					
					$GLOBALS['rlActions'] -> rlAllowHTML = true;
					$GLOBALS['rlActions'] -> insertOne($insert, 'saved_search');
					
					$_response -> script("printMessage('notice', '{$lang['search_saved']}')");
				}
				else
				{
					$_response -> script("printMessage('error', '{$lang['search_already_saved']}')");
				}
			}
			else
			{
				$_response -> script("printMessage('error', '{$lang['empty_search_disallow']}')");
			}
		}
		else
		{
			$_response -> script("printMessage('error', '{$lang['notice_operation_inhibit']}')");
		}
		
		unset($content, $exist);
		
		return $_response;
	}
	
	/**
	* delete saved search
	*
	* @package xAjax
	*
	* @param string $id  - search id
	*
	**/
	function ajaxDeleteSavedSearch( $id )
	{
		global $_response, $account_info, $lang;

		if ( defined( 'IS_LOGIN' ) )
		{
			$id = (int)$id;
			$info = $this -> fetch( array('ID', 'Account_ID'), array( 'ID' => $id ), null, 1, 'saved_search', 'row' );

			if ( $info['Account_ID'] == $account_info['ID'] )
			{
				$this -> query( "DELETE FROM `". RL_DBPREFIX ."saved_search` WHERE `ID` = '{$info['ID']}' LIMIT 1" );

				$this -> setTable('saved_search');
				$fav = $this -> fetch( array('ID'), array('Account_ID' => $account_info['ID']) );
				
				if ( empty($fav) )
				{
					$empty_mess = '<div class="info">'.$lang['no_saved_search'].'</div>';
					$_response -> assign( 'saved_search_obj', 'innerHTML', $empty_mess );
				}
				
				$_response -> script( "$('#item_{$id}').fadeOut('slow');" );
				$_response -> script( "printMessage('notice', '{$lang['notice_saved_search_deleted']}');" );
			}
		}
		
		return $_response;
	}
	
	/**
	* activate/deactivate/delete saved search
	*
	* @package xAjax
	*
	* @param string $id  - search id
	*
	**/
	function ajaxMassSavedSearch( $id = false, $action = 'activate' )
	{
		global $_response, $account_info, $lang;

		$items = explode( '|', $id );

		if ( defined( 'IS_LOGIN' ) )
		{
			$status = $action == 'activate' ? 'active' : 'approval';
			
			foreach ( $items as $item )
			{
				if ( !empty($item) )
				{
					if ( $action == 'delete' )
					{
						$sql = "DELETE FROM `". RL_DBPREFIX ."saved_search` WHERE `ID` = '{$item}' AND `Account_ID` = '{$account_info['ID']}' LIMIT 1";
						$this -> query( $sql );
						
						$_response -> script( "$('#item_{$item}').fadeOut('slow');" );
					}
					else
					{
						$sql = "UPDATE `". RL_DBPREFIX ."saved_search` SET `Status` = '{$status}' WHERE `ID` = '{$item}' AND `Account_ID` = '{$account_info['ID']}' LIMIT 1";
						$this -> query( $sql );
						
						$html = '<span class="'. $status .'">'. $lang[$status] .'</span>';
						$_response -> assign( 'status_'.$item, 'innerHTML', $html );
					}
				}
			}
			
			if ( !empty($sql) )
			{
				if ( $action == 'delete' )
				{
					$mess = $lang['notice_items_deleted'];
				}
				else
				{
					$mess = $action == 'activate' ? $lang['notice_items_activated'] : $lang['notice_items_deactivated'];
				}
				$_response -> script("printMessage('notice', '{$mess}')");
			}
		}
		
		return $_response;
	}
	
	/**
	* check saved search
	*
	* @package xAjax
	*
	* @param string $id  - search id
	*
	**/
	function ajaxCheckSavedSearch( $id = false )
	{
		global $_response, $pages, $page_info, $account_info, $config, $rlListingTypes, $search_results_url, $rlActions;

		$id = (int)$id;

		if ( defined( 'IS_LOGIN' ) )
		{
			$search = $this -> fetch( array('ID', 'Form_key', 'Content', 'Listing_type'), array('ID' => $id, 'Account_ID' => $account_info['ID']), null, 1, 'saved_search', 'row' );
			$listing_type = $search['Listing_type'];
			
			$update = array(
				'fields' => array('Date' => 'NOW()'),
				'where' => array('ID' => $search['ID'])
			);
			$rlActions -> updateOne($update, 'saved_search');
			
			$data = unserialize( $search['Content'] );

			$_SESSION['post_form_key'] = $search['Form_key'];
			$_SESSION[$listing_type .'_post'] = $data;
			
			$url = $config['mod_rewrite'] ? SEO_BASE . $pages[$rlListingTypes -> types[$listing_type]['Page_key']] .'/'. $search_results_url .'.html' : SEO_BASE .'?page='. $pages[$rlListingTypes -> types[$listing_type]['Page_key']] .'&'. $search_results_url;
			$_response -> redirect($url);
		}
		
		return $_response;
	}
	
	/**
	* build search forms, depends of the forms count, listing types relations and arrange settings
	*
	* @todo - build forms and assign them to SMARTY
	*
	**/
	function getHomePageSearchForm()
	{
		global $rlListingTypes, $rlSmarty, $rlHook, $home_search_forms, $lang;
		
		/* get search forms */
		foreach ($rlListingTypes -> types as $type_key => $listing_type)
		{
			if ( $listing_type['Search_home'] )
			{
				$type_form_number++;
				$active_form_key = $type_key;
				$active_type = $rlListingTypes -> types[$active_form_key];
			}
		}
		
		if ( !$type_form_number )
			return false;
		
		/* get forms by listing types */
		if ( $type_form_number > 1 )
		{
			foreach ($rlListingTypes -> types as $type_key => $listing_type)
			{
				if ( $listing_type['Search_home'] )
				{
					if ( $search_form = $this -> buildSearch( $type_key .'_quick', $type_key ) )
					{
						$form_key = $type_key .'_quick';
						$home_search_forms[$form_key]['data'] = $search_form;
						$home_search_forms[$form_key]['name'] = $lang['search_forms+name+'. $form_key];
						$home_search_forms[$form_key]['listing_type'] = $type_key;
					}
				}
			}
		}
		/* get arranged (optional) search forms by signle type */
		elseif ( $type_form_number == 1 )
		{
			if ( $active_type['Arrange_field'] && $active_type['Arrange_search'] )
			{
				$arrange_values = explode(',', $active_type['Arrange_values']);
				
				foreach ($arrange_values as $arrange_value)
				{
					$form_key = $active_form_key .'_tab'. $arrange_value;
					if ( $search_form = $this -> buildSearch($form_key, $active_form_key) )
					{
						$home_search_forms[$form_key]['data'] = $search_form;
						$home_search_forms[$form_key]['name'] = $lang['search_forms+name+'. $form_key];
						$home_search_forms[$form_key]['listing_type'] = $active_form_key;
						$home_search_forms[$form_key]['arrange_field'] = $active_type['Arrange_field'];
						$home_search_forms[$form_key]['arrange_value'] = $arrange_value;
					}
				}
			}
			else
			{
				if ( $search_form = $this -> buildSearch( $active_form_key .'_quick', $active_form_key ) )
				{
					$form_key = $active_form_key .'_quick';
					$home_search_forms[$active_form_key]['data'] = $search_form;
					$home_search_forms[$active_form_key]['name'] = $lang['search_forms+name+'. $form_key];
					$home_search_forms[$active_form_key]['listing_type'] = $active_form_key;
				}
			}
		}
		
		$rlHook -> load('phpHomeSearchForms');

		$rlSmarty -> assign_by_ref( 'search_forms', $home_search_forms );
	}
}