/*
 * 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
 */

package com.pullenti.util;

/**
 * Реализация кодировщика строк, замена системного Encoding. 
 * Введена из-за того, что в .NET Core не поддержаны разные 1251 и пр. 
 * Да и для кросспрограммности и кроссплатформенности это необходимо.
 * Кодировщик строк
 */
public class EncodingWrapper {

    /**
     * Создать обёртку
     * @param typ Стандартная кодировка, для которой есть собственная реализация
     * @param str Строковое название, например, windows-1251, utf-8 и пр.
     * @param cp Кодовая страница Windows, например, 1251
     */
    public EncodingWrapper(EncodingStandard typ, String str, int cp) {
        codePage = cp;
        stdTyp = typ;
        if (cp == 1251) 
            stdTyp = EncodingStandard.CP1251;
        else if (cp == 1252) 
            stdTyp = EncodingStandard.CP1252;
        if (com.pullenti.unisharp.Utils.isNullOrEmpty(str)) 
            return;
        String ss = str.replace("-", "").toUpperCase();
        if (ss.endsWith("ASCII")) {
            stdTyp = EncodingStandard.ACSII;
            return;
        }
        if (ss.startsWith("WINDOWS")) {
            int cod;
            com.pullenti.unisharp.Outargwrapper<Integer> wrapcod651 = new com.pullenti.unisharp.Outargwrapper<Integer>();
            boolean inoutres652 = com.pullenti.unisharp.Utils.parseInteger(ss.substring(7), 0, null, wrapcod651);
            cod = (wrapcod651.value != null ? wrapcod651.value : 0);
            if (inoutres652) {
                codePage = cod;
                if (cod == 1251) 
                    stdTyp = EncodingStandard.CP1251;
                else if (cod == 1252) 
                    stdTyp = EncodingStandard.CP1252;
                return;
            }
        }
        if (ss.startsWith("CP")) {
            int cod;
            com.pullenti.unisharp.Outargwrapper<Integer> wrapcod653 = new com.pullenti.unisharp.Outargwrapper<Integer>();
            boolean inoutres654 = com.pullenti.unisharp.Utils.parseInteger(ss.substring(2), 0, null, wrapcod653);
            cod = (wrapcod653.value != null ? wrapcod653.value : 0);
            if (inoutres654) {
                codePage = cod;
                if (cod == 1251) 
                    stdTyp = EncodingStandard.CP1251;
                else if (cod == 1252) 
                    stdTyp = EncodingStandard.CP1252;
                return;
            }
        }
        if (com.pullenti.unisharp.Utils.stringsEq(ss, "UTF8")) {
            stdTyp = EncodingStandard.UTF8;
            return;
        }
        if (com.pullenti.unisharp.Utils.stringsEq(ss, "UNICODE") || com.pullenti.unisharp.Utils.stringsEq(ss, "UTF16") || com.pullenti.unisharp.Utils.stringsEq(ss, "UTF16LE")) {
            stdTyp = EncodingStandard.UTF16LE;
            return;
        }
        if (com.pullenti.unisharp.Utils.stringsEq(ss, "UTF16BE")) {
            stdTyp = EncodingStandard.UTF16BE;
            return;
        }
    }

    /**
     * Стандартный тип
     */
    public EncodingStandard stdTyp = EncodingStandard.UNDEFINED;

    /**
     * Кодовая страница Windows
     */
    public int codePage;

    @Override
    public String toString() {
        if (stdTyp != EncodingStandard.UNDEFINED) 
            return stdTyp.toString();
        if (codePage > 0) 
            return ("CodePage=" + codePage);
        return "?";
    }

    /**
     * Закодировать строку
     * @param str кодируемая строка
     * @return массив файт
     */
    public byte[] getBytes(String str) {
        if (str == null) 
            return new byte[0];
        if (stdTyp == EncodingStandard.ACSII) 
            return MiscHelper.encodeStringAscii(str);
        if (stdTyp == EncodingStandard.UTF8) 
            return MiscHelper.encodeStringUtf8(str, false);
        if (stdTyp == EncodingStandard.CP1251) 
            return MiscHelper.encodeString1251(str);
        if (stdTyp == EncodingStandard.CP1252) 
            return MiscHelper.encodeString1252(str);
        if (stdTyp == EncodingStandard.UTF16LE) 
            return MiscHelper.encodeStringUnicode(str);
        if (stdTyp == EncodingStandard.UTF16BE) 
            return MiscHelper.encodeStringUnicodeBE(str);
        return MiscHelper.encodeString1252(str);
    }

    /**
     * Раскодировать строку
     * @param dat массив байт
     * @param len начальная позиция в массиве
     * @param pos длина
     * @return 
     */
    public String getString(byte[] dat, int pos, int len) {
        if (dat == null || (pos < 0)) 
            return null;
        if (dat.length == 0) 
            return "";
        if (len < 0) 
            len = dat.length - pos;
        if (stdTyp == EncodingStandard.ACSII) 
            return MiscHelper.decodeStringAscii(dat, pos, len);
        if (stdTyp == EncodingStandard.UTF8) 
            return MiscHelper.decodeStringUtf8(dat, pos, len);
        if (stdTyp == EncodingStandard.CP1251) 
            return MiscHelper.decodeString1251(dat, pos, len);
        if (stdTyp == EncodingStandard.CP1252) 
            return MiscHelper.decodeString1252(dat, pos, len);
        if (stdTyp == EncodingStandard.UTF16LE) 
            return MiscHelper.decodeStringUnicode(dat, pos, len);
        if (stdTyp == EncodingStandard.UTF16BE) 
            return MiscHelper.decodeStringUnicodeBE(dat, pos, len);
        return MiscHelper.decodeString1252(dat, pos, len);
    }

