/*
    Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
    и других спецификаций для функционирования компактных приложений на языке
    Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

    Copyright © 2016–2017, 2019–2022 Малик Разработчик

    Это свободная программа: вы можете перераспространять ее и/или изменять
    ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
*/

package java.lang;

import java.io.*;
import malik.emulator.i18n.*;

public final class String extends Object
{
    public static void checkBound(String method, int stringLength, int offset, int length) throws StringIndexOutOfBoundsException {
        int lim;
        if((lim = offset + length) > stringLength || lim < offset || offset > stringLength || offset < 0)
        {
            throw new StringIndexOutOfBoundsException(method != null ? method.concat(": индекс выходит из диапазона.") : "Индекс выходит из диапазона.", true);
        }
    }

    public static String valueOf(boolean value) {
        return value ? "true" : "false";
    }

    public static String valueOf(char value) {
        return new String(value);
    }

    public static String valueOf(float value) {
        return Float.toString(value);
    }

    public static String valueOf(double value) {
        return Double.toString(value);
    }

    public static String valueOf(int value) {
        return Integer.toString(value, 10);
    }

    public static String valueOf(long value) {
        return Long.toString(value, 10);
    }

    public static String valueOf(char[] src) {
        int len;
        if((len = src == null ? 0 : src.length) > 0)
        {
            MalikSystem.arraycopyf_short(src, 0, src = new char[len], 0, len);
        } else
        {
            src = null;
        }
        return new String(0, len, src);
    }

    public static String valueOf(char[] src, int offset, int length) {
        if(src == null)
        {
            throw new NullPointerException("String.valueOf: аргумент src равен нулевой ссылке.");
        }
        Array.checkBound("String.valueOf", src.length, offset, length);
        if(length > 0)
        {
            MalikSystem.arraycopyf_short(src, offset, src = new char[length], 0, length);
        } else
        {
            src = null;
        }
        return new String(0, length, src);
    }

    public static String valueOf(Object value) {
        return value == null ? "null" : value.toString();
    }

    private final int offset;
    private final int length;
    private final char[] chars;

    public String() {
        this.offset = 0;
        this.length = 0;
        this.chars = null;
    }

    public String(char[] src) {
        int len;
        if(src == null)
        {
            throw new NullPointerException("String: аргумент src равен нулевой ссылке.");
        }
        if((len = src.length) > 0)
        {
            MalikSystem.arraycopyf_short(src, 0, src = new char[len], 0, len);
        } else
        {
            src = null;
        }
        this.offset = 0;
        this.length = len;
        this.chars = src;
    }

    public String(char[] src, int offset, int length) {
        if(src == null)
        {
            throw new NullPointerException("String: аргумент src равен нулевой ссылке.");
        }
        Array.checkBound("String", src.length, offset, length);
        if(length > 0)
        {
            MalikSystem.arraycopyf_short(src, offset, src = new char[length], 0, length);
        } else
        {
            src = null;
        }
        this.offset = 0;
        this.length = length;
        this.chars = src;
    }

    public String(byte[] src) {
        int len;
        char[] chars = src != null ? Helper.byteToCharArray(src, 0, src.length) : null;
        if((len = chars == null ? 0 : chars.length) <= 0) chars = null;
        this.offset = 0;
        this.length = len;
        this.chars = chars;
    }

    public String(byte[] src, String encoding) throws UnsupportedEncodingException {
        int len;
        char[] chars = src != null ? Helper.byteToCharArray(src, 0, src.length, encoding) : null;
        if((len = chars == null ? 0 : chars.length) <= 0) chars = null;
        this.offset = 0;
        this.length = len;
        this.chars = chars;
    }

    public String(byte[] src, int offset, int length) {
        int len;
        long bounds;
        char[] chars;
        offset = (int) (bounds = Array.intersectBound(src == null ? 0 : src.length, offset, length));
        length = (int) (bounds >> 32);
        chars = src != null ? Helper.byteToCharArray(src, offset, length) : null;
        if((len = chars == null ? 0 : chars.length) <= 0) chars = null;
        this.offset = 0;
        this.length = len;
        this.chars = chars;
    }

