/*
 * SDK Pullenti Unitext, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved. 
 * Non-Commercial Freeware and Commercial Software. 
 * This class is generated using the converter UniSharping (www.unisharping.ru) from Pullenti C# project. 
 * The latest version of the code is available on the site www.pullenti.ru
 */

const fs = require('fs');
const Utils = require("./../unisharp/Utils");
const RefOutArgWrapper = require("./../unisharp/RefOutArgWrapper");
const StringBuilder = require("./../unisharp/StringBuilder");
const Stream = require("./../unisharp/Stream");
const FileStream = require("./../unisharp/FileStream");
const FileInfo = require("./../unisharp/FileInfo");

const FileFormat = require("./../unitext/FileFormat");
const FileFormatClass = require("./../unitext/FileFormatClass");
const MiscHelper = require("./MiscHelper");
const CreateDocumentParam = require("./../unitext/CreateDocumentParam");
const UnitextService = require("./../unitext/UnitextService");
const EncodingWrapper = require("./EncodingWrapper");
const FileFormatsHelper = require("./FileFormatsHelper");
const UnitextItem = require("./../unitext/UnitextItem");
const UnitextHelper = require("./../unitext/internal/uni/UnitextHelper");
const TextHelperComapreTextsResult = require("./TextHelperComapreTextsResult");

/**
 * Различные утилитки работы с текстами
 */
class TextHelper {
    
    /**
     * Преобразовать текст в список слов (разделителем является любой пробельный символ)
     * @param text 
     * @return 
     */
    static getWords(text) {
        if (text === null) 
            return null;
        let res = new Array();
        for (let i = 0; i < text.length; i++) {
            if (Utils.isWhitespace(text[i])) 
                continue;
            let j = 0;
            for (j = i; j < text.length; j++) {
                if (Utils.isWhitespace(text[j])) 
                    break;
            }
            res.push(text.substring(i, i + j - i));
            i = j;
        }
        return res;
    }
    
    /**
     * Получение текста из текстового файла, при этом автоматически определяется 
     * кодировка Windows-1251 или DOS и осуществляется соответствующее преобразование 
     * (английский текст не изменяется). 
     * Также корректируются переходы на новую строку, чтобы везде были бы \r\n .
     * @param fileName имя текстового файла
     * @return результат
     * Корректировка символов перехода на новую строку (одно 0D или 0A преобразуются в 0D0A), 
     * замена nbsp на обычные пробелы
     * @param txt 
     * @return 
     */
    static correctWhitespaces(txt) {
        if (txt === null) 
            return null;
        let i = 0;
        for (i = 0; i < txt.length; i++) {
            if ((txt.charCodeAt(i)) === 0xD) {
                if ((i + 1) >= txt.length) 
                    break;
                if ((txt.charCodeAt(i + 1)) !== 0xA) 
                    break;
            }
            else if ((txt.charCodeAt(i)) === 0xA) {
                if (i === 0) 
                    break;
                if ((txt.charCodeAt(i - 1)) !== 0xD) 
                    break;
            }
            else if ((txt.charCodeAt(i)) === 0xA0) 
                break;
        }
        if (i >= txt.length) 
            return txt;
        let res = new StringBuilder();
        for (i = 0; i < txt.length; i++) {
            if ((txt.charCodeAt(i)) === 0xD) {
                res.append("\r\n");
                if (((i + 1) < txt.length) && (txt.charCodeAt(i + 1)) === 0xA) 
                    i++;
            }
            else if ((txt.charCodeAt(i)) === 0xA) 
                res.append("\r\n");
            else if ((txt.charCodeAt(i)) === 0xA0) 
                res.append(' ');
            else 
                res.append(txt[i]);
        }
        let resTxt = res.toString();
        for (i = 0; i < (resTxt.length - 1); i++) {
            if ((resTxt.charCodeAt(i)) === 0xD && (resTxt.charCodeAt(i + 1)) === 0xD) {
            }
        }
        return resTxt;
    }
    
