<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

add_filter( 'wp_translator', 'QTX_Translator::get_translator' );

function qtranxf_get_front_page_config() {
	static $page_configs;//cache
	if ( $page_configs ) {
		return $page_configs;
	}

	global $q_config;
	$url_path  = $q_config['url_info']['wp-path'];
	$url_query = isset( $q_config['url_info']['query'] ) ? $q_config['url_info']['query'] : '';

	$front_config = $q_config['front_config'];
	/**
	 * Customize the front configuration for all pages.
	 *
	 * @param (array) $front_config token 'front-config' of the configuration.
	 */
	$front_config = apply_filters( 'i18n_front_config', $front_config );
	//qtranxf_dbg_log('qtranxf_get_front_page_config: $front_config: ', json_encode($front_config,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));

	$page_configs = qtranxf_parse_page_config( $front_config, $url_path, $url_query );

	return $page_configs;
}

function qtranxf_wp_head() {
	global $q_config;

	if ( $q_config['header_css_on'] ) {
		echo '<style type="text/css">' . PHP_EOL . $q_config['header_css'] . '</style>' . PHP_EOL;
	}

	if ( is_404() ) {
		return;
	}

	// set links to translations of current page
	foreach ( $q_config['enabled_languages'] as $lang ) {
		if ( ! empty( $q_config['locale_html'][ $lang ] ) ) {
			$hreflang = $q_config['locale_html'][ $lang ];
		} else {
			$hreflang = $lang;
		}
		//if($language != qtranxf_getLanguage())//standard requires them all
		echo '<link hreflang="' . $hreflang . '" href="' . qtranxf_convertURL( '', $lang, false, true ) . '" rel="alternate" />' . PHP_EOL;
	}
	//https://support.google.com/webmasters/answer/189077
	echo '<link hreflang="x-default" href="' . qtranxf_convertURL( '', $q_config['default_language'] ) . '" rel="alternate" />' . PHP_EOL;
}

add_action( 'wp_head', 'qtranxf_wp_head' );

/**
 * Moved line '<meta name="generator"' to a separate action.
 * Developers may use code
 *   remove_action('wp_head','qtranxf_wp_head_meta_generator');
 * to remove this line from the header.
 * @since 3.4.5.4
 */
function qtranxf_wp_head_meta_generator() {
	echo '<meta name="generator" content="qTranslate-XT ' . QTX_VERSION . '" />' . PHP_EOL;
}

add_action( 'wp_head', 'qtranxf_wp_head_meta_generator' );

