<?php

class WPML_LS_Model_Build extends WPML_SP_User {

	const LINK_CSS_CLASS = 'wpml-ls-link';

	/* @var WPML_LS_Settings $settings */
	private $settings;

	/* @var WPML_Mobile_Detect $mobile_detect */
	private $mobile_detect;

	/* @var bool $is_touch_screen */
	private $is_touch_screen = false;

	/* @var string $css_prefix */
	private $css_prefix;

	private $allowed_vars = [
		'languages'              => 'array',
		'current_language_code'  => 'string',
		'css_classes'            => 'string',
		'css_classes_link'       => 'string',
		'backward_compatibility' => 'array',
	];

	private $allowed_language_vars = [
		'code'                   => 'string',
		'url'                    => 'string',
		'flag_url'               => 'string',
		'flag_title'             => 'string',
		'flag_alt'               => 'string',
		'native_name'            => 'string',
		'display_name'           => 'string',
		'is_current'             => 'bool',
		'css_classes'            => 'string',
		'db_id'                  => 'string',
		'menu_item_parent'       => 'mixed',
		'is_parent'              => 'bool',
		'backward_compatibility' => 'array',
		'flag_width'             => 'int',
		'flag_height'            => 'int',
	];

	/**
	 * WPML_Language_Switcher_Render_Model constructor.
	 *
	 * @param WPML_LS_Settings $settings
	 * @param SitePress        $sitepress
	 * @param string           $css_prefix
	 */
	public function __construct( $settings, $sitepress, $css_prefix ) {
		$this->settings   = $settings;
		$this->css_prefix = $css_prefix;
		parent::__construct( $sitepress );
	}

	/**
	 * @param WPML_LS_Slot $slot
	 * @param array        $template_data
	 *
	 * @return array
	 */
	public function get( $slot, $template_data = [] ) {
		$vars = [];

		$vars['current_language_code'] = $this->sitepress->get_current_language();
		$vars['languages']             = $this->get_language_items( $slot, $template_data );
		$vars['css_classes']           = $this->get_slot_css_classes( $slot );
		$vars['css_classes_link']      = self::LINK_CSS_CLASS;

		$vars = $this->add_backward_compatibility_to_wrapper( $vars, $slot );

		return $this->sanitize_vars( $vars, $this->allowed_vars );
	}

	/**
	 * @param WPML_LS_Slot $slot
	 *
	 * @return string
	 */
	public function get_slot_css_classes( $slot ) {
		$classes = [ $this->get_slot_css_main_class( $slot->group(), $slot->slug() ) ];

		$classes[] = trim( $this->css_prefix, '-' );

		if ( $this->sitepress->is_rtl( $this->sitepress->get_current_language() ) ) {
			$classes[] = $this->css_prefix . 'rtl';
		}

		$classes = $this->add_user_agent_touch_device_classes( $classes );

		/**
		 * Filter the css classes for the language switcher wrapper
		 * The wrapper is not available for menus
		 *
		 * @param array $classes
		 */
		$classes = apply_filters( 'wpml_ls_model_css_classes', $classes );

		return implode( ' ', $classes );
	}

	/**
	 * @param string $group
	 * @param string $slug
	 *
	 * @return string
	 */
	public function get_slot_css_main_class( $group, $slug ) {
		return $this->css_prefix . $group . '-' . $slug;
	}

	/**
	 * @return string
	 */
	public function get_css_prefix() {
		return $this->css_prefix;
	}

