<?php

namespace Velis;

use Psr\SimpleCache\InvalidArgumentException;
use ReflectionException;
use Velis\Bpm\Ticket\Log;
use Velis\Bpm\Ticket\Status;
use Velis\Lang\Language;
use Velis\Lang\LangVariant;
use Velis\Lang\Value;
use Velis\Model\BaseModel;
use Velis\Model\DataObject;

/**
 * Language packages loader
 *
 * @author Olek Procki <olo@velis.pl>
 */
class Lang extends BaseModel
{
    public const ITEMS_PER_PAGE = 30;

    /**
     * Count items found in last query
     * @var int
     */
    public static $listItemsFound;

    /**
     * Behaviour constant: on empty key return null string
     */
    public const BHVR_RETURN_NULL         = 0;

    /**
     * Behaviour constant: on empty key return key passed to function
     */
    public const BHVR_RETURN_KEY          = 1;

    /**
     * Behaviour constant: on empty key return anchor leading to language editor
     */
    public const BHVR_RETURN_EDIT_LINK    = 2;

    /**
     * Behaviour constant: on empty key try to return english translation
     */
    public const BHVR_RETURN_EN           = 3;

    /**
     * Database timestamp pattern
     */
    public const DATETIME_PATTERN = '/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))( ([012]\d:[0-5]\d(:[0-5]\d)?))?/';


    public static $langDefinition         = array();
    public static $onEmpty                = self::BHVR_RETURN_KEY;

    public static $langsContainer         = array();

    /**
     * List of required langs
     * @var string[]
     */
    public static $requiredLangs = ['en', 'pl'];

    /**
     * Default lang settings
     * @var string
     */
    public static $lang = 'pl';


    /**
     * Default locale settings
     * @var string
     */
    public static $locale = 'pl_PL';


    /**
     * Is the selected language a right-to-left language?
     * @var string
     */
    public static $isRtl = false;


    protected static $_missingTranslations = array();


    /**
     * Available languages list
     * @var array
     */
    protected static $_languages;


    /**
     * Languages with right-to-left writing
     * @var array
     */
    protected static $_rtlLanguages = array();


    /**
     * Returns available languages dictionary
     * @return array
     */
    public static function getLanguages($additionalLang = null)
    {
        if (!isset(self::$_languages)) {
            self::$_languages    = App::$cache['languages'] ?: [];
            self::$_rtlLanguages = App::$cache['rtlLanguages'] ?: [];

            if (!self::$_languages || self::$_rtlLanguages === false) {
                self::$_languages    = array();
                self::$_rtlLanguages = array();

                $query = "SELECT * FROM app.lang_tab WHERE active = 1 ORDER BY lang_id ASC";

                foreach (self::$_db->getAll($query) as $lang) {
                    self::$_languages[$lang['lang_id']] = $lang['name'];

                    if ($lang['is_rtl']) {
                        self::$_rtlLanguages[] = $lang['lang_id'];
                    }
                }
                App::$cache['languages'] = self::$_languages;
                App::$cache['rtlLanguages'] = self::$_rtlLanguages;
            }
        }
        $langs = self::$_languages;

        if (App::isSuper(true)) {
            if (!isset($langs['pl'])) {
                $langs['pl'] = 'polski';
            }
        }

        if ($additionalLang != null) {
            $langs[$additionalLang] = null;
        }
        return $langs;
    }


    /**
     * Configures behaviour of language subsystem
     *
     * @param int[optional] $returnBehaviour - sets the way of returning empty lang variable (see BHVR_ constants)
     * @param string[optional] $lang - sets language acronym (PL, EN, SE etc)
     * @return void;
     */
    public static function configure($returnBehaviour = self::BHVR_RETURN_KEY, $lang = null)
    {
        if ($lang == null) {
            $lang = self::getDefaultLanguage();
        }

        self::$lang    = strtoupper($lang);
        self::$onEmpty = $returnBehaviour;

        setlocale(LC_TIME, App::$user->getLocale());
    }