function qtranxf_wp_get_nav_menu_items( $items, $menu, $args ) {
	global $q_config;
	$language     = $q_config['language'];
	$itemid       = 0;
	$menu_order   = 0;
	$itemsremoved = array();
	$qtransmenus  = array();
	//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: count(items)='.count($items).'; args: ', $args);//,true);
	//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: count(items)=',count($items));
	foreach ( $items as $key => $item ) {
		if ( isset( $item->item_lang ) ) {
			continue;
		}
		if ( isset( $itemsremoved[ $item->menu_item_parent ] ) ) {
			$itemsremoved[ $item->ID ] = $item;
			unset( $items[ $key ] );//remove a child of removed item
			continue;
		}
		$item->item_lang = $language;
		//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: $item->url: ',$item->url);
		//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: $item: ',$item);
		//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: item: '.$item->title.'; p='.$item->menu_item_parent.'; ID='.$item->ID);
		$qtransLangSw = isset( $item->url ) && stristr( $item->url, 'qtransLangSw' ) !== false;
		if ( ! $qtransLangSw ) {
			$item_title = $item->title;
			if ( ! empty( $item_title ) ) {
				if ( empty( $item->post_title ) && ! qtranxf_isMultilingual( $item_title ) ) {
					//$item does not have custom menu title, then it fetches information from post_title, but it is already translated with ShowEmpty=false, which gives either valid translation or possibly something like "(English) English Text". We need to translate again and to skip menu item if translation does not exist.
					switch ( $item->type ) {
						case 'post_type':
							$p = get_post( $item->object_id );
							if ( $p ) {
								$post_title_ml = isset( $p->post_title_ml ) ? $p->post_title_ml : $p->post_title;
								$item_title    = qtranxf_use_language( $language, $post_title_ml, false, true );
							}
							break;
						case 'taxonomy':
							//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: taxonomy: $item: ',$item);
							$term = wp_cache_get( $item->object_id, $item->object );
							if ( $term ) {
								//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: $term: ',$term);
								if ( isset( $q_config['term_name'][ $term->name ][ $language ] ) ) {
									$item_title = $q_config['term_name'][ $term->name ][ $language ];
								} else {
									$item_title = '';
								}
								if ( ! empty( $term->description ) ) {
									$item->description = $term->description;
								}
							}
							break;
					}
				} else {
					//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: $item->title && $item->post_title: $item: ',$item);
					$item_title = qtranxf_use_language( $language, $item_title, false, true );
				}
			}
			//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: $item_title: ',$item_title);
			if ( empty( $item_title ) ) {
				//qtranxf_dbg_log('removed item: '.$item->title.'; p='.$item->menu_item_parent);
				$itemsremoved[ $item->ID ] = $item;
				unset( $items[ $key ] );//remove menu item with empty title for this language
				continue;
			}
			$item->title = $item_title;
			if ( $item->object == 'custom' && ! empty( $item->url ) ) {
				if ( strpos( $item->url, 'setlang=no' ) === false ) {
					$item->url = qtranxf_convertURL( $item->url, $language );
				} else {
					$item->url = remove_query_arg( 'setlang', $item->url );
				}
				$i = strpos( $item->url, '#?lang=' );
				if ( $i !== false ) {
					$lang            = substr( $item->url, $i + 7, 2 );
					$item->url       = qtranxf_convertURL( '', $lang, false, true );
					$item->item_lang = $lang;
				}
			}
		}

		$item->post_content = qtranxf_use_language( $language, $item->post_content, false, true );
		$item->post_title   = qtranxf_use_language( $language, $item->post_title, false, true );
		$item->post_excerpt = qtranxf_use_language( $language, $item->post_excerpt, false, true );
		$item->description  = qtranxf_use_language( $language, $item->description, false, true );
		if ( isset( $item->attr_title ) ) {
			$item->attr_title = qtranxf_use_language( $language, $item->attr_title, false, true );
		}

		if ( $itemid < $item->ID ) {
			$itemid = $item->ID;
		}
		if ( $menu_order < $item->menu_order ) {
			$menu_order = $item->menu_order;
		}
		if ( ! $qtransLangSw ) {
			continue;
		}
		$qtransmenus[ $key ] = $item;
	}

	//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: $itemsremoved: ',$itemsremoved);
	if ( ! empty( $itemsremoved ) ) {
		qtranxf_remove_detached_children( $items, $itemsremoved );
	}

	if ( ! empty( $qtransmenus ) ) {
		foreach ( $qtransmenus as $key => $item ) {
			$nlang = count( $items );
			qtranxf_add_language_menu_item( $items, $menu_order, $itemid, $key, $language );
			$nlang       = count( $items ) - $nlang;
			$menu->count += $nlang;
			$menu_order  += $nlang;
		}
	}

	//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: done: $items: ',$items);
	return $items;
}

add_filter( 'wp_get_nav_menu_items', 'qtranxf_wp_get_nav_menu_items', 20, 3 );