    /**
     * Исправиление ситуации, когда текстографический формат для размещения слитного фрагмента текста 
     * разбивает его на строки (например, для PDF). Производится анализ таких ситуаций и замена 
     * переносов на пробелы.
     * @param txt текст
     * @param count вернёт количество исправлений
     * @return исправленный или исходный текст
     */
    static correctNewlinesForParagraphs(txt, count) {
        count.value = 0;
        if (Utils.isNullOrEmpty(txt)) 
            return txt;
        let i = 0;
        let j = 0;
        let cou = 0;
        let totalLen = 0;
        for (i = 0; i < txt.length; i++) {
            let ch = txt[i];
            if ((ch.charCodeAt(0)) !== 0xD && (ch.charCodeAt(0)) !== 0xA && ch !== '\f') 
                continue;
            let len = 0;
            let lastChar = ch;
            for (j = i + 1; j < txt.length; j++) {
                ch = txt[j];
                if ((ch.charCodeAt(0)) === 0xD || (ch.charCodeAt(0)) === 0xA || ch === '\f') 
                    break;
                else if ((ch.charCodeAt(0)) === 0x9) 
                    len += 5;
                else {
                    lastChar = ch;
                    len++;
                }
            }
            if (j >= txt.length) 
                break;
            if (len < 30) 
                continue;
            if (len > 200) 
                return txt;
            if (lastChar !== '.' && lastChar !== ':' && lastChar !== ';') {
                let nextIsDig = false;
                for (let k = j + 1; k < txt.length; k++) {
                    if (!Utils.isWhitespace(txt[k])) {
                        if (Utils.isDigit(txt[k])) 
                            nextIsDig = true;
                        break;
                    }
                }
                if (!nextIsDig) {
                    cou++;
                    totalLen += len;
                }
            }
            i = j;
        }
        if (cou < 4) 
            return txt;
        totalLen = Utils.intDiv(totalLen, cou);
        if ((totalLen < 50) || totalLen > 100) 
            return txt;
        let tmp = new StringBuilder(txt);
        for (i = 0; i < tmp.length; i++) {
            let ch = tmp.charAt(i);
            let jj = 0;
            let len = 0;
            let lastChar = ch;
            for (j = i + 1; j < tmp.length; j++) {
                ch = tmp.charAt(j);
                if ((ch.charCodeAt(0)) === 0xD || (ch.charCodeAt(0)) === 0xA || ch === '\f') 
                    break;
                else if ((ch.charCodeAt(0)) === 0x9) 
                    len += 5;
                else {
                    lastChar = ch;
                    len++;
                }
            }
            if (j >= tmp.length) 
                break;
            for (jj = j - 1; jj >= 0; jj--) {
                if (!Utils.isWhitespace((lastChar = tmp.charAt(jj)))) 
                    break;
            }
            let notSingle = false;
            jj = j + 1;
            if ((jj < tmp.length) && (tmp.charAt(j).charCodeAt(0)) === 0xD && (tmp.charAt(jj).charCodeAt(0)) === 0xA) 
                jj++;
            for (; jj < tmp.length; jj++) {
                ch = tmp.charAt(jj);
                if (!Utils.isWhitespace(ch)) 
                    break;
                if ((ch.charCodeAt(0)) === 0xD || (ch.charCodeAt(0)) === 0xA || ch === '\f') {
                    notSingle = true;
                    break;
                }
            }
            if (((!notSingle && len > (totalLen - 20) && (len < (totalLen + 10))) && lastChar !== '.' && lastChar !== ':') && lastChar !== ';') {
                tmp.setCharAt(j, ' ');
                count.value++;
                if ((j + 1) < tmp.length) {
                    ch = tmp.charAt(j + 1);
                    if ((ch.charCodeAt(0)) === 0xA) {
                        tmp.setCharAt(j + 1, ' ');
                        j++;
                    }
                }
            }
            i = j - 1;
        }
        return tmp.toString();
    }
    
    /**
     * Считывание текста из текстового файла. 
     * Кодировка файла определяется префиксом. Поддерживаются такие префиксы: 
     * EF BB BF - для UTF8 
     * FF FE или FE FF - для Unicode, младний байт раньше 
     *  
     * Если префикс не задан, то подразумевается Windows-1251
     * @param fileName имя входного файла
     * @return строка или null при отсутствии
     */
    static readStringFromFile(fileName, utf8 = false, donotCorrectWhitespaces = false) {
        if (!fs.existsSync(fileName) && fs.statSync(fileName).isFile()) 
            return null;
        let data = UnitextHelper.loadDataFromFile(fileName, 0);
        if (data === null) 
            return null;
        return TextHelper.readStringFromBytes(data, utf8, donotCorrectWhitespaces);
    }
    
    static checkEncoding(fileName) {
        let fi = new FileInfo(fileName);
        if (!fi.exists() || fi.length === (0)) 
            return null;
        let buf = new Uint8Array((fi.length > (5000) ? 5000 : fi.length));
        let f = new FileStream(fileName, "r", false); 
        try {
            f.read(buf, 0, buf.length);
        }
        finally {
            f.close();
        }
        let i = 0;
        let wrapi671 = new RefOutArgWrapper();
        let inoutres672 = EncodingWrapper.checkEncoding(buf, wrapi671);
        i = wrapi671.value;
        return inoutres672;
    }
    
    static readStringFromBytes(data, utf8 = false, doNotCorrectWhitespaces = false) {
        if (utf8) 
            return MiscHelper.decodeStringUtf8(data, 0, -1);
        let hlen = 0;
        let wraphlen673 = new RefOutArgWrapper();
        let enc = EncodingWrapper.checkEncoding(data, wraphlen673);
        hlen = wraphlen673.value;
        if (enc === null) 
            return null;
        let txt = enc.getString(data, 0, -1);
        if (!doNotCorrectWhitespaces) 
            return txt;
        return TextHelper.correctWhitespaces(txt);
    }
    