    /**
     * Switches to another language pack
     *
     * @param string[optional] $newLang - language acro (pl, en etc);
     * @param bool $setupLocale
     *
     * @return bool
     */
    public static function switchLanguage($newLang = null, $setupLocale = true, $force = false, bool $setCookie = true)
    {
        if ($newLang == null) {
            $newLang = self::getDefaultLanguage();
        }
        if ($newLang != self::$lang && (array_key_exists($newLang, self::getLanguages()) || $force)) {
            self::$lang = $newLang;
            self::$isRtl = in_array($newLang, self::$_rtlLanguages ?: array());
        } else {
            return false;
        }

        self::$langDefinition = array();

        if ($setupLocale) {
            self::setupLocale($setCookie);
        }

        return true;
    }


    /**
     * Changes application localization settings
     * @param bool $setCookie
     */
    public static function setupLocale($setCookie = true)
    {
        $locale = App::$user->getLocale();

        $locales = [
            LC_CTYPE => 'en_US.utf8',
            LC_COLLATE => 'en_US.utf8',
            LC_MESSAGES => 'en_US.utf8',
            LC_MONETARY => ["$locale.utf8", $locale],
            LC_NUMERIC => ["$locale.utf8", $locale],
            LC_TIME => ["$locale.utf8", $locale],
        ];

        array_walk($locales, function ($locale, int $category) {
            setlocale($category, $locale);
        });

        self::$locale = $locale;

        if (!App::isConsole() && $setCookie) {
            setcookie('lang', self::$lang, time() + 31536000, '/');
        }
    }

    /**
     * @deprecated use LangVariantProvider::getLangVariant() instead
     */
    public static function getLangVariant(): ?LangVariant
    {
        $langVariantProvider = App::getService(LangVariant\LangVariantProvider::class);

        return $langVariantProvider->getLangVariant();
    }

    /**
     * Returns content related to translation acronym of current language
     *
     * @param string $translationAcronym - translation acronym
     * @param bool $noOverloaded - don't change acronym to overloaded
     * @return string|null
     */
    public static function get(string $translationAcronym, bool $noOverloaded = false): ?string
    {
        static $langNo = 0;

        static $overloadedLangs;

        if (!isset($overloadedLangs)) {
            $overloadedLangs = Arrays::toArray(App::$config->langs);
        }

        if (!$noOverloaded && array_key_exists($translationAcronym, $overloadedLangs)) {
            $translationAcronym = $overloadedLangs[$translationAcronym];
        }

        $langVariant = self::getLangVariant();

        if (!$noOverloaded && $translationAcronym && $langVariant) {
            $translationAcronym = $langVariant->getAcronymForVariant(
                $translationAcronym
            );
        }

        if (session_id() && App::$session && App::$session->showLangKeys) {
            return $translationAcronym;
        }

        $langPrefix = explode('_', $translationAcronym, 2);

        $translationAcronym = $langPrefix[1];
        $langPrefix = strtoupper($langPrefix[0]);

        if (!key_exists($langPrefix, self::$langDefinition)) {
            try {
                self::_load($langPrefix);
            } catch (\Throwable) {
                return $translationAcronym;
            }
        }

        $langValue = self::$langDefinition[$langPrefix][self::$lang][$translationAcronym] ?? '';

        if (!isset(self::$langDefinition[$langPrefix][self::$lang][$translationAcronym])) {
            self::$langsContainer[$translationAcronym] = 'NULL';
            if (!in_array($langPrefix . '_' . $translationAcronym, self::$_missingTranslations)) {
                self::$_missingTranslations[] = $langPrefix . '_' . $translationAcronym;
            }

            switch (self::$onEmpty) {
                case self::BHVR_RETURN_EN:
                    if (isset(self::$langDefinition[$langPrefix]['en'][$translationAcronym])) {
                        return self::$langDefinition[$langPrefix]['en'][$translationAcronym];
                    }
                    // no break
                case self::BHVR_RETURN_KEY:
                    return $langPrefix . '_' . $translationAcronym;

                case self::BHVR_RETURN_NULL:
                    return null;

                case self::BHVR_RETURN_EDIT_LINK:
                    $langNo++;
                    $html = '<span class="_langKey ' . $langPrefix . "_" . $translationAcronym . '"'
                            . ' data-lang-group="' . $langPrefix . '"'
                            . ' data-lang-key="' . $translationAcronym . '"';
                    foreach (self::getLanguages() as $langId => $lang) {
                        $html .= ' data-translation-' . $langId . '="' . addslashes(self::$langDefinition[$langPrefix][$langId][$translationAcronym]) . '"';
                    }
                    $html .= ' id="_langKey' . $langNo . '">' . $langPrefix . "_" . $translationAcronym . '</span>';
                    return $html;
            }
        } else {
            self::$langsContainer[$translationAcronym] = substr($langValue, 0, 50) . '...';
        }

        // replacing {newline} into \n
        // and single apostrophe due to JS errors mainly with French langs
        return str_replace(['{newline}', "'"], ["\n", '’'], $langValue);
    }