function qtranxf_add_language_menu_item( &$items, &$menu_order, &$itemid, $key, $language ) {
	global $q_config;
	//qtranxf_dbg_log('qtranxf_add_language_menu_item: $key: ',$key);
	$item          = $items[ $key ];
	$flag_location = qtranxf_flag_location();
	$altlang       = null;
	$url           = '';
	//options
	$type       = 'LM'; // [LM|AL]
	$title      = 'Language'; // [none|Language|Current]
	$current    = true; // [shown|hidden]
	$flags      = true; // [none|all|items]
	$lang_names = true; // names=[shown|hidden]
	$colon      = true; // [shown|hidden]
	$topflag    = true;

	$p = strpos( $item->url, '?' );
	if ( $p !== false ) {
		$qs   = substr( $item->url, $p + 1 );
		$qs   = str_replace( '#', '', $qs );
		$pars = array();
		parse_str( $qs, $pars );
		if ( isset( $pars['type'] ) && stripos( $pars['type'], 'AL' ) !== false ) {
			$type = 'AL';
		}
		if ( isset( $pars['flags'] ) ) {
			$flags = ( stripos( $pars['flags'], 'no' ) === false );
			if ( $flags ) {
				$topflag = ( stripos( $pars['flags'], 'items' ) === false );
			} else {
				$topflag = false;
			}
		}
		if ( isset( $pars['names'] ) ) {
			$lang_names = ( stripos( $pars['names'], 'hid' ) === false );
		}
		if ( isset( $pars['title'] ) ) {
			$title = $pars['title'];
			if ( stripos( $pars['title'], 'no' ) !== false ) {
				$title = '';
			}
			if ( ! $topflag && empty( $title ) ) {
				$title = 'Language';
			}
		}
		if ( isset( $pars['colon'] ) ) {
			$colon = ( stripos( $pars['colon'], 'hid' ) === false );
		}
		if ( isset( $pars['current'] ) ) {
			$current = ( stripos( $pars['current'], 'hid' ) === false );
		}
		if ( ! $lang_names && ! $flags ) {
			$flags = true;
		}
	}
	$toplang = $language;
	if ( $type == 'AL' ) {
		foreach ( $q_config['enabled_languages'] as $lang ) {
			if ( $lang == $language ) {
				continue;
			}
			$toplang = $lang;
			$altlang = $lang;
			break;
		}
		$item->title = empty( $title ) ? '' : $q_config['language_name'][ $toplang ];
		$item->url   = qtranxf_convertURL( $url, $altlang, false, true );
	} else {
		if ( empty( $title ) ) {
			$item->title = '';
		} elseif ( stripos( $title, 'Current' ) !== false ) {
			$item->title = $q_config['language_name'][ $toplang ];
		} else {
			$blocks = qtranxf_get_language_blocks( $item->title );
			if ( count( $blocks ) <= 1 ) {//no customization is done
				$item->title = qtranxf_translate_wp( 'Language' );    // translators: expected in WordPress default textdomain
			} else {
				$item->title = qtranxf_use_block( $language, $blocks );
			}
		}
		$item->url = '#';
	}
	if ( $topflag ) {
		if ( ! empty( $item->title ) ) {
			if ( $colon ) {
				$item->title = sprintf( __( '%s:', 'qtranslate' ), $item->title );
			}    // translators: Colon after a title. For example, in top item of Language Menu.
			$item->title .= '&nbsp;';
		}
		$item->title .= '<img src="' . $flag_location . $q_config['flag'][ $toplang ] . '" alt="' . $q_config['language_name'][ $toplang ] . '" />';//.' '.__('Flag', 'qtranslate')
	}
	if ( empty( $item->attr_title ) ) {
		$item->attr_title = $q_config['language_name'][ $toplang ];
	}
	$item->classes[] = 'qtranxs-lang-menu';
	$item->classes[] = 'qtranxs-lang-menu-' . $toplang;
	//qtranxf_dbg_log('qtranxf_wp_get_nav_menu_items: top $item: ',$item);
	$qtransmenu = $item;

	// find children in case this function was already applied (customize.php on menu change)
	foreach ( $items as $k => $item ) {
		if ( $item->menu_item_parent != $qtransmenu->ID ) {
			continue;
		}
		unset( $items[ $k ] );
	}

	foreach ( $q_config['enabled_languages'] as $lang ) {
		if ( $type == 'AL' ) {
			if ( $lang == $language ) {
				continue;
			}
			if ( $lang == $altlang ) {
				continue;
			}
		} elseif ( ! $current ) {
			if ( $lang == $language ) {
				continue;
			}
		}
		$item = new WP_Post( (object) array( 'ID' => ++ $itemid ) );

		// add properties required for nav_menu_item, whose absense causes class-wp-customize-setting.php to throw Exception in function __construct
		$item->target      = '';
		$item->description = '';
		$item->xfn         = '';

		// set properties for language menu item
		$item->menu_item_parent = $qtransmenu->ID;
		$item->menu_order       = ++ $menu_order;
		$item->post_type        = 'nav_menu_item';
		$item->object           = 'custom';
		$item->object_id        = $qtransmenu->object_id;
		$item->type             = 'custom';
		$item->type_label       = 'Custom';
		$item->title            = '';
		if ( $flags ) {
			$item->title = '<img src="' . $flag_location . $q_config['flag'][ $lang ] . '" alt="' . $q_config['language_name'][ $lang ] . '" />';
		}
		if ( $lang_names ) {
			if ( $flags ) {
				$item->title .= '&nbsp;';
			}
			$item->title .= $q_config['language_name'][ $lang ];
		}
		$item->post_title = $item->title;
		$item->post_name  = 'language-menuitem-' . $lang;
		$item->url = qtranxf_convertURL( $url, $lang, false, true );
		$item->url = esc_url( $item->url );//not sure if this is needed
		$item->attr_title = $q_config['language_name'][ $lang ];
		$item->classes    = array();
		$item->classes[] = 'qtranxs-lang-menu-item';
		$item->classes[] = 'qtranxs-lang-menu-item-' . $lang;
		//qtx specific properties
		$item->item_lang = $lang; // to store the language assigned
		$items[]         = $item;
		//qtranxf_dbg_log('qtranxf_add_language_menu_item: language menu $item',$item);
	}
}

function qtranxf_remove_detached_children( &$items, &$itemsremoved ) {
	do {
		$more = false;
		foreach ( $items as $key => $item ) {
			if ( $item->menu_item_parent == 0 ) {
				continue;
			}
			if ( ! isset( $itemsremoved[ $item->menu_item_parent ] ) ) {
				continue;
			}
			$itemsremoved[ $item->ID ] = $item;
			unset( $items[ $key ] );
			$more = true;
			//qtranxf_dbg_log('qtranxf_remove_detached_children: removed: $key='.$key.'; $item: ',$item);
		}
	} while ( $more );
}