	/**
	 * @param WPML_LS_Slot $slot
	 * @param array        $template_data
	 *
	 * @return array
	 */
	private function get_language_items( $slot, $template_data ) {
		$ret = [];


		$get_ls_args = [
			'skip_missing' => ! $this->settings->get_setting( 'link_empty' ),
		];

		if ( $slot->is_post_translations() ) {
			$get_ls_args['skip_missing'] = true;
		} elseif ( ( WPML_Root_Page::uses_html_root() || WPML_Root_Page::get_root_id() ) && WPML_Root_Page::is_current_request_root() ) {
			$get_ls_args['skip_missing'] = false;
		}

		$flag_width  = $slot->get( 'include_flag_width' );
		$flag_height = $slot->get( 'include_flag_height' );

		$languages = $this->sitepress->get_ls_languages( $get_ls_args );
		$languages = is_array( $languages ) ? $languages : [];

		$languages = $this->order_languages( $languages, $slot->get( 'language_order_by' ), $slot->get( 'language_order_by' ) );

		if ( $languages ) {

			foreach ( $languages as $code => $data ) {

				$is_current_language = $code === $this->sitepress->get_current_language();

				if ( ! $slot->get( 'display_link_for_current_lang' ) && $is_current_language ) {
					continue;
				}

				$ret[ $code ] = [
					'code' => $code,
					'url'  => $data['url'],
				];

				if ( $flag_width ) {
					$ret[ $code ]['flag_width'] = $flag_width;
				}
				if ( $flag_height ) {
					$ret[ $code ]['flag_height'] = $flag_height;
				}

				/* @deprecated Use 'wpml_ls_language_url' instead */
				$ret[ $code ]['url'] = apply_filters( 'WPML_filter_link', $ret[ $code ]['url'], $data );

				/**
				 * This filter allows to change the URL for each languages links in the switcher
				 *
				 * @param string $ret  [ $code ]['url'] The language URL to be filtered
				 * @param array  $data The language information
				 */
				$ret[ $code ]['url'] = apply_filters( 'wpml_ls_language_url', $ret[ $code ]['url'], $data );

				$ret[ $code ]['url'] = $this->sitepress->get_wp_api()->is_admin() ? '#' : $ret[ $code ]['url'];

				$css_classes = $this->get_language_css_classes( $slot, $code );

				$display_native = $slot->get( 'display_names_in_native_lang' );
				$display_name   = $slot->get( 'display_names_in_current_lang' );

				if ( $slot->get( 'display_flags' ) ) {
					$ret[ $code ]['flag_url']   = $this->filter_flag_url( $data['country_flag_url'], $template_data );
					$ret[ $code ]['flag_title'] = $data['native_name'];
					$ret[ $code ]['flag_alt']   = ( $display_name || $display_native ) ? '' : $data['translated_name'];
				}

				if ( $display_native ) {
					$ret[ $code ]['native_name'] = $data['native_name'];
				}

				if ( $display_name ) {
					$ret[ $code ]['display_name'] = $data['translated_name'];
				}

				if ( $is_current_language ) {
					$ret[ $code ]['is_current'] = true;
					array_push( $css_classes, $this->css_prefix . 'current-language' );
				}

				if ( $slot->is_menu() ) {
					$ret[ $code ]['db_id']            = $this->get_menu_item_id( $code, $slot );
					$ret[ $code ]['menu_item_parent'] = $slot->get( 'is_hierarchical' ) && ! $is_current_language
						? $this->get_menu_item_id( $this->sitepress->get_current_language(), $slot ) : 0;
					$ret[ $code ]['is_parent']        = $slot->get( 'is_hierarchical' ) && $is_current_language
						? true : false;

					array_unshift( $css_classes, 'menu-item' );
					array_push( $css_classes, $this->css_prefix . 'menu-item' );
				}

				$ret[ $code ]['css_classes'] = $css_classes;
			}

			$i = 1;
			foreach ( $ret as &$lang ) {
				if ( $i === 1 ) {
					array_push( $lang['css_classes'], $this->css_prefix . 'first-item' );
				}

				if ( $i === count( $ret ) ) {
					array_push( $lang['css_classes'], $this->css_prefix . 'last-item' );
				}

				$lang = $this->add_backward_compatibility_to_languages( $lang, $slot );

				/**
				 * Filter the css classes for each language item
				 *
				 * @param array $lang ['css_classes']
				 */
				$lang['css_classes'] = apply_filters( 'wpml_ls_model_language_css_classes', $lang['css_classes'] );

				$lang['css_classes'] = implode( ' ', $lang['css_classes'] );

				$lang = $this->sanitize_vars( $lang, $this->allowed_language_vars );
				$i++;
			}
		}

		return $ret;
	}