    /**
     * Returns missing translations for current view
     * @return array
     */
    public static function getMissingTranslations()
    {
        return self::$_missingTranslations;
    }


    /**
     * Returns name column name with language e.g. name_pl
     * @param string $column - column name (default: 'name')
     * @return string
     */
    public static function getNameColumn(string $column = 'name'): string
    {
        return $column . '_' . strtolower(self::getLanguage());
    }

    /**
     * Returns name of columns "name" for all enabled languages
     * @param string $column
     * @return array
     */
    public static function getTranslationNameColumns(string $column = 'name'): array
    {
        foreach (self::getLanguages() as $langId => $lang) {
            $columns[] = $column . '_' . $langId;
        }
        return $columns;
    }

    /**
     * @return mixed|Cache\CacheInterface
     */
    protected static function getCache()
    {
        $cache = App::$cache;

        if (App::$di->has('lang_cache')) {
            $cache = App::$di->getShared('lang_cache');
        }
        return $cache;
    }


    /**
     * Checks if translation with given acronym exists
     *
     * @param string $translationAcronym - translation acronym
     * @return bool
     */
    public static function exists($translationAcronym)
    {
        $langPrefix = explode('_', $translationAcronym, 2);

        $translationAcronym = $langPrefix[1];
        $langPrefix         = strtoupper($langPrefix[0]);

        if (!key_exists($langPrefix, self::$langDefinition)) {
            self::_load($langPrefix);
        }

        return isset(self::$langDefinition[$langPrefix][$translationAcronym]);
    }


    /**
     * Loads all translations
     * @return void
     * @throws Exception
     */
    public static function loadAll(): void
    {
        $langs = array_keys(self::getLanguages());
        $cache = self::getCache();
        $query = 'SELECT * FROM app.lang_matrix WHERE lang IN (\'' . implode("','", $langs) . '\') ORDER BY lang, "group"';
        $translations = [];

        foreach (self::$_db->getAll($query) as $row) {
            if ($cacheKey && $cacheKey != 'lang_' . $row['lang'] . '_' . strtolower($row['group'])) {
                $cache[$cacheKey] = $translations;
                $translations = [];
            }
            $translations[$row['lang']][$row['key']] = $row['value'];
            $cacheKey = 'lang_' . $row['lang'] . '_' . strtolower($row['group']);
        }
    }