/**
 * @param (mixed) $value to translate, which may be array, object or string
 *                and may have serialized parts with embedded multilingual values.
 *
 * @since 3.3.8.9
 *
 */
function qtranxf_translate_deep( $value, $lang ) {
	if ( is_string( $value ) ) {
		if ( ! qtranxf_isMultilingual( $value ) ) {
			return $value;
		} //most frequent case
		if ( is_serialized( $value ) ) {
			$value = unserialize( $value );
			$value = qtranxf_translate_deep( $value, $lang );//recursive call

			return serialize( $value );
		}
		$lang_value = qtranxf_use_language( $lang, $value );

		return $lang_value;
	} else if ( is_array( $value ) ) {
		foreach ( $value as $k => $v ) {
			$value[ $k ] = qtranxf_translate_deep( $v, $lang );
		}
	} else if ( is_object( $value ) || $value instanceof __PHP_Incomplete_Class ) {
		foreach ( get_object_vars( $value ) as $k => $v ) {
			if ( ! isset( $value->$k ) ) {
				continue;
			}
			$value->$k = qtranxf_translate_deep( $v, $lang );
		}
	}

	return $value;
}

/**
 * @since 3.3.8.9
 * Used to filter option values
 */
function qtranxf_translate_option( $value, $lang = null ) {
	global $q_config;
	if ( ! $lang ) {
		$lang = $q_config['language'];
	}

	return qtranxf_translate_deep( $value, $lang );
}

/**
 * Filter all options for language tags
 */
function qtranxf_filter_options() {
	global $q_config, $wpdb;
	switch ( $q_config['filter_options_mode'] ) {
		case QTX_FILTER_OPTIONS_ALL:
			$where = ' WHERE autoload=\'yes\' AND (option_value LIKE \'%![:__!]%\' ESCAPE \'!\' OR option_value LIKE \'%{:__}%\' OR option_value LIKE \'%<!--:__-->%\')';
			break;

		case QTX_FILTER_OPTIONS_LIST:
			if ( empty( $q_config['filter_options'] ) ) {
				return;
			}
			$where = ' WHERE FALSE';
			foreach ( $q_config['filter_options'] as $nm ) {
				$where .= ' OR option_name LIKE "' . $nm . '"';
			}
			break;

		default:
			return;
	}
	$result = $wpdb->get_results( 'SELECT option_name FROM ' . $wpdb->options . $where );
	if ( ! $result ) {
		return;
	}
	foreach ( $result as $row ) {
		$option = $row->option_name;
		//qtranxf_dbg_log('add_filter: option_'.$option);
		add_filter( 'option_' . $option, 'qtranxf_translate_option', 5 );
	}
}

qtranxf_filter_options();

/**
 * @since 3.4.7
 */
function qtranxf_translate_object_property( $lang, $txt, $key, $post, $show_available, $show_empty ) {
	$blocks = qtranxf_get_language_blocks( $txt );
	if ( count( $blocks ) <= 1 ) {
		return;  // value is not multilingual
	}
	$key_ml           = $key . '_ml';
	$post->$key_ml    = $txt;
	$langs            = array();
	$content          = qtranxf_split_blocks( $blocks, $langs );
	$post->$key       = qtranxf_use_content( $lang, $content, $langs, $show_available, $show_empty );
	$key_langs        = $key . '_langs';
	$post->$key_langs = $langs;
}

/**
 * @since 3.4.6.5
 */
function qtranxf_translate_post( $post, $lang ) {
	foreach ( get_object_vars( $post ) as $key => $txt ) {
		switch ( $key ) {
			// known to skip
			case 'ID'://int
			case 'post_author':
			case 'post_date':
			case 'post_date_gmt':
			case 'post_status':
			case 'comment_status':
			case 'ping_status':
			case 'post_password':
			case 'post_name': //slug!
			case 'to_ping':
			case 'pinged':
			case 'post_modified':
			case 'post_modified_gmt':
			case 'post_parent': //int
			case 'guid':
			case 'menu_order': //int
			case 'post_type':
			case 'post_mime_type':
			case 'comment_count':
			case 'filter':
				break;
			// known to translate
			case 'post_content':
				qtranxf_translate_object_property( $lang, $txt, $key, $post, true, false );
				break;
			case 'post_excerpt':
			case 'post_content_filtered'://not sure how this is in use
			case 'post_title':
				qtranxf_translate_object_property( $lang, $txt, $key, $post, false, false );
				break;
			// other maybe, if it is a string, most likely it never comes here
			default:
				$post->$key = qtranxf_use( $lang, $txt, false );
		}
	}
}