    public String(byte[] src, int offset, int length, String encoding) throws UnsupportedEncodingException {
        int len;
        long bounds;
        char[] chars;
        offset = (int) (bounds = Array.intersectBound(src == null ? 0 : src.length, offset, length));
        length = (int) (bounds >> 32);
        chars = src != null ? Helper.byteToCharArray(src, offset, length, encoding) : null;
        if((len = chars == null ? 0 : chars.length) <= 0) chars = null;
        this.offset = 0;
        this.length = len;
        this.chars = chars;
    }

    public String(StringBuffer source) {
        if(source == null)
        {
            throw new NullPointerException("String: аргумент buffer равен нулевой ссылке.");
        }
        synchronized(source.monitor())
        {
            this.offset = 0;
            this.length = source.length();
            this.chars = source.content();
            source.setShared();
        }
    }

    public String(String source) {
        if(source == null)
        {
            this.offset = 0;
            this.length = 0;
            this.chars = null;
            return;
        }
        this.offset = source.offset;
        this.length = source.length;
        this.chars = source.chars;
    }

    private String(char source) {
        this.offset = 0;
        this.length = 1;
        this.chars = new char[] { source };
    }

    private String(int offset, int length, char[] chars) {
        this.offset = offset;
        this.length = length;
        this.chars = chars;
    }

    public boolean equals(Object anot) {
        int len;
        String s;
        return anot == this || anot instanceof String && (len = length) == (s = (String) anot).length && (offset == s.offset && chars == s.chars || regionMatches(false, 0, s, 0, len));
    }

    public int hashCode() {
        int result = 0;
        char[] c = chars;
        for(int ofs, e = 1, i = (ofs = offset) + length; i-- > ofs; e *= 31) result += e * c[i];
        return result;
    }

    public String toString() {
        return this;
    }

    public void getChars(int beginIndex, int endIndex, char[] dst, int offset) {
        int lim;
        int len;
        int length;
        if((beginIndex | endIndex) < 0 || beginIndex > (len = this.length) || endIndex > len || beginIndex > endIndex)
        {
            throw new StringIndexOutOfBoundsException("String.getChars: индекс выходит из диапазона.");
        }
        if(dst == null)
        {
            throw new NullPointerException("String.getChars: аргумент dst равен нулевой ссылке.");
        }
        if((lim = offset + (length = endIndex - beginIndex)) > (len = dst.length) || lim < offset || offset > len || offset < 0)
        {
            throw new ArrayIndexOutOfBoundsException("String.getChars: индекс выходит из диапазона.");
        }
        if(length > 0) MalikSystem.arraycopyf_short(this.chars, this.offset + beginIndex, dst, offset, length);
    }

    public boolean regionMatches(boolean ignoreCase, int thisOffset, String anot, int anotOffset, int length) {
        char c1;
        char c2;
        int lim;
        int len;
        char[] thisChars;
        char[] anotChars;
        if(
            anot == null ||
            (lim = thisOffset + length) > (len = this.length) || lim < thisOffset || thisOffset > len || thisOffset < 0 ||
            (lim = anotOffset + length) > (len = anot.length) || lim < anotOffset || anotOffset > len || anotOffset < 0
        ) return false;
        thisOffset += this.offset;
        anotOffset += anot.offset;
        thisChars = this.chars;
        anotChars = anot.chars;
        do
        {
            do
            {
                if(length-- <= 0) return true;
            } while((c1 = thisChars[thisOffset++]) == (c2 = anotChars[anotOffset++]));
            if(!ignoreCase) break;
        } while(Character.toUpperCase(c1) == Character.toUpperCase(c2) || Character.toLowerCase(c1) == Character.toLowerCase(c2));
        return false;
    }

    public boolean equalsIgnoreCase(String anot) {
        int length;
        return anot != null && (length = this.length) == anot.length && regionMatches(true, 0, anot, 0, length);
    }

    public boolean endsWith(String suffix) {
        if(suffix == null)
        {
            throw new NullPointerException("String.endsWith: аргумент suffix равен нулевой ссылке.");
        }
        return startsWith(suffix, this.length - suffix.length);
    }

    public boolean startsWith(String prefix) {
        if(prefix == null)
        {
            throw new NullPointerException("String.startsWith: аргумент prefix равен нулевой ссылке.");
        }
        return startsWith(prefix, 0);
    }

