<?php

/**
 * @package     Joomla.Plugin
 * @subpackage  System.languagefilter
 *
 * @copyright   (C) 2010 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\Plugin\System\LanguageFilter\Extension;

use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\CMS\Association\AssociationServiceInterface;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\Folder;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\LanguageFactoryInterface;
use Joomla\CMS\Language\LanguageHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Router\Router;
use Joomla\CMS\Router\SiteRouterAwareTrait;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Menus\Administrator\Helper\MenusHelper;
use Joomla\Event\DispatcherInterface;
use Joomla\Filesystem\Path;
use Joomla\Registry\Registry;
use Joomla\String\StringHelper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Joomla! Language Filter Plugin.
 *
 * @since  1.6
 */
final class LanguageFilter extends CMSPlugin
{
    use SiteRouterAwareTrait;

    /**
     * The routing mode.
     *
     * @var    boolean
     * @since  2.5
     */
    protected $mode_sef;

    /**
     * Available languages by sef.
     *
     * @var    array
     * @since  1.6
     */
    protected $sefs;

    /**
     * Available languages by language codes.
     *
     * @var    array
     * @since  2.5
     */
    protected $lang_codes;

    /**
     * The current language code.
     *
     * @var    string
     * @since  3.4.2
     */
    protected $current_lang;

    /**
     * The default language code.
     *
     * @var    string
     * @since  2.5
     */
    protected $default_lang;

    /**
     * The logged user language code.
     *
     * @var    string
     * @since  3.3.1
     */
    private $user_lang_code;

    /**
     * The language factory
     *
     * @var   LanguageFactoryInterface
     *
     * @since 4.4.0
     */
    private $languageFactory;

    /**
     * Constructor.
     *
     * @param   DispatcherInterface       $dispatcher       The dispatcher
     * @param   array                     $config           An optional associative array of configuration settings
     * @param   CMSApplicationInterface   $app              The language factory
     * @param   LanguageFactoryInterface  $languageFactory  The language factory
     *
     * @since   1.6.0
     */
    public function __construct(
        DispatcherInterface $dispatcher,
        array $config,
        CMSApplicationInterface $app,
        LanguageFactoryInterface $languageFactory
    ) {
        parent::__construct($dispatcher, $config);

        $this->languageFactory = $languageFactory;

        $this->setApplication($app);

        // Setup language data.
        $this->mode_sef     = $this->getApplication()->get('sef', 0);
        $this->sefs         = LanguageHelper::getLanguages('sef');
        $this->lang_codes   = LanguageHelper::getLanguages('lang_code');
        $this->default_lang = ComponentHelper::getParams('com_languages')->get('site', 'en-GB');

        // If language filter plugin is executed in a site page.
        if ($this->getApplication()->isClient('site')) {
            $levels = $this->getApplication()->getIdentity()->getAuthorisedViewLevels();

            foreach ($this->sefs as $sef => $language) {
                // @todo: In Joomla 2.5.4 and earlier access wasn't set. Non modified Content Languages got 0 as access value
                // we also check if frontend language exists and is enabled
                if (
                    ($language->access && !\in_array($language->access, $levels))
                    || (!\array_key_exists($language->lang_code, LanguageHelper::getInstalledLanguages(0)))
                ) {
                    unset($this->lang_codes[$language->lang_code], $this->sefs[$language->sef]);
                }
            }
        } else {
            // If language filter plugin is executed in an admin page (ex: Route site).
            // Set current language to default site language, fallback to en-GB if there is no content language for the default site language.
            $this->current_lang = isset($this->lang_codes[$this->default_lang]) ? $this->default_lang : 'en-GB';

            foreach ($this->sefs as $sef => $language) {
                if (!\array_key_exists($language->lang_code, LanguageHelper::getInstalledLanguages(0))) {
                    unset($this->lang_codes[$language->lang_code]);
                    unset($this->sefs[$language->sef]);
                }
            }
        }
    }

    /**
     * After initialise.
     *
     * @return  void
     *
     * @since   1.6
     */
    public function onAfterInitialise()
    {
        $router = $this->getSiteRouter();

        // Attach build rules for language SEF.
        $router->attachBuildRule([$this, 'preprocessBuildRule'], Router::PROCESS_BEFORE);

        if ($this->mode_sef) {
            $router->attachBuildRule([$this, 'buildRule'], Router::PROCESS_BEFORE);
            $router->attachBuildRule([$this, 'postprocessSEFBuildRule'], Router::PROCESS_AFTER);
        } else {
            $router->attachBuildRule([$this, 'postprocessNonSEFBuildRule'], Router::PROCESS_AFTER);
        }

        // Attach parse rule.
        $router->attachParseRule([$this, 'parseRule'], Router::PROCESS_BEFORE);
    }