function qtranxf_postsFilter( $posts, $query ) {
	global $q_config;
	//qtranxf_dbg_log('qtranxf_postsFilter: $posts: ',$posts);
	if ( ! is_array( $posts ) ) {
		return $posts;
	}
	//qtranxf_dbg_log('qtranxf_postsFilter: $post_type: ',$query->query_vars['post_type']);
	//qtranxf_dbg_log('qtranxf_postsFilter: query_vars: ',$query->query_vars);
	switch ( $query->query_vars['post_type'] ) {
		case 'nav_menu_item':
			return $posts;//will translate later in qtranxf_wp_get_nav_menu_items: to be able to filter empty labels.
		default:
			break;
	}
	$lang = $q_config['language'];
	// post is an object derived from WP_Post
	foreach ( $posts as $post ) {
		//qtranxf_dbg_log('qtranxf_postsFilter: ID='.$post->ID.'; post_type='.$post->post_type.'; $post->filter: ',$post->filter);
		qtranxf_translate_post( $post, $lang );
	}

	return $posts;
}

add_filter( 'the_posts', 'qtranxf_postsFilter', 5, 2 );

/** allow all filters within WP_Query - many other add_filters may not be needed now? */
function qtranxf_pre_get_posts( $query ) {//WP_Query
	//qtranxf_dbg_log('qtranxf_pre_get_posts: $query: ',$query);
	if ( isset( $query->query_vars['post_type'] ) ) {
		switch ( $query->query_vars['post_type'] ) {
			case 'nav_menu_item':
				return;
			default:
				break;
		}
	}
	$query->query_vars['suppress_filters'] = false;
}

add_action( 'pre_get_posts', 'qtranxf_pre_get_posts', 99 );

/**
 * since 3.1-b3 new query to pass empty content and content without closing tags (sliders, galleries and other special kind of posts that never get translated)
 */
function qtranxf_where_clause_translated_posts( $lang, $table_posts ) {
	$post_content = $table_posts . '.post_content';

	return "($post_content='' OR $post_content LIKE '%![:$lang!]%' ESCAPE '!' OR $post_content LIKE '%<!--:$lang-->%' OR ($post_content NOT LIKE '%![:!]%' ESCAPE '!' AND $post_content NOT LIKE '%<!--:-->%'))";
}

function qtranxf_excludePages( $pages ) {
	static $exclude = 0;
	if ( ! is_array( $exclude ) ) {
		global $wpdb;
		$lang       = qtranxf_getLanguage();
		$where      = qtranxf_where_clause_translated_posts( $lang, $wpdb->posts );
		$query      = "SELECT ID FROM $wpdb->posts WHERE post_type = 'page' AND post_status = 'publish' AND NOT " . $where;
		$hide_pages = $wpdb->get_results( $query );
		$exclude    = array();
		foreach ( $hide_pages as $page ) {
			$exclude[] = $page->ID;
		}
	}

	return array_merge( $exclude, $pages );
}

/**
 * @since 3.3.7
 * applied in /wp-includes/link-template.php on line
 *
 * $where = apply_filters( "get_{$adjacent}_post_where", $wpdb->prepare( "WHERE p.post_date $op %s AND p.post_type = %s $where", $current_post_date, $post->post_type ), $in_same_term, $excluded_terms );
 *
 */
function qtranxf_excludeUntranslatedAdjacentPosts( $where ) {
	$lang  = qtranxf_getLanguage();
	$where .= ' AND ' . qtranxf_where_clause_translated_posts( $lang, 'p' );

	//qtranxf_dbg_log('qtranxf_excludeUntranslatedAdjacentPosts: $where: ', $where);
	return $where;
}

function qtranxf_excludeUntranslatedPosts( $where, $query ) {//WP_Query
	//qtranxf_dbg_log('qtranxf_excludeUntranslatedPosts: post_type: ',$query->query_vars['post_type']);
	switch ( $query->query_vars['post_type'] ) {
		//known not to filter
		case 'nav_menu_item':
			return $where;
		//known to filter
		case '':
		case 'any':
		case 'page':
		case 'post':
		default:
			break;
	}
	//qtranxf_dbg_log('qtranxf_excludeUntranslatedPosts: post_type is empty: $query: ',$query, true);
	//qtranxf_dbg_log('qtranxf_excludeUntranslatedPosts: $where: ',$where);
	//qtranxf_dbg_log('qtranxf_excludeUntranslatedPosts: is_singular(): ',is_singular());
	$single_post_query = $query->is_singular();//since 3.1 instead of top is_singular()
	while ( ! $single_post_query ) {
		$single_post_query = preg_match( '/ID\s*=\s*[\'"]*(\d+)[\'"]*/i', $where, $matches ) == 1;
		if ( $single_post_query ) {
			break;
		}
		$single_post_query = preg_match( '/post_name\s*=\s*[^\s]+/i', $where, $matches ) == 1;
		break;
	}
	//qtranxf_dbg_log('qtranxf_excludeUntranslatedPosts: $single_post_query: ',$single_post_query);
	if ( ! $single_post_query ) {
		global $wpdb;
		$lang  = qtranxf_getLanguage();
		$where .= ' AND ' . qtranxf_where_clause_translated_posts( $lang, $wpdb->posts );
	}

	return $where;
}