    /**
     * Loads specified package with translation definitions
     *
     * @param string $pack - name of translation package
     */
    private static function _load($pack, $lang = null)
    {
        $currentLang = $lang ?: self::$lang;
        $cacheKey = 'lang_' . $currentLang . '_' . strtolower($pack);
        $cache = self::getCache();

        $translations = $cache[$cacheKey];

        if (null === $translations) {
            $translations = [];

            $query = 'SELECT * FROM app.lang_matrix WHERE "group" = :group';
            $params = [
                'group' => $pack,
            ];

            if (self::$onEmpty == self::BHVR_RETURN_EN) {
                $query .= " AND lang IN (:lang, 'en')";
                $params['lang'] = $currentLang;
            } elseif (self::$onEmpty != self::BHVR_RETURN_EDIT_LINK) {
                $query .= " AND lang=:lang";
                $params['lang'] = $currentLang;
            }

            foreach (self::$_db->getAll($query, $params) as $row) {
                $translations[$row['lang']][$row['key']] = $row['value'];
            }
            $cache[$cacheKey] = $translations;
        }
        self::$langDefinition[strtoupper($pack)] = $translations;
    }


    /**
     * Unsets langs cache
     */
    public static function refresh()
    {
        $groups = self::getGroups();
        $cache = self::getCache();

        foreach (self::getLanguages() as $langId => $lang) {
            foreach ($groups as $group) {
                $cacheKey = 'lang_' . $langId . '_' . strtolower($group);
                unset($cache[$cacheKey]);
            }
        }
    }


    /**
     * Returns list of defined groups
     * @return array
     * @throws Exception
     */
    public static function getGroups()
    {
        return Arrays::getColumn(
            self::$_db->getAll(
                "SELECT DISTINCT lang_group_id FROM app.lang_group_tab g ORDER BY g.lang_group_id ASC"
            ),
            'lang_group_id'
        );
    }


    /**
     * Search for translations
     *
     * @param string      $query
     * @param string      $target
     * @param bool        $showEmpty
     * @param string|bool $dateFrom
     * @param string|bool $dateTo
     *
     * @return array|false
     *
     * @throws Exception
     * @throws ReflectionException
     */
    public static function search($query, $target = 'key', $showEmpty = false, $dateFrom = false, $dateTo = false)
    {
        if (is_array($query)) {
            $lang = $query['searchLang'];
            $groupId = $query['group'];
            $page = $query['page'];
            $limit = $query['limit'];
            $query = $query['query'];
        }


        if (($page != null && $limit != null) || $page == -1) {
            $offset     = DataObject::offset($page, $limit);
        }

        $sql = "SELECT DISTINCT (lk.lang_group_id || '_' || lk.lang_key_id) as lang_acro, lk.lang_key_id FROM app.lang_key_tab lk LEFT JOIN app.lang_value_tab lv USING(lang_group_id, lang_key_id) WHERE 1=1";

        if ($groupId) {
            $sql .= " AND lv.lang_group_id ILIKE :groupId";
            $params['groupId'] = '%' . $groupId . '%';
        }

        if ($query) {
            if ($target == 'key') {
                $sql .= " AND (lk.lang_group_id || '_' || lk.lang_key_id) ILIKE :key";
            } else {
                $sql .= " AND lv.value ILIKE :key";
            }

            if ($target == 'exact') {
                $params['key'] = $query;
            } else {
                $params['key'] = '%' . $query . '%';
            }
        }

        if ($showEmpty && $lang) {
            $sql .= " AND NOT EXISTS(SELECT 1 FROM app.lang_value_tab lv2 WHERE lv.lang_key_id=lv2.lang_key_id AND lv.lang_group_id=lv2.lang_group_id AND lv2.lang_id = :lang)";
            $params['lang'] = $lang;
        } elseif ($showEmpty) {
            $sql .= " AND (SELECT COUNT(*) FROM app.lang_value_tab lv2 WHERE lv.lang_key_id=lv2.lang_key_id AND lv.lang_group_id=lv2.lang_group_id) < " . count(self::getLanguages());
        } elseif ($lang) {
            $sql .= " AND lv.lang_id = :lang";
            $params['lang'] = $lang;
        }

        if ($dateFrom) {
            $sql .= " AND lv.update_date >= :date_from ";
            $params['date_from'] = $dateFrom . " 00:00:00";
        }

        if ($dateTo) {
            $sql .= " AND lv.update_date <= :date_to ";
            $params['date_to'] = $dateTo . " 23:59:59";
        }

        $dataQuery = $sql . " ORDER BY lang_acro";
        $countQuery = "SELECT COUNT(*) FROM (" . $sql . ") as count_query";

        if ($page != null) {
            $dataQuery .= " LIMIT " . Filter::filterInt($limit)  . " OFFSET " . Filter::filterInt($offset);
        } elseif ($limit != null) {
            $dataQuery .= " LIMIT " . Filter::filterInt($limit);
        }

        $result = self::$_db->getAll($dataQuery, $params, 'lang_acro');

        if (($page != null && $limit != null) || $page == -1) {
            if ($page == 1 && count($result) < $limit) {
                self::$listItemsFound = count($result);
            } else {
                self::$listItemsFound = static::$_db->getOne($countQuery, $params);
            }
        }

        if (!$result) {
            return [];
        }

        $preparedData = [];
        $preparedData['keys'] = Arrays::getColumn($result, 'lang_key_id');

        $params = new ParameterBag([
            'lang_acro' => array_keys($result),
            'update_date_from' => $dateFrom,
            'update_date_to' => $dateTo,
        ]);

        $result = Value::listAll($params);

        foreach ($result as $row) {
            $preparedData['values'][$row['lang_group_id']][$row['lang_key_id']][$row['lang_id']] = $row;
        }

        return $preparedData;
    }