    public boolean startsWith(String prefix, int offset) {
        int i;
        char[] thisChars;
        char[] anotChars;
        if(prefix == null)
        {
            throw new NullPointerException("String.startsWith: аргумент prefix равен нулевой ссылке.");
        }
        if(offset < 0 || offset > this.length - (i = prefix.length)) return false;
        thisChars = this.chars;
        anotChars = prefix.chars;
        for(int thisOffset = this.offset + offset, anotOffset = prefix.offset; i-- > 0; ) if(thisChars[thisOffset++] != anotChars[anotOffset++]) return false;
        return true;
    }

    public char charAt(int index) {
        if(index < 0 || index >= length)
        {
            throw new StringIndexOutOfBoundsException("String.charAt: аргумент index выходит из диапазона.");
        }
        return chars[offset + index];
    }

    public int length() {
        return length;
    }

    public int compareTo(String anot) {
        int lengthDiff;
        int thisLength;
        int anotLength;
        int thisOffset;
        int anotOffset;
        char[] thisChars;
        char[] anotChars;
        if(anot == null)
        {
            throw new NullPointerException("String.compareTo: аргумент anot равен нулевой ссылке.");
        }
        lengthDiff = (thisLength = this.length) - (anotLength = anot.length);
        thisOffset = this.offset;
        anotOffset = anot.offset;
        thisChars = this.chars;
        anotChars = anot.chars;
        for(int i = 0, j = 0; i < thisLength && j < anotLength; i++, j++)
        {
            int c1 = thisChars[thisOffset++];
            int c2 = anotChars[anotOffset++];
            int characterDiff;
            if((characterDiff = c1 - c2) != 0) return characterDiff;
        }
        return lengthDiff;
    }

    public int indexOf(int character) {
        int ofs;
        int len;
        int result;
        return
            0 >= (len = length) || character < Character.MIN_VALUE || character > Character.MAX_VALUE ||
            (result = MalikSystem.arrayfindf_short(chars, (ofs = offset), character) - ofs) >= len ? -1 : result
        ;
    }

    public int indexOf(int character, int startFromIndex) {
        int ofs;
        int len;
        int result;
        if(startFromIndex < 0) startFromIndex = 0;
        return
            startFromIndex >= (len = length) || character < Character.MIN_VALUE || character > Character.MAX_VALUE ||
            (result = MalikSystem.arrayfindf_short(chars, (ofs = offset) + startFromIndex, character) - ofs) >= len ? -1 : result
        ;
    }

    public int indexOf(String string) {
        return indexOf(string, 0);
    }

    public int indexOf(String string, int startFromIndex) {
        char first;
        int len;
        int lim;
        int thisOffset;
        int anotOffset;
        char[] thisChars;
        char[] anotChars;
        if(string == null)
        {
            throw new NullPointerException("String.indexOf: аргумент string равен нулевой ссылке.");
        }
        if(startFromIndex < 0) startFromIndex = 0;
        if(startFromIndex > (lim = this.length - (len = string.length))) return -1;
        if(len <= 0) return startFromIndex;
        thisChars = this.chars;
        thisOffset = this.offset + startFromIndex;
        first = (anotChars = string.chars)[anotOffset = string.offset];
        do
        {
            label0: if(thisChars[thisOffset] == first)
            {
                for(int i = thisOffset, j = anotOffset, k = len; k-- > 1; ) if(thisChars[++i] != anotChars[++j]) break label0;
                return startFromIndex;
            }
            thisOffset++;
        } while(++startFromIndex <= lim);
        return -1;
    }

    public int lastIndexOf(int character) {
        int ofs;
        int result;
        int startFromIndex = length - 1;
        return
            startFromIndex < 0 || character < Character.MIN_VALUE || character > Character.MAX_VALUE ||
            (result = MalikSystem.arrayfindb_short(chars, (ofs = offset) + startFromIndex, character) - ofs) < 0 ? -1 : result
        ;
    }

    public int lastIndexOf(int character, int startFromIndex) {
        int ofs;
        int len;
        int result;
        if(startFromIndex >= (len = length)) startFromIndex = len - 1;
        return
            startFromIndex < 0 || character < Character.MIN_VALUE || character > Character.MAX_VALUE ||
            (result = MalikSystem.arrayfindb_short(chars, (ofs = offset) + startFromIndex, character) - ofs) < 0 ? -1 : result
        ;
    }

    public char[] toCharArray() {
        int len;
        char[] result = new char[len = length];
        if(len > 0) MalikSystem.arraycopyf_short(chars, offset, result, 0, len);
        return result;
    }