function qtranxf_excludeUntranslatedPostComments( $clauses, $q/*WP_Comment_Query*/ ) {
	global $wpdb;

	//qtranxf_dbg_log('qtranxf_excludeUntranslatedPostComments: $clauses: ',$clauses);

	if ( ! isset( $clauses['join'] ) || empty( $clauses['join'] ) ) {
		$clauses['join'] = "JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
	} elseif ( strpos( $clauses['join'], $wpdb->posts ) === false ) {
		//do not break some more complex JOIN if it ever happens
		return $clauses;
	}
	//qtranxf_dbg_log('qtranxf_excludeUntranslatedPostComments: $wpdb->posts found');

	$single_post_query = is_singular();
	if ( $single_post_query && isset( $clauses['where'] ) ) {
		$single_post_query = preg_match( '/comment_post_ID\s*=\s*[\'"]*(\d+)[\'"]*/i', $clauses['where'] ) == 1;
	}
	if ( ! $single_post_query ) {
		$lang             = qtranxf_getLanguage();
		$clauses['where'] .= ' AND ' . qtranxf_where_clause_translated_posts( $lang, $wpdb->posts );
	}

	return $clauses;
}

function qtranxf_get_attachment_image_attributes( $attr, $attachment = null, $size = null ) {
	global $q_config;
	$lang = $q_config['language'];
	//qtranxf_dbg_echo('qtranxf_get_attachment_image_attributes: $attachment:',$attachment);
	if ( isset( $attr['alt'] ) ) {
		$attr['alt'] = qtranxf_use_language( $lang, $attr['alt'], false, false );
	}
	return $attr;
}

add_filter( 'wp_get_attachment_image_attributes', 'qtranxf_get_attachment_image_attributes', 5, 3 );

function qtranxf_home_url( $url, $path, $orig_scheme, $blog_id ) {
	global $q_config;
	$lang = $q_config['language'];
	//qtranxf_dbg_log('qtranxf_home_url: url='.$url.'; path='.$path.'; orig_scheme='.$orig_scheme);
	$url = qtranxf_get_url_for_language( $url, $lang, ! $q_config['hide_default_language'] || $lang != $q_config['default_language'] );

	//qtranxf_dbg_log('qtranxf_home_url: url='.$url.'; lang='.$lang);
	return $url;
}

function qtranxf_esc_html( $text ) {
	//qtranxf_dbg_echo('qtranxf_esc_html:text=',$text,true);
	/**
	 * since 3.1-b1
	 * used to return qtranxf_useDefaultLanguage($text)
	 */
	return qtranxf_useCurrentLanguageIfNotFoundShowEmpty( $text );
}

add_filter( 'esc_html', 'qtranxf_esc_html', 0 );

if ( ! function_exists( 'qtranxf_trim_words' ) ) {
// filter added in qtranslate_hooks.php
	function qtranxf_trim_words( $text, $num_words, $more, $original_text ) {
		global $q_config;
		$blocks = qtranxf_get_language_blocks( $original_text );
		if ( count( $blocks ) <= 1 ) {
			return $text;
		}
		$lang = $q_config['language'];
		$text = qtranxf_use_block( $lang, $blocks, true, false );

		return wp_trim_words( $text, $num_words, $more );
	}
}

/**
 * @since 3.2.9.9.6
 * Delete translated post_meta cache for all languages.
 * Cache may have a few languages, if it is persistent.
 */
function qtranxf_cache_delete_metadata( $meta_type, $object_id ) {//, $meta_key) {
	global $q_config;
	// maybe optimized to only replace the meta_key needed ?
	foreach ( $q_config['enabled_languages'] as $lang ) {
		$cache_key_lang = $meta_type . '_meta' . $lang;
		wp_cache_delete( $object_id, $cache_key_lang );
	}
}

/**
 * @since 3.2.3 translation of meta data
 * @since 3.4.6.4 improved caching algorithm
 */