    /**
     * Returns lang list
     *
     * @param int $page
     * @param array|ArrayObject $params
     * @param int $limit
     *
     * @return Lang[]
     *
     * @throws Exception
     * @throws ReflectionException
     */
    public static function getList($page = 1, $params = null, $limit = self::ITEMS_PER_PAGE)
    {
        $arr = [
            'query' => $params['query'],
            'searchLang' => $params['searchLang'],
            'group' => $params['group'],
            'export' => $params['xls'],
            'page' => $page,
            'limit' => $limit
        ];

        return self::search($arr, $params['search'], (bool) $params['empty'], $params['date_from'], $params['date_to'])['values'];
    }


    /**
     * Returns current language
     * @return string
     */
    public static function getLanguage()
    {
        return self::$lang;
    }


    /**
     * Is the current language a right-to-left language?
     */
    public static function isRtl()
    {
        return self::$isRtl;
    }


    /**
     * Returns default language
     * @description Returns default language for client or demo instance. The same logic is used in the vintage frontend plugins
     * - module/Application/view/plugin/function.name_fields.php
     * - module/Application/view/plugin/function.name_fields_some.php
     * @return string
     */
    public static function getDefaultLanguage(): string
    {
        if (App::demoMode()) {
            return Lang::getLanguage();
        }

        return App::$config->settings->defaultLanguage ?: 'pl';
    }

    /**
     * Get history translation
     * @param string $translation
     * @return string
     * @throws InvalidArgumentException
     */
    public static function getHistoryTranslation($translation)
    {
        //whitespace characters declaration
        $sep1 = chr(30);
        $sep2 = chr(31);

        //patterns
        $langPattern = '/\{[A-Z\_0-9]+\}/';
        $langVersionPattern = '/\[\{[^\]]+\}\]/';

        preg_match_all($langPattern, $translation, $matches);

        foreach ($matches[0] as $match) {
            //for status only
            if (strpos($match, 'TICKET_STATUS')) {
                $status = str_replace(['{', '}'], '', $match);
                $statusSplit = explode('_', $status);
                $status = ucfirst(strtolower(end($statusSplit)));
                $status = Status::get($status);

                if ($status) {
                    $translation = str_replace($match, $status, $translation);
                    continue;
                }
            }

            $translation = str_replace($match, self::get(substr($match, 1, -1)), $translation);
        }

        preg_match_all($langVersionPattern, $translation, $matches);

        foreach ($matches[0] as $match) {
            $languageVersion = explode($sep2, $match);

            $wordsArray = [];
            foreach ($languageVersion as $language) {
                $langArray = explode($sep1, $language);
                $charactersClear = ['{', '}', '[', ']'];
                $wordsArray[str_replace($charactersClear, '', $langArray[0])] = str_replace($charactersClear, '', $langArray[1]);
            }
            if (array_key_exists(self::getLanguage(), $wordsArray)) {
                $translation = str_replace($match, $wordsArray[self::getLanguage()], $translation);
            } else {
                $translation = str_replace($match, '', $translation);
            }
        }

        return preg_replace_callback(self::DATETIME_PATTERN, function ($match) {
            $includeTime = isset($match[0]) && strlen($match[0]) > 10;

            return Output::formatDate($match[0], $includeTime);
        }, $translation);
    }