    /**
     * After route.
     *
     * @return  void
     *
     * @since   3.4
     */
    public function onAfterRoute()
    {
        // Add custom site name.
        if ($this->getApplication()->isClient('site') && isset($this->lang_codes[$this->current_lang]) && $this->lang_codes[$this->current_lang]->sitename) {
            $this->getApplication()->set('sitename', $this->lang_codes[$this->current_lang]->sitename);
        }
    }

    /**
     * Add build preprocess rule to router.
     *
     * @param   Router  &$router  Router object.
     * @param   Uri     &$uri     Uri object.
     *
     * @return  void
     *
     * @since   3.4
     */
    public function preprocessBuildRule(&$router, &$uri)
    {
        $lang = $uri->getVar('lang', $this->current_lang);

        if (isset($this->sefs[$lang])) {
            $lang = $this->sefs[$lang]->lang_code;
        }

        $uri->setVar('lang', $lang);
    }

    /**
     * Add build rule to router.
     *
     * @param   Router  &$router  Router object.
     * @param   Uri     &$uri     Uri object.
     *
     * @return  void
     *
     * @since   1.6
     */
    public function buildRule(&$router, &$uri)
    {
        $lang = $uri->getVar('lang');

        if (isset($this->lang_codes[$lang])) {
            $sef = $this->lang_codes[$lang]->sef;
        } else {
            $sef = $this->lang_codes[$this->current_lang]->sef;
        }

        if (
            !$this->params->get('remove_default_prefix', 0)
            || $lang !== $this->default_lang
            || $lang !== $this->current_lang
        ) {
            $uri->setPath($uri->getPath() . '/' . $sef . '/');
        }
    }

    /**
     * postprocess build rule for SEF URLs
     *
     * @param   Router  &$router  Router object.
     * @param   Uri     &$uri     Uri object.
     *
     * @return  void
     *
     * @since   3.4
     */
    public function postprocessSEFBuildRule(&$router, &$uri)
    {
        $uri->delVar('lang');
    }

    /**
     * postprocess build rule for non-SEF URLs
     *
     * @param   Router  &$router  Router object.
     * @param   Uri     &$uri     Uri object.
     *
     * @return  void
     *
     * @since   3.4
     */
    public function postprocessNonSEFBuildRule(&$router, &$uri)
    {
        $lang = $uri->getVar('lang');

        if (isset($this->lang_codes[$lang])) {
            $uri->setVar('lang', $this->lang_codes[$lang]->sef);
        }
    }

