<?php

namespace goodboyalex\php_components_pack\extensions;

/**
 * Расширение строк.
 *
 * @author Александр Бабаев
 * @package php_components_pack
 * @version 1.0
 * @since 1.0
 */
final class StringExtension
{
    /**
     * @var array|string[] $RussianLetters Набор русских букв
     */
    private static array $RussianLetters =
        [
            'а', 'б', 'в', 'г', 'д', 'е', 'ё', 'ж', 'з', 'и', 'й',
            'к', 'л', 'м', 'н', 'о', 'п', 'р', 'с', 'т', 'у', 'ф',
            'х', 'ц', 'ч', 'ш', 'щ', 'ъ', 'ы', 'ь', 'э', 'ю', 'я',
            'А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'Й',
            'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф',
            'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я'
        ];

    /**
     * @var array|string[] $RussianLettersTransliteration Набор русских букв в транслитерации
     */
    private static array $RussianLettersTransliteration =
        [
            "a", "b", "v", "g", "d", "e", "yo", "zh", "z", "i", "j",
            "k", "l", "m", "n", "o", "p", "r", "s", "t", "u", "f",
            "h", "c", "ch", "sh", "sch", "j", "i", "j", "e", "yu", "ya",
            "A", "B", "V", "G", "D", "E", "Yo", "Zh", "Z", "I", "J",
            "K", "L", "M", "N", "O", "P", "R", "S", "T", "U", "F",
            "H", "C", "Ch", "Sh", "Sch", "J", "I", "J", "E", "Yu", "Ya"
        ];

    /**
     * Сравнивает две строки.
     *
     * @param string|null $str1 Первая строка.
     * @param string|null $str2 Вторая строка.
     * @param bool $ignoreCase Игнорировать регистр.
     *
     * @return int Результат сравнения. Возвращаемые значения:
     *
     *      -1 | значение $str1 меньше значения $str2.
     *       0 | значения $str1 и $str2 равны.
     *       1 | значение $str1 больше значения $str2.
     */
    public static function Compare (string|null $str1, string|null $str2, bool $ignoreCase = false): int
    {
        // Если оба пусты
        if (self::IsNullOrWhitespace($str1) && self::IsNullOrWhitespace($str2))
            // - то равны
            return 0;

        // Если первый из них не пуст, а второй пуст
        if (!self::IsNullOrWhitespace($str1) && self::IsNullOrWhitespace($str2))
            // - то первый больше
            return 1;

        // Если первый из них пуст, а второй не пуст
        if (!self::IsNullOrWhitespace($str1) && self::IsNullOrWhitespace($str2))
            // - то первый меньше
            return -1;

        // Если нужно игнорировать регистр
        if ($ignoreCase)
            // - то сравниваем по "человечески" без учёта регистра
            return strnatcasecmp($str1, $str2);

        // Иначе сравниваем по "человечески" с учётом регистра
        return strnatcmp($str1, $str2);
    }

    /**
     * Проверяет, пуста ли строка, содержит ли вместо текста только пробелы.
     *
     * @param string|null $str Проверяемая строка.
     *
     * @return bool Результат проверки.
     */
    public static function IsNullOrWhitespace (string|null $str): bool
    {
        return self::IsNullOrEmpty($str) || trim($str) === '';
    }

    /**
     * Проверяет, пуста ли строка.
     *
     * @param string|null $str Проверяемая строка.
     *
     * @return bool Результат проверки.
     */
    public static function IsNullOrEmpty (string|null $str): bool
    {
        return is_null($str) || $str === '';
    }

    /**
     * Конвертация в латиницу.
     *
     * @param string $source Исходное слово или выражение
     *
     * @return string Транслитерированное слово или выражение
     */
    public static function ConvertToLatin (string $source): string
    {
        // Создаю результат
        $result = "";

        // Длина исходного слова
        $length = mb_strlen($source);

        // Для каждой буквы или символа из слова
        for ($i = 0; $i < $length; $i++) {
            // - получаю букву или символ
            $letter = mb_substr($source, $i, 1);

            // - если это буква из русского алфавита
            if (self::IsRussianLetter($letter)) {
                // -- транслитерирую эту букву
                $resultTransliteration = self::TransliterationFromRussian($letter);

                // -- если транслитерация не удалась
                if ($resultTransliteration === false)
                    // --- вывожу оригинальную букву
                    $result = $result . $letter;
                else
                    // --- вывожу транслитерированную букву
                    $result = $result . $resultTransliteration;

                // -- и иду к следующей букве или символу
                continue;
            }

            // - если до сих пор буква или символ не обработаны, то возвращаю их
            $result = $result . $letter;
        }

        // Вывожу результат
        return $result;
    }

    /**
     * Проверяет, является ли буква русской.
     *
     * @param string $letter Буква
     *
     * @return bool Является ли буква русской
     */
    private static function IsRussianLetter (string $letter): bool
    {
        return in_array($letter, self::$RussianLetters);
    }

    /**
     * Получаю транслитерированную букву русского алфавита.
     *
     * @param string $letter Буква
     *
     * @return false|string Транслитерированная буква или false, если массивы не совпадают или буква не содержится
     * в массивах.
     */
    private static function TransliterationFromRussian (string $letter): false|string
    {
        // Если размерность массивов разная
        if (count(self::$RussianLetters) !== count(self::$RussianLettersTransliteration))
            // - то вывожу ошибку
            return false;

        // Получаю индекс буквы
        $ind = array_search($letter, self::$RussianLetters, true);

        // Если буква не найдена
        if ($ind === false)
            // - то вывожу ошибку
            return false;

        // Получаю транслитерированную букву
        return self::$RussianLettersTransliteration[$ind];
    }

    /**
     * Обрезает строку до указанных в параметре $maxLength символов
     *
     * @param string $text Исходный текст
     * @param int $maxLength Максимальная длина текста
     * @param string $endDots Суффикс, которым завершается обрезанная строка
     *
     * @return string Обрезанный текст
     */
    public static function GetShortText (string $text, int $maxLength, string $endDots = ''): string
    {
        // Если длина текста меньше максимальной
        if (mb_strlen($text) <= $maxLength)
            // - то возвращаю исходный текст
            return $text;

        // Если длина текста больше максимальной, то получаю длину текста без суффикса
        $lengthWithoutEndDots = $maxLength - mb_strlen($endDots);

        // Возвращаю обрезанный текст
        return mb_substr($text, 0, $lengthWithoutEndDots) . $endDots;
    }
}