    /**
     * Get post translation
     * @param type $translation
     * @return string
     */
    public static function getPostTranslation($translation)
    {
        $pattern = '/\[[a-zA-Z]+\]/';
        $langPattern = '/\{[A-Z\_0-9]+\}/';

        preg_match_all($pattern, $translation, $matches);

        foreach ($matches[0] as $match) {
            $replacement = Log::getActionNameById(substr($match, 1, -1));
            if (empty($replacement)) {
                continue;
            }
            $translation = str_replace($match, $replacement, $translation);
        }

        preg_match_all($langPattern, $translation, $matches);

        foreach ($matches[0] as $match) {
            $translation = str_replace($match, self::get(substr($match, 1, -1)), $translation);
        }

        return $translation;
    }


    /**
     * Return text translation
     * @param string $translation
     * @return string
     */
    public static function getTranslation($translation)
    {
        $langPattern = '/\{[A-Z\_0-9]+\}/';

        $result = preg_replace_callback(
            $langPattern,
            function ($match) {
                $match = trim($match[0], '{}');
                return Lang::get($match);
            },
            $translation
        );

        return $result;
    }


    /**
     * Get translation
     *
     * @param string $key
     * @return string|null
     */
    public function offsetGet($key): ?string
    {
        return self::get($key);
    }


    /**
     * Forbidden
     *
     * @param string $key
     * @param string $value
     * @throws Exception
     */
    public function offsetSet($key, $value): void
    {
        Exception::raise('Cannot change dictionary values');
    }


    /**
     * Checks if translation exists in dictionary
     *
     * @param string $key
     * @return bool
     */
    public function offsetExists($key): bool
    {
        return self::exists($key);
    }


    /**
     * Forbidden
     * @param string $key
     * @throws Exception
     */
    public function offsetUnset($key): void
    {
        Exception::raise('Cannot unset dictionary values');
    }


    /**
     * Returns language from subdomain, if language exists in application
     * @return string language
     */
    public static function getByDomain()
    {
        $lang = explode('.', $_SERVER['SERVER_NAME']);
        $lang = strtolower($lang[0]);
        if (array_key_exists($lang, self::getLanguages())) {
            return $lang;
        } else {
            return false;
        }
    }


    /**
     * Returns language from request Accept-Language header
     * @return string language
     */
    public static function getByBrowser()
    {
        $lang = substr(locale_accept_from_http($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''), 0, 2);
        if ($lang && array_key_exists($lang, self::getLanguages())) {
            return $lang;
        } else {
            return false;
        }
    }


    /**
     * ON/OFF languages
     * @param string $langId
     * @param int(0|1) $active
     * @throws \Exception
     */
    public static function onOffLang($langId, $active)
    {
        $sql    = 'UPDATE app.lang_tab SET active = :active WHERE lang_id = :langId';
        $params = ['active' => $active, 'langId' => $langId];

        try {
            self::$_db->execDML($sql, $params);
        } catch (\Exception $e) {
            throw $e;
        }

        unset(App::$cache['languages']);
        unset(App::$cache['rtlLanguages']);
        self::$_languages = null;
    }