function qtranxf_translate_metadata( $meta_type, $original_value, $object_id, $meta_key = '', $single = false ) {
	global $q_config;
	static $meta_cache_unserialized = array();
	if ( ! isset( $q_config['url_info'] ) ) {
		//qtranxf_dbg_log('qtranxf_translate_metadata: too early: $object_id='.$object_id.'; $meta_key',$meta_key,true);
		return $original_value;
	}
	//qtranxf_dbg_log('qtranxf_translate_metadata: $object_id='.$object_id.'; $meta_key=',$meta_key);

	$lang           = $q_config['language'];
	$cache_key      = $meta_type . '_meta';
	$cache_key_lang = $cache_key . $lang;

	$meta_cache_wp = wp_cache_get( $object_id, $cache_key );
	if ( $meta_cache_wp ) {
		// if there is wp cache, then we check if there is qtx cache
		$meta_cache = wp_cache_get( $object_id, $cache_key_lang );
	} else {
		// reset qtx cache, since it would not be valid in the absence of wp cache
		qtranxf_cache_delete_metadata( $meta_type, $object_id );
		$meta_cache = null;
	}

	if ( ! isset( $meta_cache_unserialized[ $meta_type ] ) ) {
		$meta_cache_unserialized[ $meta_type ] = array();
	}
	if ( ! isset( $meta_cache_unserialized[ $meta_type ][ $object_id ] ) ) {
		$meta_cache_unserialized[ $meta_type ][ $object_id ] = array();
	}
	$meta_unserialized = &$meta_cache_unserialized[ $meta_type ][ $object_id ];

	if ( ! $meta_cache ) {
		if ( $meta_cache_wp ) {
			$meta_cache = $meta_cache_wp;
		} else {
			$meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
			$meta_cache = $meta_cache[ $object_id ];
		}
		$meta_unserialized = array();//clear this cache if we are re-doing meta_cache
		//qtranxf_dbg_log('qtranxf_translate_metadata: $object_id='.$object_id.'; $meta_cache before:',$meta_cache);
		foreach ( $meta_cache as $mkey => $mval ) {
			$meta_unserialized[ $mkey ] = array();
			if ( strpos( $mkey, '_url' ) !== false ) {
				switch ( $mkey ) {
					case '_menu_item_url':
						break; // function qtranxf_wp_get_nav_menu_items takes care of this later
					default:
						foreach ( $mval as $k => $v ) {
							$s = is_serialized( $v );
							if ( $s ) {
								$v = unserialize( $v );
							}
							$v                                = qtranxf_convertURLs( $v, $lang );
							$meta_unserialized[ $mkey ][ $k ] = $v;
							if ( $s ) {
								$v = serialize( $v );
							}
							$meta_cache[ $mkey ][ $k ] = $v;
						}
						break;
				}
			} else {
				foreach ( $mval as $k => $v ) {
					if ( ! qtranxf_isMultilingual( $v ) ) {
						continue;
					}
					$s = is_serialized( $v );
					if ( $s ) {
						$v = unserialize( $v );
					}
					$v                                = qtranxf_use( $lang, $v, false, false );
					$meta_unserialized[ $mkey ][ $k ] = $v;
					if ( $s ) {
						$v = serialize( $v );
					}
					$meta_cache[ $mkey ][ $k ] = $v;
				}
			}
		}
		//qtranxf_dbg_log('qtranxf_translate_metadata: $object_id='.$object_id.'; $meta_cache  after:',$meta_cache);
		wp_cache_set( $object_id, $meta_cache, $cache_key_lang );
	}

	if ( ! $meta_key ) {
		if ( $single ) {
			return array( $meta_cache );
		}

		return $meta_cache;
	}

	if ( isset( $meta_cache[ $meta_key ] ) ) {
		// cache unserialized values, just for the sake of performance.
		$meta_key_unserialized = &$meta_unserialized[ $meta_key ];
		if ( $single ) {
			if ( ! isset( $meta_key_unserialized[0] ) ) {
				$meta_key_unserialized[0] = maybe_unserialize( $meta_cache[ $meta_key ][0] );
			}
		} else {
			foreach ( $meta_cache[ $meta_key ] as $k => $v ) {
				if ( ! isset( $meta_key_unserialized[ $k ] ) ) {
					$meta_key_unserialized[ $k ] = maybe_unserialize( $meta_cache[ $meta_key ][ $k ] );
				}
			}
		}

		return $meta_key_unserialized;
	}

	if ( $single ) {
		return '';
	} else {
		return array();
	}
}

/**
 * @since 3.2.3 translation of postmeta
 */
function qtranxf_filter_postmeta( $original_value, $object_id, $meta_key = '', $single = false ) {
	return qtranxf_translate_metadata( 'post', $original_value, $object_id, $meta_key, $single );
}

add_filter( 'get_post_metadata', 'qtranxf_filter_postmeta', 5, 4 );