    /**
     * Add parse rule to router.
     *
     * @param   Router  &$router  Router object.
     * @param   Uri     &$uri     Uri object.
     *
     * @return  void
     *
     * @since   1.6
     */
    public function parseRule(&$router, &$uri)
    {
        // Did we find the current and existing language yet?
        $found = false;

        // Are we in SEF mode or not?
        if ($this->mode_sef) {
            $path  = $uri->getPath();
            $parts = explode('/', $path);

            $sef = StringHelper::strtolower($parts[0]);

            // Do we have a URL Language Code ?
            if (!isset($this->sefs[$sef])) {
                // Check if remove default URL language code is set
                if ($this->params->get('remove_default_prefix', 0)) {
                    if ($parts[0]) {
                        // We load a default site language page
                        $lang_code = $this->default_lang;
                    } else {
                        // We check for an existing language cookie
                        $lang_code = $this->getLanguageCookie();
                    }
                } else {
                    $lang_code = $this->getLanguageCookie();
                }

                // No language code. Try using browser settings or default site language
                if (!$lang_code && $this->params->get('detect_browser', 0) == 1) {
                    $lang_code = LanguageHelper::detectLanguage();
                }

                if (!$lang_code) {
                    $lang_code = $this->default_lang;
                }

                if ($lang_code === $this->default_lang && $this->params->get('remove_default_prefix', 0)) {
                    $found = true;
                }
            } else {
                // We found our language
                $found     = true;
                $lang_code = $this->sefs[$sef]->lang_code;

                // If we found our language, but it's the default language and we don't want a prefix for that, we are on a wrong URL.
                // Or we try to change the language back to the default language. We need a redirect to the proper URL for the default language.
                if ($lang_code === $this->default_lang && $this->params->get('remove_default_prefix', 0)) {
                    // Create a cookie.
                    $this->setLanguageCookie($lang_code);

                    $found = false;
                    array_shift($parts);
                    $path = implode('/', $parts);
                }

                // We have found our language and the first part of our URL is the language prefix
                if ($found) {
                    array_shift($parts);

                    // Empty parts array when "index.php" is the only part left.
                    if (\count($parts) === 1 && $parts[0] === 'index.php') {
                        $parts = [];
                    }

                    $uri->setPath(implode('/', $parts));
                }
            }
        } else {
            // We are not in SEF mode
            $lang_code = $this->getLanguageCookie();

            if (!$lang_code && $this->params->get('detect_browser', 1)) {
                $lang_code = LanguageHelper::detectLanguage();
            }

            if (!isset($this->lang_codes[$lang_code])) {
                $lang_code = $this->default_lang;
            }
        }

        $lang = $uri->getVar('lang', $lang_code);

        if (isset($this->sefs[$lang])) {
            // We found our language
            $found     = true;
            $lang_code = $this->sefs[$lang]->lang_code;
        }

        // We are called via POST or the nolangfilter url parameter was set. We don't care about the language
        // and simply set the default language as our current language.
        if (
            $this->getApplication()->getInput()->getMethod() === 'POST'
            || $this->getApplication()->getInput()->get('nolangfilter', 0) == 1
            || \count($this->getApplication()->getInput()->post) > 0
            || \count($this->getApplication()->getInput()->files) > 0
        ) {
            $found = true;

            if (!isset($lang_code)) {
                $lang_code = $this->getLanguageCookie();
            }

            if (!$lang_code && $this->params->get('detect_browser', 1)) {
                $lang_code = LanguageHelper::detectLanguage();
            }

            if (!isset($this->lang_codes[$lang_code])) {
                $lang_code = $this->default_lang;
            }
        }

        // We have not found the language and thus need to redirect
        if (!$found) {
            // Lets find the default language for this user
            if (!isset($lang_code) || !isset($this->lang_codes[$lang_code])) {
                $lang_code = false;

                if ($this->params->get('detect_browser', 1)) {
                    $lang_code = LanguageHelper::detectLanguage();

                    if (!isset($this->lang_codes[$lang_code])) {
                        $lang_code = false;
                    }
                }

                if (!$lang_code) {
                    $lang_code = $this->default_lang;
                }
            }

            if ($this->mode_sef) {
                // Use the current language sef or the default one.
                if (
                    $lang_code !== $this->default_lang
                    || !$this->params->get('remove_default_prefix', 0)
                ) {
                    $path = $this->lang_codes[$lang_code]->sef . '/' . $path;
                }

                $uri->setPath($path);

                if (!$this->getApplication()->get('sef_rewrite')) {
                    $uri->setPath('index.php/' . $uri->getPath());
                }

                $redirectUri = $uri->base() . $uri->toString(['path', 'query', 'fragment']);
            } else {
                $uri->setVar('lang', $this->lang_codes[$lang_code]->sef);
                $redirectUri = $uri->base() . 'index.php?' . $uri->getQuery();
            }

            // Set redirect HTTP code to "302 Found".
            $redirectHttpCode = 302;

            // If selected language is the default language redirect code is "301 Moved Permanently".
            if ($lang_code === $this->default_lang) {
                $redirectHttpCode = 301;

                // We cannot cache this redirect in browser. 301 is cacheable by default so we need to force to not cache it in browsers.
                $this->getApplication()->setHeader('Expires', 'Wed, 17 Aug 2005 00:00:00 GMT', true);
                $this->getApplication()->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true);
                $this->getApplication()->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate', false);
                $this->getApplication()->sendHeaders();
            }

            // Redirect to language.
            $this->getApplication()->redirect($redirectUri, $redirectHttpCode);
        }

        // We have found our language and now need to set the cookie and the language value in our system
        $this->current_lang = $lang_code;

        // Set the request var.
        $this->getApplication()->getInput()->set('language', $lang_code);
        $this->getApplication()->set('language', $lang_code);
        $language = $this->getApplication()->getLanguage();