    /**
     * Get languages config
     * @throws \Exception
     * @return array
     */
    public static function getLanguageSettings()
    {
        $sql = 'SELECT lang_id, name, active FROM app.lang_tab ORDER BY lang_id ASC';
        $rv  = [];

        try {
            foreach (self::$_db->getAll($sql) as $lang) {
                $rv[$lang['lang_id']] = [
                    'name'   => $lang['name'],
                    'active' => $lang['active']
                ];
            }
        } catch (\Exception $e) {
            throw $e;
        }

        return $rv;
    }


    /**
     * Get custom translations for layout
     * @return array
     */
    public static function getOverloadedLangs()
    {
        if (!$langs = App::$config->langs) {
            return null;
        }

        $translation = [];

        foreach ($langs as $key => $lang) {
            $translation[$key] = self::get($lang);
        }
        return $translation;
    }

    /**
     * Returns overloaded translations, including those overloaded by the lang variant
     * @return array<string, string>
     * @deprecated use LangVariant\OverloadedLangProvider::getOverloadedLangs() instead
     */
    public static function getOverloadedLangsMap(): array
    {
        $provider = App::getService(LangVariant\OverloadedLangProvider::class);

        return $provider->getOverloadedLangs();
    }

    /**
     * Returns locale string to use with dojo
     * @return string
     */
    public static function getDojoLocale()
    {
        if (strpos(App::$user->getLocale(), 'ar_') !== false && Lang::getLanguage() !== 'ar') {
            $locale = 'en_GB';
        } else {
            $locale = App::$user->getLocale();
        }

        return strtolower(str_replace('_', '-', $locale));
    }


    /**
     * Returns translated lang
     * @param string $acro
     * @param string $lang
     * @return string
     */
    public static function translate($acro, $lang)
    {
        if (is_null($lang)) {
            return self::get($acro);
        }

        $currentLang = self::getLanguage();
        self::switchLanguage($lang);
        $translatedLang = self::get($acro);
        self::switchLanguage($currentLang);

        return $translatedLang;
    }


    /**
     * Converts locale string into language code
     * @param string $lang
     * @return string
     */
    public static function getLangFromLocale($lang)
    {
        if (strpos($lang, "-")) {
            [$language, $country] = explode("-", $lang);

            if ($language == "es") {
                return strtolower($country);
            }

            return $language;
        }

        return $lang;
    }


    /**
     * @param string $lang
     * @return string
     */
    public static function getDefaultLocaleForLang($lang)
    {
        $language = Language::get($lang);

        return $language['default_locale'];
    }

    /**
     * @param string $acro
     * @return bool
     */
    public static function isRequired($acro): bool
    {
        return in_array($acro, self::$requiredLangs);
    }


    /**
     * @return LangVariant[]
     */
    public static function getLangVariants(): array
    {
        $variants = [];
        $list = self::$_db->cacheGetAll(
            "SELECT value FROM app.setting_allowed_value_tab st WHERE setting_id = 'LangVariant'",
            null,
            'value'
        );

        foreach (array_keys($list) as $variant) {
            $variants[] = new LangVariant($variant);
        }

        return $variants;
    }

    /**
     * Clear not required langs of the $fields from the items in the $list
     *
     * @param array $list
     * @param array $fields List of translated fields
     * @param array $requiredLangs Required langs (acronyms)
     * @return void
     */
    public static function clearNotRequiredLangsFromList(array $list, array $fields, array $requiredLangs)
    {
        foreach ($list as $item) {
            foreach ($fields as $field) {
                $keys = array_keys(is_array($item) ? $item : $item->getArrayCopy());
                foreach ($keys as $value) {
                    $prefix = $field . '_';
                    if (
                        substr($value, 0, strlen($prefix)) === $prefix &&
                        !in_array(substr($value, strlen($prefix)), $requiredLangs)
                    ) {
                        unset($item[$value]);
                    }
                }
            }
        }
    }
}
