/*
    Реализация спецификаций 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;

public final class StringBuffer extends Object
{
    private boolean shared;
    private int length;
    private char[] chars;
    private final Object monitor;

    public StringBuffer() {
        this.chars = new char[16];
        this.monitor = new Object();
    }

    public StringBuffer(int initialCapacity) {
        if(initialCapacity < 0)
        {
            throw new NegativeArraySizeException("StringBuffer: аргумент initialCapacity не может быть отрицательным.");
        }
        this.chars = new char[initialCapacity];
        this.monitor = new Object();
    }

    public StringBuffer(String initialContent) {
        int length;
        char[] chars;
        if(initialContent != null)
        {
            length = initialContent.length();
            initialContent.getChars(0, length, chars = new char[length + 16], 0);
        } else
        {
            length = 0;
            chars = new char[16];
        }
        this.length = length;
        this.chars = chars;
        this.monitor = new Object();
    }

    public String toString() {
        return new String(this);
    }

    public void ensureCapacity(int minimumCapacity) {
        synchronized(monitor)
        {
            if(minimumCapacity > chars.length) madeLarger(minimumCapacity);
        }
    }

    public void setLength(int length) {
        if(length < 0)
        {
            throw new IndexOutOfBoundsException("StringBuffer.setLength: аргумент length не может быть отрицательным.");
        }
        synchronized(monitor)
        {
            int len;
            char[] c;
            if(length > (c = chars).length) c = madeLarger(length);
            len = this.length;
            this.length = length;
            if(length > len)
            {
                MalikSystem.arrayfill_short(c, len, length - len, 0);
            }
            else if(length < len && shared)
            {
                if(length > 0)
                {
                    copy();
                } else
                {
                    chars = new char[16];
                    shared = false;
                }
            }
        }
    }

    public void setCharAt(int index, char character) {
        int error = 0;
        synchronized(monitor)
        {
            label0:
            {
                if(index < 0 || index >= length)
                {
                    error = 1;
                    break label0;
                }
                (shared ? copy() : chars)[index] = character;
            }
        }
        if(error == 1)
        {
            throw new StringIndexOutOfBoundsException("StringBuffer.setCharAt: аргумент index выходит из диапазона.");
        }
    }

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

    public char charAt(int index) {
        char result;
        int error = 0;
        synchronized(monitor)
        {
            label0:
            {
                if(index < 0 || index >= length)
                {
                    error = 1;
                    result = 0;
                    break label0;
                }
                result = chars[index];
            }
        }
        if(error == 1)
        {
            throw new StringIndexOutOfBoundsException("StringBuffer.charAt: аргумент index выходит из диапазона.");
        }
        return result;
    }

    public int length() {
        return length;
    }

    public int capacity() {
        return chars.length;
    }

    public StringBuffer insert(int position, boolean value) {
        return insert(position, value ? "true" : "false");
    }

    public StringBuffer insert(int position, char value) {
        int error = 0;
        synchronized(monitor)
        {
            label0:
            {
                int len;
                int newlen;
                char[] c;
                if(position < 0 || position > (len = length))
                {
                    error = 1;
                    break label0;
                }
                if((newlen = len + 1) > (c = chars).length) c = madeLarger(newlen);
                if(len > position)
                {
                    if(shared) c = copy();
                    MalikSystem.arraycopyb_short(c, len, c, newlen, len - position);
                }
                c[position] = value;
                length = newlen;
            }
        }
        if(error == 1)
        {
            throw new StringIndexOutOfBoundsException("StringBuffer.insert: аргумент position выходит из диапазона.");
        }
        return this;
    }

    public StringBuffer insert(int position, float value) {
        return insert(position, Float.toString(value));
    }

    public StringBuffer insert(int position, double value) {
        return insert(position, Double.toString(value));
    }

    public StringBuffer insert(int position, int value) {
        return insert(position, Integer.toString(value, 10));
    }

    public StringBuffer insert(int position, long value) {
        return insert(position, Long.toString(value, 10));
    }

    public StringBuffer insert(int position, char[] src) {
        int error;
        int length;
        if((length = src == null ? 0 : src.length) <= 0) return this;
        error = 0;
        synchronized(monitor)
        {
            label0:
            {
                int len;
                int newlen;
                char[] c;
                if(position < 0 || position > (len = this.length))
                {
                    error = 1;
                    break label0;
                }
                if(length <= 0) break label0;
                if((newlen = len + length) > (c = chars).length) c = madeLarger(newlen);
                if(len > position)
                {
                    if(shared) c = copy();
                    MalikSystem.arraycopyb_short(c, len, c, newlen, len - position);
                }
                MalikSystem.arraycopyf_short(src, 0, c, position, length);
                this.length = newlen;
            }
        }
        if(error == 1)
        {
            throw new StringIndexOutOfBoundsException("StringBuffer.insert: аргумент position выходит из диапазона.");
        }
        return this;
    }

    public StringBuffer insert(int position, String value) {
        int length;
        int error = 0;
        if(value == null) value = "null";
        length = value.length();
        synchronized(monitor)
        {
            label0:
            {
                int len;
                int newlen;
                char[] c;
                if(position < 0 || position > (len = this.length))
                {
                    error = 1;
                    break label0;
                }
                if(length <= 0) break label0;
                if((newlen = len + length) > (c = chars).length) c = madeLarger(newlen);
                if(len > position)
                {
                    if(shared) c = copy();
                    MalikSystem.arraycopyb_short(c, len, c, newlen, len - position);
                }
                value.getChars(0, length, c, position);
                this.length = newlen;
            }
        }
        if(error == 1)
        {
            throw new StringIndexOutOfBoundsException("StringBuffer.insert: аргумент position выходит из диапазона.");
        }
        return this;
    }

    public StringBuffer insert(int position, Object value) {
        return insert(position, value == null ? "null" : value.toString());
    }

    public StringBuffer append(boolean value) {
        return append(value ? "true" : "false");
    }

    public StringBuffer append(char value) {
        synchronized(monitor)
        {
            int len;
            int newlen;
            char[] c;
            if((newlen = (len = length) + 1) > (c = chars).length) c = madeLarger(newlen);
            c[len] = value;
            length = newlen;
        }
        return this;
    }

    public StringBuffer append(float value) {
        return append(Float.toString(value));
    }

    public StringBuffer append(double value) {
        return append(Double.toString(value));
    }

    public StringBuffer append(int value) {
        return append(Integer.toString(value, 10));
    }

    public StringBuffer append(long value) {
        return append(Long.toString(value, 10));
    }

    public StringBuffer append(char[] src) {
        return src == null ? this : append(src, 0, src.length);
    }

    public StringBuffer append(char[] src, int offset, int length) {
        long bounds;
        if(src == null) return this;
        offset = (int) (bounds = Array.intersectBound(src.length, offset, length));
        length = (int) (bounds >> 32);
        synchronized(monitor)
        {
            int len;
            int newlen;
            char[] c;
            if((newlen = (len = this.length) + length) > (c = chars).length) c = madeLarger(newlen);
            if(length > 0)
            {
                MalikSystem.arraycopyf_short(src, offset, c, len, length);
                this.length = newlen;
            }
        }
        return this;
    }

    public StringBuffer append(String value) {
        int length;
        if(value == null) value = "null";
        length = value.length();
        synchronized(monitor)
        {
            int len;
            int newlen;
            char[] c;
            if((newlen = (len = this.length) + length) > (c = chars).length) c = madeLarger(newlen);
            if(length > 0)
            {
                value.getChars(0, length, c, len);
                this.length = newlen;
            }
        }
        return this;
    }

    public StringBuffer append(Object value) {
        return append(value == null ? "null" : value.toString());
    }

    public StringBuffer deleteCharAt(int index) {
        int error = 0;
        synchronized(monitor)
        {
            label0:
            {
                int cnt;
                int len;
                char[] c;
                if(index < 0 || index >= (len = length))
                {
                    error = 1;
                    break label0;
                }
                c = shared ? copy() : chars;
                if((cnt = --len - index) > 0) MalikSystem.arraycopyf_short(c, index + 1, c, index, cnt);
                length = len;
            }
        }
        if(error == 1)
        {
            throw new StringIndexOutOfBoundsException("StringBuffer.deleteCharAt: аргумент index выходит из диапазона.");
        }
        return this;
    }

    public StringBuffer delete(int beginIndex, int endIndex) {
        int error = 0;
        synchronized(monitor)
        {
            label0:
            {
                int cnt;
                int len;
                char[] c;
                if(endIndex > (len = length)) endIndex = len;
                if((beginIndex | endIndex) < 0 || beginIndex > endIndex)
                {
                    error = 1;
                    break label0;
                }
                if((cnt = endIndex - beginIndex) <= 0) break label0;
                c = shared ? copy() : chars;
                if(len > endIndex) MalikSystem.arraycopyf_short(c, endIndex, c, beginIndex, len - endIndex);
                length = len - cnt;
            }
        }
        if(error == 1)
        {
            throw new StringIndexOutOfBoundsException("StringBuffer.delete: индекс выходит из диапазона.");
        }
        return this;
    }

    public StringBuffer reverse() {
        synchronized(monitor)
        {
            int len = length;
            char[] c = shared ? copy() : chars;
            for(int i = len >> 1, j = len - i; i-- > 0; j++)
            {
                char ci = c[i];
                c[i] = c[j];
                c[j] = ci;
            }
        }
        return this;
    }

    void setShared() {
        shared = true;
    }

    char[] content() {
        return chars;
    }

    Object monitor() {
        return monitor;
    }

    private char[] copy() {
        char[] result;
        MalikSystem.arraycopyf_short(result = chars, 0, result = chars = new char[result.length], 0, length);
        shared = false;
        return result;
    }

    private char[] madeLarger(int neededCharacters) {
        int capacity;
        char[] result;
        if((capacity = ((result = chars).length + 1) << 1) <= 0)
        {
            capacity = Integer.MAX_VALUE;
        }
        else if(capacity < neededCharacters)
        {
            capacity = neededCharacters;
        }
        MalikSystem.arraycopyf_short(result, 0, result = chars = new char[capacity], 0, length);
        shared = false;
        return result;
    }
}