        if ($language->getTag() !== $lang_code) {
            $language_new = $this->languageFactory->createLanguage($lang_code, (bool) $this->getApplication()->get('debug_lang'));

            foreach ($language->getPaths() as $extension => $files) {
                if (strpos($extension, 'plg_system') !== false) {
                    $extension_name = substr($extension, 11);

                    $language_new->load($extension, JPATH_ADMINISTRATOR)
                    || $language_new->load($extension, JPATH_PLUGINS . '/system/' . $extension_name);

                    continue;
                }

                $language_new->load($extension);
            }

            Factory::$language = $language_new;
            $this->getApplication()->loadLanguage($language_new);
        }

        // Create a cookie.
        if ($this->getLanguageCookie() !== $lang_code) {
            $this->setLanguageCookie($lang_code);
        }
    }

    /**
     * Reports the privacy related capabilities for this plugin to site administrators.
     *
     * @return  array
     *
     * @since   3.9.0
     */
    public function onPrivacyCollectAdminCapabilities()
    {
        $this->loadLanguage();

        return [
            $this->getApplication()->getLanguage()->_('PLG_SYSTEM_LANGUAGEFILTER') => [
                $this->getApplication()->getLanguage()->_('PLG_SYSTEM_LANGUAGEFILTER_PRIVACY_CAPABILITY_LANGUAGE_COOKIE'),
            ],
        ];
    }

    /**
     * Before store user method.
     *
     * Method is called before user data is stored in the database.
     *
     * @param   array    $user   Holds the old user data.
     * @param   boolean  $isnew  True if a new user is stored.
     * @param   array    $new    Holds the new user data.
     *
     * @return  void
     *
     * @since   1.6
     */
    public function onUserBeforeSave($user, $isnew, $new)
    {
        if (\array_key_exists('params', $user) && $this->params->get('automatic_change', 1) == 1) {
            $registry             = new Registry($user['params']);
            $this->user_lang_code = $registry->get('language');

            if (empty($this->user_lang_code)) {
                $this->user_lang_code = $this->current_lang;
            }
        }
    }

    /**
     * After store user method.
     *
     * Method is called after user data is stored in the database.
     *
     * @param   array    $user     Holds the new user data.
     * @param   boolean  $isnew    True if a new user is stored.
     * @param   boolean  $success  True if user was successfully stored in the database.
     * @param   string   $msg      Message.
     *
     * @return  void
     *
     * @since   1.6
     */
    public function onUserAfterSave($user, $isnew, $success, $msg): void
    {
        if ($success && \array_key_exists('params', $user) && $this->params->get('automatic_change', 1) == 1) {
            $registry  = new Registry($user['params']);
            $lang_code = $registry->get('language');

            if (empty($lang_code)) {
                $lang_code = $this->current_lang;
            }

            if ($lang_code === $this->user_lang_code || !isset($this->lang_codes[$lang_code])) {
                if ($this->getApplication()->isClient('site')) {
                    $this->getApplication()->setUserState('com_users.edit.profile.redirect', null);
                }
            } else {
                if ($this->getApplication()->isClient('site')) {
                    $this->getApplication()->setUserState('com_users.edit.profile.redirect', 'index.php?Itemid='
                        . $this->getApplication()->getMenu()->getDefault($lang_code)->id . '&lang=' . $this->lang_codes[$lang_code]->sef);

                    // Create a cookie.
                    $this->setLanguageCookie($lang_code);
                }
            }
        }
    }

    /**
     * Method to handle any login logic and report back to the subject.
     *
     * @param   array  $user     Holds the user data.
     * @param   array  $options  Array holding options (remember, autoregister, group).
     *
     * @return  null
     *
     * @since   1.5
     */
    public function onUserLogin($user, $options = [])
    {
        if ($this->getApplication()->isClient('site')) {
            $menu = $this->getApplication()->getMenu();

            if ($this->params->get('automatic_change', 1)) {
                $assoc     = Associations::isEnabled();
                $lang_code = $user['language'];

                // If no language is specified for this user, we set it to the site default language
                if (empty($lang_code)) {
                    $lang_code = $this->default_lang;
                }

                // The language has been deleted/disabled or the related content language does not exist/has been unpublished
                // or the related home page does not exist/has been unpublished
                if (
                    !\array_key_exists($lang_code, $this->lang_codes)
                    || !\array_key_exists($lang_code, Multilanguage::getSiteHomePages())
                    || !Folder::exists(JPATH_SITE . '/language/' . $lang_code)
                ) {
                    $lang_code = $this->current_lang;
                }

                // Try to get association from the current active menu item
                $active = $menu->getActive();

                $foundAssociation = false;

                /**
                 * Looking for associations.
                 * If the login menu item form contains an internal URL redirection,
                 * This will override the automatic change to the user preferred site language.
                 * In that case we use the redirect as defined in the menu item.
                 *  Otherwise we redirect, when available, to the user preferred site language.
                 */
                if ($active && !$active->getParams()->get('login_redirect_url')) {
                    if ($assoc) {
                        $associations = MenusHelper::getAssociations($active->id);
                    }

                    // Retrieves the Itemid from a login form.
                    $uri = new Uri($this->getApplication()->getUserState('users.login.form.return'));

                    if ($uri->getVar('Itemid')) {
                        // The login form contains a menu item redirection. Try to get associations from that menu item.
                        // If any association set to the user preferred site language, redirect to that page.
                        if ($assoc) {
                            $associations = MenusHelper::getAssociations($uri->getVar('Itemid'));
                        }

                        if (isset($associations[$lang_code]) && $menu->getItem($associations[$lang_code])) {
                            $associationItemid = $associations[$lang_code];
                            $this->getApplication()->setUserState('users.login.form.return', 'index.php?Itemid=' . $associationItemid);
                            $foundAssociation = true;
                        }
                    } elseif (isset($associations[$lang_code]) && $menu->getItem($associations[$lang_code])) {
                        /**
                         * The login form does not contain a menu item redirection.
                         * The active menu item has associations.
                         * We redirect to the user preferred site language associated page.
                         */
                        $associationItemid = $associations[$lang_code];
                        $this->getApplication()->setUserState('users.login.form.return', 'index.php?Itemid=' . $associationItemid);
                        $foundAssociation = true;
                    } elseif ($active->home) {
                        // We are on a Home page, we redirect to the user preferred site language Home page.
                        $item = $menu->getDefault($lang_code);

                        if ($item && $item->language !== $active->language && $item->language !== '*') {
                            $this->getApplication()->setUserState('users.login.form.return', 'index.php?Itemid=' . $item->id);
                            $foundAssociation = true;
                        }
                    }
                }

                if ($foundAssociation && $lang_code !== $this->current_lang) {
                    // Change language.
                    $this->current_lang = $lang_code;

                    // Create a cookie.
                    $this->setLanguageCookie($lang_code);

                    // Change the language code.
                    $this->languageFactory->createLanguage($lang_code);
                }
            } else {
                if ($this->getApplication()->getUserState('users.login.form.return')) {
                    $this->getApplication()->setUserState('users.login.form.return', Route::_($this->getApplication()->getUserState('users.login.form.return'), false));
                }
            }
        }
    }

    /**
     * Method to add alternative meta tags for associated menu items.
     *
     * @return  void
     *
     * @since   1.7
     */
    public function onAfterDispatch()
    {
        $doc = $this->getApplication()->getDocument();

        if ($this->getApplication()->isClient('site') && $this->params->get('alternate_meta', 1) && $doc->getType() === 'html') {
            $languages             = $this->lang_codes;
            $homes                 = Multilanguage::getSiteHomePages();
            $menu                  = $this->getApplication()->getMenu();
            $active                = $menu->getActive();
            $levels                = $this->getApplication()->getIdentity()->getAuthorisedViewLevels();
            $remove_default_prefix = $this->params->get('remove_default_prefix', 0);
            $server                = Uri::getInstance()->toString(['scheme', 'host', 'port']);
            $is_home               = false;

            // Router can be injected when turned into a DI built plugin
            $currentInternalUrl    = 'index.php?' . http_build_query($this->getSiteRouter()->getVars());

            if ($active) {
                $active_link  = Route::_($active->link . '&Itemid=' . $active->id);
                $current_link = Route::_($currentInternalUrl);

                // Load menu associations
                if ($active_link === $current_link) {
                    $associations = MenusHelper::getAssociations($active->id);
                }

                // Check if we are on the home page
                $is_home = ($active->home
                    && ($active_link === $current_link || $active_link === $current_link . 'index.php' || $active_link . '/' === $current_link));
            }

            // Load component associations.
            $option = $this->getApplication()->getInput()->get('option');

            $component = $this->getApplication()->bootComponent($option);

            if ($component instanceof AssociationServiceInterface) {
                $cassociations = $component->getAssociationsExtension()->getAssociationsForItem();
            } else {
                $cName = ucfirst(substr($option, 4)) . 'HelperAssociation';
                \JLoader::register($cName, Path::clean(JPATH_SITE . '/components/' . $option . '/helpers/association.php'));

                if (class_exists($cName) && \is_callable([$cName, 'getAssociations'])) {
                    $cassociations = \call_user_func([$cName, 'getAssociations']);
                }
            }

            // For each language...
            foreach ($languages as $i => $language) {
                switch (true) {
                    // Language without frontend UI || Language without specific home menu || Language without authorized access level
                    case !\array_key_exists($i, LanguageHelper::getInstalledLanguages(0)):
                    case !isset($homes[$i]):
                    case isset($language->access) && $language->access && !\in_array($language->access, $levels):
                        unset($languages[$i]);
                        break;

                    // Home page
                    case $is_home:
                        $language->link = Route::_('index.php?lang=' . $language->sef . '&Itemid=' . $homes[$i]->id);
                        break;

                    // Current language link
                    case $i === $this->current_lang:
                        $language->link = Route::_($currentInternalUrl);
                        break;

                    // Component association
                    case isset($cassociations[$i]):
                        $language->link = Route::_($cassociations[$i]);
                        break;

                    // Menu items association
                    // Heads up! "$item = $menu" here below is an assignment, *NOT* comparison
                    case isset($associations[$i]) && ($item = $menu->getItem($associations[$i])):
                        $language->link = Route::_('index.php?Itemid=' . $item->id . '&lang=' . $language->sef);
                        break;

                    // Too bad...
                    default:
                        unset($languages[$i]);
                }
            }

            // If there are at least 2 of them, add the rel="alternate" links to the <head>
            if (\count($languages) > 1) {
                // Remove the sef from the default language if "Remove URL Language Code" is on
                if ($remove_default_prefix && isset($languages[$this->default_lang])) {
                    $languages[$this->default_lang]->link
                                    = preg_replace('|/' . $languages[$this->default_lang]->sef . '/|', '/', $languages[$this->default_lang]->link, 1);
                }

                foreach ($languages as $i => $language) {
                    $doc->addHeadLink($server . $language->link, 'alternate', 'rel', ['hreflang' => $i]);
                }

                // Add x-default language tag
                if ($this->params->get('xdefault', 1)) {
                    $xdefault_language = $this->params->get('xdefault_language', $this->default_lang);
                    $xdefault_language = ($xdefault_language === 'default') ? $this->default_lang : $xdefault_language;

                    if (isset($languages[$xdefault_language])) {
                        // Use a custom tag because addHeadLink is limited to one URI per tag
                        $doc->addCustomTag('<link href="' . $server . $languages[$xdefault_language]->link . '" rel="alternate" hreflang="x-default">');
                    }
                }
            }
        }
    }

    /**
     * Set the language cookie
     *
     * @param   string  $languageCode  The language code for which we want to set the cookie
     *
     * @return  void
     *
     * @since   3.4.2
     */
    private function setLanguageCookie($languageCode)
    {
        // If is set to use language cookie for a year in plugin params, save the user language in a new cookie.
        if ((int) $this->params->get('lang_cookie', 0) === 1) {
            // Create a cookie with one year lifetime.
            $this->getApplication()->getInput()->cookie->set(
                ApplicationHelper::getHash('language'),
                $languageCode,
                [
                    'expires'  => time() + 365 * 86400,
                    'path'     => $this->getApplication()->get('cookie_path', '/'),
                    'domain'   => $this->getApplication()->get('cookie_domain', ''),
                    'secure'   => $this->getApplication()->isHttpsForced(),
                    'httponly' => true,
                ]
            );
        } else {
            // If not, set the user language in the session (that is already saved in a cookie).
            $this->getApplication()->getSession()->set('plg_system_languagefilter.language', $languageCode);
        }
    }

    /**
     * Get the language cookie
     *
     * @return  string
     *
     * @since   3.4.2
     */
    private function getLanguageCookie()
    {
        // Is is set to use a year language cookie in plugin params, get the user language from the cookie.
        if ((int) $this->params->get('lang_cookie', 0) === 1) {
            $languageCode = $this->getApplication()->getInput()->cookie->get(ApplicationHelper::getHash('language'));
        } else {
            // Else get the user language from the session.
            $languageCode = $this->getApplication()->getSession()->get('plg_system_languagefilter.language');
        }

        // Let's be sure we got a valid language code. Fallback to null.
        if (!\array_key_exists($languageCode, $this->lang_codes)) {
            $languageCode = null;
        }

        return $languageCode;
    }
}