/**
 * @since 3.2.9.9.6
 * Delete translated post_meta cache for all languages on cache update.
 * Cache may have a few languages, if it is persistent.
 */
function qtranxf_updated_postmeta( $meta_id, $object_id, $meta_key, $meta_value ) {
	qtranxf_cache_delete_metadata( 'post', $object_id );
}

add_action( 'updated_postmeta', 'qtranxf_updated_postmeta', 5, 4 );

/**
 * @since 3.4 translation of usermeta
 */
function qtranxf_filter_usermeta( $original_value, $object_id, $meta_key = '', $single = false ) {
	return qtranxf_translate_metadata( 'user', $original_value, $object_id, $meta_key, $single );
}

add_filter( 'get_user_metadata', 'qtranxf_filter_usermeta', 5, 4 );

/**
 * @since 3.4
 * Delete translated user_meta cache for all languages on cache update.
 * Cache may have a few languages, if it is persistent.
 */
function qtranxf_updated_usermeta( $meta_id, $object_id, $meta_key, $meta_value ) {
	qtranxf_cache_delete_metadata( 'user', $object_id );
}

add_action( 'updated_usermeta', 'qtranxf_updated_usermeta', 5, 4 );

function qtranxf_checkCanonical( $redirect_url, $requested_url ) {
	global $q_config;
	$lang = $q_config['language'];
	// fix canonical conflicts with language urls
	$redirect_url_lang = qtranxf_convertURL( $redirect_url, $lang );

	return $redirect_url_lang;
}

add_filter( 'redirect_canonical', 'qtranxf_checkCanonical', 10, 2 );

/**
 * @since 3.2.8 moved here from _hooks.php
 */
function qtranxf_convertBlogInfoURL( $url, $what ) {
	switch ( $what ) {
		case 'stylesheet_url':
		case 'template_url':
		case 'template_directory':
		case 'stylesheet_directory':
			return $url;
		default:
			return qtranxf_convertURL( $url );
	}
}

/**
 * @since 3.3.1
 * Moved here from qtranslate_hooks.php and modified.
 */
function qtranxf_pagenum_link( $url ) {
	$url_fixed = preg_replace( '#\?lang=[a-z]{2}/#i', '/', $url ); //kind of ugly fix for function get_pagenum_link in /wp-includes/link-template.php. Maybe we should cancel filter 'bloginfo_url' instead?

	return qtranxf_convertURL( $url_fixed );
}

add_filter( 'get_pagenum_link', 'qtranxf_pagenum_link' );

/**
 * @since 3.3.7
 */
function qtranxf_add_front_filters() {
	global $q_config;

	if ( $q_config['hide_untranslated'] ) {
		add_filter( 'wp_list_pages_excludes', 'qtranxf_excludePages' );//moved here from _hooks.php since 3.2.8
		add_filter( 'posts_where_request', 'qtranxf_excludeUntranslatedPosts', 10, 2 );
		add_filter( 'comments_clauses', 'qtranxf_excludeUntranslatedPostComments', 10, 2 );
		add_filter( 'get_previous_post_where', 'qtranxf_excludeUntranslatedAdjacentPosts' );
		add_filter( 'get_next_post_where', 'qtranxf_excludeUntranslatedAdjacentPosts' );
	}

	foreach ( $q_config['text_field_filters'] as $nm ) {
		add_filter( $nm, 'qtranxf_useCurrentLanguageIfNotFoundUseDefaultLanguage' );
	}

	$page_configs = qtranxf_get_front_page_config();
	//qtranxf_dbg_log('$page_configs: ', $page_configs);
	if ( ! empty( $page_configs['']['filters'] ) ) {
		qtranxf_add_filters( $page_configs['']['filters'] );
	}

	if ( $q_config['url_mode'] != QTX_URL_QUERY ) {
		/* WP uses line like 'trailingslashit( get_bloginfo( 'url' ) )' in /wp-includes/link-template.php, for example, which obviously breaks the further processing in QTX_URL_QUERY mode.
		*/
		add_filter( 'bloginfo_url', 'qtranxf_convertBlogInfoURL', 10, 2 );
		add_filter( 'home_url', 'qtranxf_home_url', 0, 4 );
	}

	// Hooks (execution time critical filters)
	// since 3.2.9.9.4 gettext* filters moved to frontend.php
	// they should not be needed on admin side and they, in particular, broke WPBakery Visual Composer in raw Editor Mode.
	add_filter( 'gettext', 'qtranxf_gettext', 0 );
	add_filter( 'gettext_with_context', 'qtranxf_gettext_with_context', 0 );
	add_filter( 'ngettext', 'qtranxf_ngettext', 0 );
}

qtranxf_add_front_filters();