    /**
     * Определение кодировки по байтовому массиву
     * @param data кодированный массив
     * @param headLen размер префикса, если он есть
     * @return результирующая кодировка
     */
    public static EncodingWrapper checkEncoding(byte[] data, com.pullenti.unisharp.Outargwrapper<Integer> headLen) {
        headLen.value = 0;
        if ((data.length >= 3 && data[0] == ((byte)0xEF) && data[1] == ((byte)0xBB)) && data[2] == ((byte)0xBF)) {
            if (data.length == 3) 
                return null;
            headLen.value = 3;
            return new EncodingWrapper(EncodingStandard.UTF8, null, 0);
        }
        if (data.length >= 2 && data[0] == ((byte)0xFF) && data[1] == ((byte)0xFE)) {
            if (data.length == 2) 
                return null;
            headLen.value = 2;
            return new EncodingWrapper(EncodingStandard.UTF16LE, null, 0);
        }
        if (data.length >= 2 && data[0] == ((byte)0xFE) && data[1] == ((byte)0xFF)) {
            if (data.length == 2) 
                return null;
            headLen.value = 2;
            return new EncodingWrapper(EncodingStandard.UTF16BE, null, 0);
        }
        if (data.length > 10000) {
            byte[] buf = new byte[10000];
            for (int ii = 0; ii < buf.length; ii++) {
                buf[ii] = data[ii];
            }
            data = buf;
        }
        int i;
        int j;
        int dos = 0;
        int win = 0;
        int utf8 = 0;
        int d0 = 0;
        int rus = 0;
        for (i = 0; i < data.length; i++) {
            j = Byte.toUnsignedInt(data[i]);
            if (j == 0xE2 && ((i + 2) < data.length)) {
                if (data[i + 1] == ((byte)0x80) && data[i + 2] == ((byte)0x99)) {
                    utf8++;
                    i += 2;
                    continue;
                }
            }
            if (j >= 0xC0) 
                win++;
            if ((j >= 0x80 && j <= 0xAF)) 
                dos++;
            else if (j >= 0xE0 && j <= 0xEF) 
                dos++;
            if (j >= 0x80) {
                rus++;
                if (j == 0xD0 || j == 0xD1) 
                    d0++;
            }
        }
        if (dos > win && utf8 == 0) {
            byte[] data2 = new byte[data.length];
            for (i = 0; i < data.length; i++) {
                data2[i] = data[i];
                j = Byte.toUnsignedInt(data[i]);
                if (j >= 0xE0 && j <= 0xEF) 
                    j -= 0x30;
                if (j >= 0x80 && j <= 0xDF) 
                    data2[i] = (byte)((j + 0x40));
                if (j == 0xF1) 
                    data2[i] = (byte)0xB8;
                if (j == 0xF0) 
                    data2[i] = (byte)0xA8;
            }
            data = data2;
        }
        EncodingWrapper enc = null;
        String txt = null;
        if (d0 > ((rus / 5)) || utf8 > 0) {
            try {
                txt = MiscHelper.decodeStringUtf8(data, 0, -1);
                enc = new EncodingWrapper(EncodingStandard.UTF8, null, 0);
                if (utf8 > 0) 
                    return enc;
            } catch (Exception ex655) {
            }
        }
        if (txt == null || enc == null) 
            return new EncodingWrapper(EncodingStandard.CP1251, null, 0);
        try {
            String txt2 = MiscHelper.decodeString1251(data, 0, -1);
            int ru = 0;
            int ru2 = 0;
            for (char ch : txt.toCharArray()) {
                if (((int)ch) >= 0x400 && (((int)ch) < 0x500)) {
                    if (Character.isLetter(ch)) 
                        ru++;
                }
            }
            for (char ch : txt2.toCharArray()) {
                if (((int)ch) >= 0x400 && (((int)ch) < 0x500)) {
                    if (Character.isLetter(ch)) 
                        ru2++;
                }
            }
            if (ru2 > ((ru * 2))) 
                return new EncodingWrapper(EncodingStandard.CP1251, null, 0);
            return enc;
        } catch (Exception ex) {
        }
        return new EncodingWrapper(EncodingStandard.UTF8, null, 0);
    }

    /**
     * Считать строку из потока и раскодировать (использовать вместо StreamReader, 
     * чтобы и на Linux раскодировало винданутые кодировки)
     * @param s 
     * @return 
     */
    public String readLine(com.pullenti.unisharp.Stream s) throws java.io.IOException {
        java.util.ArrayList<Byte> arr = new java.util.ArrayList<Byte>();
        while (true) {
            int i = s.read();
            if (i < 0) 
                break;
            if (i == 0xD || i == 0xA) {
                if (arr.size() == 0) 
                    continue;
                break;
            }
            arr.add((byte)i);
        }
        if (arr.size() == 0) 
            return null;
        return this.getString(com.pullenti.unisharp.Utils.toBytesArray(arr.toArray(new Byte[arr.size()])), 0, -1);
    }
    public EncodingWrapper() {
    }
}