	/**
	 * @param array  $languages
	 * @param string $order_by
	 * @param string $order_way
	 *
	 * @return array
	 */
	private function order_languages( $languages, $order_by, $order_way ) {
		$ret = $languages;

		if ( ! empty( $order_by ) && ! empty( $order_way ) ) {
			$method = 'order_by_' . $order_by;
			if ( is_callable( [ $this, $method ] ) ) {
				uasort( $ret, [ $this, 'order_by_' . $order_by ] );
			}

			$ret = strtoupper( $order_way ) === 'ASC' ? array_reverse( $ret ) : $ret;
		}

		return $ret;
	}

	/**
	 * @param string $url
	 * @param array  $template_data
	 *
	 * @return string
	 */
	private function filter_flag_url( $url, $template_data = [] ) {
		$wp_upload_dir   = wp_upload_dir();
		$has_custom_flag = strpos( $url, $wp_upload_dir['baseurl'] . '/flags/' ) === 0 ? true : false;

		if ( ! $has_custom_flag && ! empty( $template_data['flags_base_uri'] ) ) {
			$url = trailingslashit( $template_data['flags_base_uri'] ) . basename( $url );

			if ( isset( $template_data['flag_extension'] ) ) {
				$old_ext = pathinfo( $url, PATHINFO_EXTENSION );
				$url     = preg_replace( '#' . $old_ext . '$#', $template_data['flag_extension'], $url, 1 );
			}
		}

		return $url;
	}

	/**
	 * @param WPML_LS_Slot $slot
	 * @param string       $code
	 *
	 * @return array
	 */
	private function get_language_css_classes( $slot, $code ) {
		return [
			$this->css_prefix . 'slot-' . $slot->slug(),
			$this->css_prefix . 'item',
			$this->css_prefix . 'item-' . $code,
		];
	}

	/**
	 * @param array $classes
	 *
	 * @return array
	 */
	private function add_user_agent_touch_device_classes( $classes ) {

		if ( is_null( $this->mobile_detect ) ) {
			require_once WPML_PLUGIN_PATH . '/lib/mobile-detect.php';
			$this->mobile_detect   = new WPML_Mobile_Detect();
			$this->is_touch_screen = $this->mobile_detect->isMobile() || $this->mobile_detect->isTablet();
		}

		if ( $this->is_touch_screen ) {
			$classes[] = $this->css_prefix . 'touch-device';
		}

		return $classes;
	}

	/**
	 * @return bool
	 */
	private function needs_backward_compatibility() {
		return (bool) $this->settings->get_setting( 'migrated' );
	}

	/**
	 * @param string       $code
	 * @param WPML_LS_Slot $slot
	 *
	 * @return string
	 */
	private function get_menu_item_id( $code, $slot ) {
		return $this->css_prefix . $slot->slug() . '-' . $code;
	}

	/**
	 * @param array $vars
	 * @param array $allowed_vars
	 *
	 * @return array
	 */
	private function sanitize_vars( $vars, $allowed_vars ) {
		$sanitized = [];

		foreach ( $allowed_vars as $allowed_var => $type ) {
			if ( isset( $vars[ $allowed_var ] ) ) {
				switch ( $type ) {
					case 'array':
						$sanitized[ $allowed_var ] = (array) $vars[ $allowed_var ];
						break;

					case 'string':
						$sanitized[ $allowed_var ] = (string) $vars[ $allowed_var ];
						break;

					case 'int':
						$sanitized[ $allowed_var ] = (int) $vars[ $allowed_var ];
						break;

					case 'bool':
						$sanitized[ $allowed_var ] = (bool) $vars[ $allowed_var ];
						break;

					case 'mixed':
						$sanitized[ $allowed_var ] = $vars[ $allowed_var ];
						break;
				}
			}
		}

		return $sanitized;
	}