    /**
     * Сохранение текста в файл. Формат UTF-8, вставляется префикс EF BB BF.
     * @param str 
     * @param fileName 
     */
    static writeStringToFile(str, fileName) {
        if (str === null) 
            str = "";
        let data = MiscHelper.encodeStringUtf8(str, true);
        let f = new FileStream(fileName, "w+", false); 
        try {
            f.write(data, 0, data.length);
        }
        finally {
            f.close();
        }
    }
    
    /**
     * Сохранить строку в файле в кодировке Windows-1251
     * @param str 
     * @param fileName 
     */
    static writeString1251ToFile(str, fileName) {
        if (str === null || fileName === null) 
            return;
        let f = new FileStream(fileName, "w+", false); 
        try {
            let data = MiscHelper.encodeString1251(str);
            f.write(data, 0, data.length);
        }
        finally {
            f.close();
        }
    }
    
    /**
     * Проверка совпадения строк, пробелы игнорируются
     * @param str1 
     * @param str2 
     * @param ignoreCase игнорировать регистр букв
     * @param ignoreSpecChars игнорировать небуквы и нецифры
     * @return 
     */
    static compareTexts(str1, str2, ignoreCase = false, ignoreSpecChars = false) {
        if (str1 === null || str2 === null) 
            return (str1 === str2 ? TextHelperComapreTextsResult.EQUALS : TextHelperComapreTextsResult.NOTEQUAL);
        let i1 = 0;
        let i2 = 0;
        while (true) {
            for (; i1 < str1.length; i1++) {
                if (!Utils.isWhitespace(str1[i1])) {
                    if (ignoreSpecChars) {
                        if (!Utils.isLetterOrDigit(str1[i1])) 
                            continue;
                    }
                    break;
                }
            }
            for (; i2 < str2.length; i2++) {
                if (!Utils.isWhitespace(str2[i2])) {
                    if (ignoreSpecChars) {
                        if (!Utils.isLetterOrDigit(str2[i2])) 
                            continue;
                    }
                    break;
                }
            }
            if (i1 >= str1.length) 
                break;
            if (i2 >= str2.length) 
                break;
            let ch1 = str1[i1];
            let ch2 = str2[i2];
            if (ch1 !== ch2 && ignoreCase) {
                ch1 = ch1.toUpperCase();
                ch2 = ch2.toUpperCase();
            }
            if (ch1 === 'ё') 
                ch1 = 'е';
            if (ch2 === 'ё') 
                ch2 = 'е';
            if (ch1 === 'Ё') 
                ch1 = 'Е';
            if (ch2 === 'Ё') 
                ch2 = 'Е';
            if (ch1 !== ch2) 
                return TextHelperComapreTextsResult.NOTEQUAL;
            i1++;
            i2++;
        }
        TextHelper.M_COMPARE_TEXTS_END_CHAR1 = i1;
        TextHelper.M_COMPARE_TEXTS_END_CHAR2 = i2;
        if (i1 >= str1.length && i2 >= str2.length) 
            return TextHelperComapreTextsResult.EQUALS;
        if (i1 >= str1.length) 
            return TextHelperComapreTextsResult.SECONDSTARTSWITHFIRST;
        if (i2 >= str2.length) 
            return TextHelperComapreTextsResult.FIRSTSTARTSWITHSECOND;
        return TextHelperComapreTextsResult.NOTEQUAL;
    }
    
    /**
     * Выделить текст из всех форматов, какие только поддерживаются 
     * (кроме архивов)
     * @param fileName имя файла (может быть null)
     * @param content содержимое файла (если null, то fileName есть имя файла в локальной файловой системе)
     * @param unzipArchives при true будет распаковывать архивы и извлекать тексты из файлов, 
     * суммарный текст получается конкатенацией
     * @return плоский текст или null
     */
    static extractText(fileName, content = null, unzipArchives = true) {
        let frm = FileFormatsHelper.analizeFileFormat(fileName, content);
        if (frm === FileFormat.UNKNOWN) 
            return null;
        let frmcl = FileFormatsHelper.getFormatClass(frm);
        if (frmcl === FileFormatClass.ARCHIVE && !unzipArchives) 
            return null;
        if (frmcl === FileFormatClass.IMAGE) 
            return null;
        let doc = UnitextService.createDocument(fileName, content, CreateDocumentParam._new674(true, !unzipArchives));
        if (doc === null) 
            return null;
        return UnitextHelper.getPlaintext(doc, null);
    }
    
    static static_constructor() {
        TextHelper.M_COMPARE_TEXTS_END_CHAR1 = 0;
        TextHelper.M_COMPARE_TEXTS_END_CHAR2 = 0;
    }
}


TextHelper.static_constructor();

module.exports = TextHelper