    public byte[] getBytes() {
        return Helper.charToByteArray(chars, offset, length);
    }

    public byte[] getBytes(String encoding) throws UnsupportedEncodingException {
        return Helper.charToByteArray(chars, offset, length, encoding);
    }

    public String trim() {
        int o1;
        int o2;
        int ofs1 = o1 = offset;
        int ofs2 = o2 = length + ofs1 - 1;
        char[] c = chars;
        for(; ofs2 >= ofs1 && c[ofs2] <= '\u0020'; ofs2--);
        for(; ofs1 <= ofs2 && c[ofs1] <= '\u0020'; ofs1++);
        return ofs1 > ofs2 ? "" : o1 == ofs1 && o2 == ofs2 ? this : new String(ofs1, ofs2 - ofs1 + 1, c);
    }

    public String intern() {
        String result;
        return (result = StringPool.intern(this)) != this ? result : (String) Memory.intern(this);
    }

    public String toLowerCase() {
        int i;
        int j;
        int ofs;
        int len;
        int lim = (ofs = offset) + (len = length);
        char[] c = chars;
        char[] r;
        for(i = ofs; i < lim; i++)
        {
            char ci;
            if((ci = c[i]) != Character.toLowerCase(ci)) break;
        }
        if(i >= lim) return this;
        for(MalikSystem.arraycopyf_short(c, ofs, r = new char[len], 0, j = i - ofs); j < len; i++, j++) r[j] = Character.toLowerCase(c[i]);
        return new String(0, len, r);
    }

    public String toUpperCase() {
        int i;
        int j;
        int ofs;
        int len;
        int lim = (ofs = offset) + (len = length);
        char[] c = chars;
        char[] r;
        for(i = ofs; i < lim; i++)
        {
            char ci;
            if((ci = c[i]) != Character.toUpperCase(ci)) break;
        }
        if(i >= lim) return this;
        for(MalikSystem.arraycopyf_short(c, ofs, r = new char[len], 0, j = i - ofs); j < len; i++, j++) r[j] = Character.toUpperCase(c[i]);
        return new String(0, len, r);
    }

    public String substring(int beginIndex) {
        int len;
        if(beginIndex < 0 || beginIndex > (len = length))
        {
            throw new StringIndexOutOfBoundsException("String.substring: аргумент beginIndex выходит из диапазона.");
        }
        return beginIndex == len ? "" : beginIndex == 0 ? this : new String(offset + beginIndex, len - beginIndex, chars);
    }

    public String substring(int beginIndex, int endIndex) {
        int len;
        if((beginIndex | endIndex) < 0 || beginIndex > (len = this.length) || endIndex > len || beginIndex > endIndex)
        {
            throw new StringIndexOutOfBoundsException("String.substring: индекс выходит из диапазона.");
        }
        return beginIndex == endIndex ? "" : endIndex - beginIndex == len ? this : new String(offset + beginIndex, endIndex - beginIndex, chars);
    }

    public String replace(char oldCharacter, char newCharacter) {
        int i;
        int j;
        int ofs;
        int len;
        int lim;
        char[] c;
        char[] r;
        if(oldCharacter == newCharacter || (len = length) <= 0) return this;
        lim = (ofs = offset) + len;
        c = chars;
        if((i = MalikSystem.arrayfindf_short(c, ofs, oldCharacter)) >= lim) return this;
        for(MalikSystem.arraycopyf_short(c, ofs, r = new char[len], 0, j = i - ofs); j < len; i++, j++)
        {
            char ci;
            r[j] = (ci = c[i]) != oldCharacter ? ci : newCharacter;
        }
        return new String(0, len, r);
    }

    public String concat(String anot) {
        int len;
        int len1;
        int len2;
        char[] r;
        if(anot == null)
        {
            throw new NullPointerException("String.concat: аргумент anot равен нулевой ссылке.");
        }
        if((len2 = anot.length) <= 0) return this;
        if((len1 = this.length) <= 0) return anot;
        r = new char[len = len1 + len2];
        MalikSystem.arraycopyf_short(this.chars, this.offset, r, 0, len1);
        MalikSystem.arraycopyf_short(anot.chars, anot.offset, r, len1, len2);
        return new String(0, len, r);
    }

    int getCharsAddress() {
        return length > 0 ? chars.getArrayAddress() + (offset << 1) : 0;
    }
}