	/**
	 * @param array        $lang
	 * @param WPML_LS_Slot $slot
	 *
	 * @return array
	 */
	private function add_backward_compatibility_to_languages( $lang, $slot ) {

		if ( $this->needs_backward_compatibility() ) {

			$is_current_language = isset( $lang['is_current'] ) && $lang['is_current'];

			if ( $slot->is_menu() ) {

				if ( $is_current_language ) {
					array_unshift( $lang['css_classes'], 'menu-item-language-current' );
				}

				array_unshift( $lang['css_classes'], 'menu-item-language' );
			}

			if ( $slot->is_sidebar() || $slot->is_shortcode_actions() ) {

				if ( $this->is_legacy_template( $slot->template(), 'list-vertical' )
					 || $this->is_legacy_template( $slot->template(), 'list-horizontal' )
				) {
					array_unshift( $lang['css_classes'], 'icl-' . $lang['code'] );
					$lang['backward_compatibility']['css_classes_a'] = $is_current_language ?
						'lang_sel_sel' : 'lang_sel_other';
				}

				if ( $this->is_legacy_template( $slot->template(), 'dropdown' )
					 || $this->is_legacy_template( $slot->template(), 'dropdown-click' )
				) {

					if ( $is_current_language ) {
						$lang['backward_compatibility']['css_classes_a'] = 'lang_sel_sel icl-' . $lang['code'];
					} else {
						array_unshift( $lang['css_classes'], 'icl-' . $lang['code'] );
					}
				}
			}
		}

		return $lang;
	}

	/**
	 * @param array        $vars
	 * @param WPML_LS_Slot $slot
	 *
	 * @return mixed
	 */
	private function add_backward_compatibility_to_wrapper( $vars, $slot ) {

		if ( $this->needs_backward_compatibility() ) {

			if ( $slot->is_sidebar() || $slot->is_shortcode_actions() ) {

				if ( $this->is_legacy_template( $slot->template(), 'list-vertical' )
					 || $this->is_legacy_template( $slot->template(), 'list-horizontal' )
				) {
					$vars['backward_compatibility']['css_id'] = 'lang_sel_list';

					if ( $this->is_legacy_template( $slot->template(), 'list-horizontal' ) ) {
						$vars['css_classes'] = 'lang_sel_list_horizontal ' . $vars['css_classes'];
					} else {
						$vars['css_classes'] = 'lang_sel_list_vertical ' . $vars['css_classes'];
					}
				}

				if ( $this->is_legacy_template( $slot->template(), 'dropdown' ) ) {
					$vars['backward_compatibility']['css_id'] = 'lang_sel';
				}

				if ( $this->is_legacy_template( $slot->template(), 'dropdown-click' ) ) {
					$vars['backward_compatibility']['css_id'] = 'lang_sel_click';
				}
			}

			if ( $slot->is_post_translations() ) {
				$vars['css_classes'] = 'icl_post_in_other_langs ' . $vars['css_classes'];
			}

			if ( $slot->is_footer() ) {
				$vars['backward_compatibility']['css_id'] = 'lang_sel_footer';
			}

			$vars['backward_compatibility']['css_classes_flag']    = 'iclflag';
			$vars['backward_compatibility']['css_classes_native']  = 'icl_lang_sel_native';
			$vars['backward_compatibility']['css_classes_display'] = 'icl_lang_sel_translated';
			$vars['backward_compatibility']['css_classes_bracket'] = 'icl_lang_sel_bracket';
		}

		return $vars;
	}

	/**
	 * @param string      $template_slug
	 * @param string|null $type
	 *
	 * @return bool
	 */
	private function is_legacy_template( $template_slug, $type = null ) {
		$templates = $this->settings->get_core_templates();
		$ret       = in_array( $template_slug, $templates, true );

		if ( $ret && array_key_exists( $type, $templates ) ) {
			$ret = $templates[ $type ] === $template_slug;
		}

		return $ret;
	}
}
