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

import javax.microedition.lcdui.*;
import malik.emulator.application.*;
import malik.emulator.microedition.*;

public final class InputStringBuilder extends MultilinedStringBuilder implements CharacterDestination
{
    public static final char PASSWORD_CHAR = '\u2022';
    public static final int CAPACITY = 0x1000;

    static final String[][] charsets;
    static final Command[] commands;
    static final Command commandLine;
    static final Command commandChar;
    static final Command commandMode;
    static final Command commandCopy;
    static final Command commandPaste;
    static final Command commandSelect;
    static final Command commandInsert;
    static final Command commandBack;
    static String clipboard;

    static {
        Command line = new Command("Вставить перенос строки", Command.SCREEN, Integer.MIN_VALUE);
        Command charset = new Command("Вставить символ…", Command.SCREEN, Integer.MIN_VALUE);
        Command mode = new Command("Язык ввода…", Command.SCREEN, Integer.MIN_VALUE);
        Command copy = new Command("Скопировать", Command.SCREEN, Integer.MIN_VALUE);
        Command paste = new Command("Вставить", Command.SCREEN, Integer.MIN_VALUE);
        Command select = new Command("Выбрать", Command.OK, 0);
        Command insert = new Command("Вставить", Command.OK, 0);
        Command back = new Command("Назад", Command.BACK, 0);
        charsets = new String[][] {
            new String[] { "Базовая латиница", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" },
            new String[] { "Кириллица (русский язык)", "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя" },
            new String[] { "Кириллица (вся)", "ЀЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯѐђѓєѕіїјљњћќѝўџабвгдеёжзийклмнопрстуфхцчшщъыьэюя" },
            new String[] { "Прочие символы", " \'\"#$%&()+-*/<=>,.:;!?@[\\]^_`{|}~§¡¿…¤¢£¥€№•·°¹²³¼½¾‰±×÷µ–—©®™‹›«»" }
        };
        commands = new Command[] { line, charset, mode, copy, paste };
        commandLine = line;
        commandChar = charset;
        commandMode = mode;
        commandCopy = copy;
        commandPaste = paste;
        commandSelect = select;
        commandInsert = insert;
        commandBack = back;
        clipboard = "";
    }

    public static boolean isConstraintsValid(int constraints) {
        return (constraints & Input.CONSTRAINT_MASK) < 6 && (constraints >>> 16) < 0x40;
    }

    public static boolean isConstraintsMatch(int constraints, char[] src, int offset, int length) {
        boolean f;
        long bounds;
        if(!isConstraintsValid(constraints))
        {
            throw new IllegalArgumentException("InputStringBuilder.isConstraintsMatch: аргумент constraints имеет недопустимое значение.");
        }
        if(src == null) return true;
        offset = (int) (bounds = Array.intersectBound(src.length, offset, length));
        length = (int) (bounds >> 32);
        switch(constraints & Input.CONSTRAINT_MASK)
        {
        case Input.ANY:
        case Input.EMAILADDR:
        case Input.URL:
            return true;
        case Input.NUMERIC:
            for(int j = offset, i = 0; i < length; j++, i++) switch(src[j])
            {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                break;
            case '-':
                if(i > 0) return false;
                break;
            default:
                return false;
            }
            return true;
        case Input.PHONENUMBER:
            for(int j = offset, i = 0; i < length; j++, i++) switch(src[j])
            {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '*':
            case '#':
                break;
            case '+':
                if(i > 0) return false;
                break;
            default:
                return false;
            }
            return true;
        case Input.DECIMAL:
            f = false;
            for(int j = offset, i = 0; i < length; j++, i++) switch(src[j])
            {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                break;
            case '.':
                if(f) return false;
                f = true;
                break;
            case '-':
                if(i > 0) return false;
                break;
            default:
                return false;
            }
            return true;
        }
        return false;
    }

    public static boolean isConstraintsMatch(int constraints, String string) {
        boolean f;
        int length;
        if(!isConstraintsValid(constraints))
        {
            throw new IllegalArgumentException("InputStringBuilder.isConstraintsMatch: аргумент constraints имеет недопустимое значение.");
        }
        if(string == null) return true;
        length = string.length();
        switch(constraints & Input.CONSTRAINT_MASK)
        {
        case Input.ANY:
        case Input.EMAILADDR:
        case Input.URL:
            return true;
        case Input.NUMERIC:
            for(int i = 0; i < length; i++) switch(string.charAt(i))
            {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                break;
            case '-':
                if(i > 0) return false;
                break;
            default:
                return false;
            }
            return true;
        case Input.PHONENUMBER:
            for(int i = 0; i < length; i++) switch(string.charAt(i))
            {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '*':
            case '#':
                break;
            case '+':
                if(i > 0) return false;
                break;
            default:
                return false;
            }
            return true;
        case Input.DECIMAL:
            f = false;
            for(int i = 0; i < length; i++) switch(string.charAt(i))
            {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                break;
            case '.':
                if(f) return false;
                f = true;
                break;
            case '-':
                if(i > 0) return false;
                break;
            default:
                return false;
            }
            return true;
        }
        return false;
    }

    public class AdditionalCapabilities extends Object implements CommandListener, CommandOwner
    {
        private final boolean lineSeparatorAllowed;
        private int modeIndex;
        private final CharacterList[] screensWithChars;
        private StringList screenSelectCharset;
        private StringList screenSelectMode;
        protected Displayable source;
        protected final Object monitor;

        public AdditionalCapabilities(boolean lineSeparatorAllowed, Object monitor) {
            this.lineSeparatorAllowed = lineSeparatorAllowed;
            this.screensWithChars = new CharacterList[InputStringBuilder.charsets.length];
            this.monitor = monitor == null ? new Object() : monitor;
        }

        public void commandAction(Command command, Displayable screen) {
            StringList screen1;
            InputStringBuilder parent = InputStringBuilder.this;
            if(command == InputStringBuilder.commandLine)
            {
                synchronized(monitor)
                {
                    parent.insert(CharacterDestination.LINE);
                }
                return;
            }
            if(command == InputStringBuilder.commandChar)
            {
                DeviceManager.getInstance().getCurrentDisplay().setCurrent(getScreenSelectCharset());
                source = screen;
                return;
            }
            if(command == InputStringBuilder.commandMode)
            {
                DeviceManager.getInstance().getCurrentDisplay().setCurrent(getScreenSelectMode());
                source = screen;
                return;
            }
            if(command == InputStringBuilder.commandCopy)
            {
                synchronized(monitor)
                {
                    String text;
                    if((text = parent.getInputText()).length() > 0) InputStringBuilder.clipboard = text;
                }
                return;
            }
            if(command == InputStringBuilder.commandPaste)
            {
                synchronized(monitor)
                {
                    parent.insert(InputStringBuilder.clipboard);
                }
                return;
            }
            if(screen == (screen1 = screenSelectCharset))
            {
                if(command == InputStringBuilder.commandSelect)
                {
                    DeviceManager.getInstance().getCurrentDisplay().setCurrent(getScreenWithChars(screen1.getSelectedIndex()));
                    return;
                }
                if(command == InputStringBuilder.commandBack)
                {
                    DeviceManager.getInstance().getCurrentDisplay().setCurrent(source);
                    source = null;
                }
                return;
            }
            if(screen == (screen1 = screenSelectMode))
            {
                if(command == InputStringBuilder.commandSelect)
                {
                    int index;
                    modeIndex = index = screen1.getSelectedIndex();
                    parent.setInputMode(AvailableInputModes.instance.get(index));
                }
                DeviceManager.getInstance().getCurrentDisplay().setCurrent(source);
                source = null;
                return;
            }
            if(screen instanceof CharacterList)
            {
                if(command == InputStringBuilder.commandInsert)
                {
                    synchronized(monitor)
                    {
                        parent.insert(((CharacterList) screen).getSelectedChar());
                    }
                    return;
                }
                if(command == InputStringBuilder.commandBack) DeviceManager.getInstance().getCurrentDisplay().setCurrent(screenSelectCharset);
            }
        }

        public boolean isMyOwnedCommand(Command command) {
            Command[] commands;
            return Array.findf(commands = InputStringBuilder.commands, 0, command) < commands.length;
        }

        public Command[] getMyOwnedCommands() {
            boolean any;
            int constraints = (InputStringBuilder.this).constraints;
            int type = Input.CONSTRAINT_MASK & constraints;
            Command[] result;
            any = type == Input.ANY || type == Input.EMAILADDR || type == Input.URL;
            switch((constraints & (Input.PASSWORD | Input.UNEDITABLE)) >> 16)
            {
            case (Input.PASSWORD | Input.UNEDITABLE) >> 16:
                result = new Command[] { };
                break;
            case Input.UNEDITABLE >> 16:
                result = new Command[] { InputStringBuilder.commandCopy };
                break;
            case Input.PASSWORD >> 16:
                if(!any)
                {
                    result = new Command[] { InputStringBuilder.commandPaste };
                }
                else if(lineSeparatorAllowed)
                {
                    result = new Command[] { InputStringBuilder.commandLine, InputStringBuilder.commandChar, InputStringBuilder.commandMode, InputStringBuilder.commandPaste };
                }
                else
                {
                    result = new Command[] { InputStringBuilder.commandChar, InputStringBuilder.commandMode, InputStringBuilder.commandPaste };
                }
                break;
            default:
                if(!any)
                {
                    result = new Command[] { InputStringBuilder.commandCopy, InputStringBuilder.commandPaste };
                }
                else if(lineSeparatorAllowed)
                {
                    result = new Command[] {
                        InputStringBuilder.commandLine, InputStringBuilder.commandChar, InputStringBuilder.commandMode,
                        InputStringBuilder.commandCopy, InputStringBuilder.commandPaste
                    };
                }
                else
                {
                    result = new Command[] { InputStringBuilder.commandChar, InputStringBuilder.commandMode, InputStringBuilder.commandCopy, InputStringBuilder.commandPaste };
                }
                break;
            }
            return result;
        }

        public final InputStringBuilder parentBuilder() {
            return InputStringBuilder.this;
        }

        private Displayable getScreenWithChars(int index) {
            CharacterList[] array;
            CharacterList result;
            if((result = (array = screensWithChars)[index]) == null)
            {
                String[] charset = InputStringBuilder.charsets[index];
                (result = array[index] = new CharacterList(charset[0], null, charset[1])).setCommandListener(this);
                result.addCommand(InputStringBuilder.commandInsert);
                result.addCommand(InputStringBuilder.commandBack);
            }
            return result;
        }

        private Displayable getScreenSelectCharset() {
            StringList result;
            if((result = screenSelectCharset) == null)
            {
                String[][] charsets = InputStringBuilder.charsets;
                (result = screenSelectCharset = new StringList("Выберите набор символов", null)).setCommandListener(this);
                for(int len = charsets.length, i = 0; i < len; i++) result.append(charsets[i][0]);
                result.addCommand(InputStringBuilder.commandSelect);
                result.addCommand(InputStringBuilder.commandBack);
            }
            return result;
        }

        private Displayable getScreenSelectMode() {
            StringList result;
            if((result = screenSelectMode) == null)
            {
                AvailableInputModes modes = AvailableInputModes.instance;
                (result = screenSelectMode = new StringList("Выберите язык ввода", null)).setCommandListener(this);
                for(int len = modes.size(), i = 0; i < len; i++) result.append(modes.get(i).getLocalizedName());
                result.addCommand(InputStringBuilder.commandSelect);
                result.addCommand(InputStringBuilder.commandBack);
            }
            result.setSelectedIndex(modeIndex);
            return result;
        }
    }

    private boolean modified;
    int constraints;
    int caretPosition;
    int maximumLength;
    final char[] realstr;
    private InputMode mode;

    public InputStringBuilder(String inputText, int maximumLength, int constraints) {
        super(CAPACITY);
        int length;
        char[] content;
        char[] realstr;
        if(inputText == null) inputText = "";
        if(maximumLength <= 0)
        {
            throw new IllegalArgumentException("InputStringBuilder: аргумент maximumLength может быть только положительным.");
        }
        if(!isConstraintsValid(constraints))
        {
            throw new IllegalArgumentException("InputStringBuilder: аргумент constraints имеет недопустимое значение.");
        }
        if(!isConstraintsMatch(constraints, inputText))
        {
            throw new IllegalArgumentException("InputStringBuilder: текст не соответствует ограничениям.");
        }
        if((length = inputText.length()) > maximumLength)
        {
            throw new IllegalArgumentException("InputStringBuilder: длина текста превышает максимальную длину.");
        }
        if(length > CAPACITY) length = CAPACITY;
        if(maximumLength > CAPACITY) maximumLength = CAPACITY;
        content = this.content;
        realstr = new char[CAPACITY];
        inputText.getChars(0, length, realstr, 0);
        if((constraints & Input.PASSWORD) == 0)
        {
            Array.copy(realstr, 0, content, 0, CAPACITY);
        } else
        {
            Array.fill(content, 0, CAPACITY, PASSWORD_CHAR);
        }
        this.length = length;
        this.modified = true;
        this.constraints = constraints;
        this.maximumLength = maximumLength;
        this.realstr = realstr;
        this.mode = AvailableInputModes.instance.system();
    }

    public void clear() {
        length = 0;
        caretPosition = 0;
        modified = true;
    }

    public void set(int position, char character) {
        char previous;
        int len = length;
        int constraints;
        char[] buffer;
        if(position < 0 || position >= len)
        {
            throw new StringIndexOutOfBoundsException("StringBuilder.set: аргумент position выходит из диапазона.");
        }
        if((previous = (buffer = realstr)[position]) != character)
        {
            buffer[position] = character;
            if(!isConstraintsMatch(constraints = this.constraints, buffer, 0, len))
            {
                buffer[position] = previous;
            } else
            {
                if((constraints & Input.PASSWORD) == 0) content[position] = character;
                modified = true;
            }
        }
    }

    public void insert(char character) {
        int constraints;
        int oldLen;
        int newLen;
        int remLen;
        int oldPos;
        int newPos;
        char[] buffer;
        if(character == BACK)
        {
            if((oldPos = caretPosition) > 0)
            {
                newLen = (oldLen = length) - 1;
                remLen = oldLen - oldPos;
                newPos = oldPos - 1;
                Array.copy(buffer = realstr, oldPos, buffer, newPos, remLen);
                if((this.constraints & Input.PASSWORD) == 0) Array.copy(buffer, 0, content, 0, CAPACITY);
                length = newLen;
                caretPosition = newPos;
                modified = true;
            }
        }
        else if((newLen = (oldLen = length) + 1) <= maximumLength)
        {
            remLen = oldLen - (oldPos = caretPosition);
            newPos = oldPos + 1;
            Array.copy(buffer = realstr, oldPos, buffer, newPos, remLen);
            buffer[oldPos] = character;
            if(!isConstraintsMatch(constraints = this.constraints, buffer, 0, newLen))
            {
                Array.copy(buffer, newPos, buffer, oldPos, remLen);
            } else
            {
                if((constraints & Input.PASSWORD) == 0) Array.copy(buffer, 0, content, 0, CAPACITY);
                length = newLen;
                caretPosition = newPos;
                modified = true;
            }
        }
    }

    public void insert(String string) {
        int delta;
        int constraints;
        int oldLen;
        int newLen;
        int remLen;
        int oldPos;
        int newPos;
        char[] buffer;
        if((delta = string != null ? string.length() : 0) > 0 && (newLen = (oldLen = length) + delta) <= maximumLength)
        {
            remLen = oldLen - (oldPos = caretPosition);
            newPos = oldPos + delta;
            Array.copy(buffer = realstr, oldPos, buffer, newPos, remLen);
            string.getChars(0, delta, buffer, oldPos);
            if(!isConstraintsMatch(constraints = this.constraints, buffer, 0, newLen))
            {
                Array.copy(buffer, newPos, buffer, oldPos, remLen);
            } else
            {
                if((constraints & Input.PASSWORD) == 0) Array.copy(buffer, 0, content, 0, CAPACITY);
                length = newLen;
                caretPosition = newPos;
                modified = true;
            }
        }
    }

    public void keyboardEvent(KeyboardEvent event) {
        if((constraints & Input.UNEDITABLE) == 0 && event.getAction() != KeyboardEvent.ACTION_KEY_RELEASED) switch(event.getKey())
        {
        case KeyboardEvent.KEY_TAB:
            break;
        case KeyboardEvent.KEY_ENTER:
            insert(LINE);
            break;
        case KeyboardEvent.KEY_BACKSPACE:
            insert(BACK);
            break;
        default:
            mode.keyboardEvent(event, this);
            break;
        }
    }

    public void setModified(boolean modified) {
        this.modified = modified;
    }

    public void setConstraints(int constraints) {
        int oldConstraints;
        if(!isConstraintsValid(constraints))
        {
            throw new IllegalArgumentException("InputStringBuilder.setConstraints: аргумент constraints имеет недопустимое значение.");
        }
        if((oldConstraints = this.constraints) != constraints)
        {
            char[] buffer = realstr;
            if(!isConstraintsMatch(constraints, buffer, 0, length))
            {
                length = 0;
                caretPosition = 0;
                modified = true;
            }
            if((oldConstraints & Input.PASSWORD) == 0 && (constraints & Input.PASSWORD) != 0)
            {
                Array.fill(content, 0, CAPACITY, PASSWORD_CHAR);
                modified = true;
            }
            else if((oldConstraints & Input.PASSWORD) != 0 && (constraints & Input.PASSWORD) == 0)
            {
                Array.copy(buffer, 0, content, 0, CAPACITY);
                modified = true;
            }
            this.constraints = constraints;
        }
    }

    public void setCaretPosition(int caretPosition) {
        int len = length;
        if(caretPosition < 0) caretPosition = 0;
        if(caretPosition > len) caretPosition = len;
        if(this.caretPosition != caretPosition)
        {
            this.caretPosition = caretPosition;
            modified = true;
        }
    }

    public void setMaximumLength(int maximumLength) {
        if(maximumLength <= 0)
        {
            throw new IllegalArgumentException("InputStringBuilder.setMaximumLength: аргумент maximumLength может быть только положительным.");
        }
        if(maximumLength > CAPACITY) maximumLength = CAPACITY;
        if(this.maximumLength != maximumLength)
        {
            if(length > maximumLength)
            {
                length = maximumLength;
                modified = true;
            }
            if(caretPosition > maximumLength)
            {
                caretPosition = maximumLength;
                modified = true;
            }
            this.maximumLength = maximumLength;
        }
    }

    public void setInputText(String inputText) {
        int newLen;
        int constraints;
        if(inputText == null) inputText = "";
        if((newLen = inputText.length()) > maximumLength)
        {
            throw new IllegalArgumentException("InputStringBuilder.setInputText: длина текста превышает максимальную длину.");
        }
        if(!isConstraintsMatch(constraints = this.constraints, inputText))
        {
            throw new IllegalArgumentException("InputStringBuilder.setInputText: текст не соответствует ограничениям.");
        }
        if(newLen > 0)
        {
            char[] buffer = realstr;
            inputText.getChars(0, newLen, buffer, 0);
            if((constraints & Input.PASSWORD) == 0) Array.copy(buffer, 0, content, 0, CAPACITY);
        }
        length = newLen;
        caretPosition = newLen;
        modified = true;
    }

    public void setInputMode(InputMode mode) {
        this.mode = mode == null ? AvailableInputModes.instance.system() : mode;
    }

    public boolean isModified() {
        boolean result = modified;
        modified = false;
        return result;
    }

    public int getConstraints() {
        return constraints;
    }

    public int getCaretPosition() {
        return caretPosition;
    }

    public int getMaximumLength() {
        return maximumLength;
    }

    public String getInputText() {
        return new String(realstr, 0, length);
    }

    public InputMode getInputMode() {
        return mode;
    }

    public Input newInput() {
        return new Input() {
            public void insert(char[] src, int offset, int length, int position) {
                (InputStringBuilder.this).insert(position, src, offset, length);
            }

            public void insert(String text, int position) {
                (InputStringBuilder.this).insert(position, text);
            }

            public void delete(int offset, int length) {
                (InputStringBuilder.this).delete(offset, offset + length);
            }

            public void setConstraints(int constraints) {
                (InputStringBuilder.this).setConstraints(constraints);
            }

            public void setChars(char[] src, int offset, int length) {
                InputStringBuilder parent;
                (parent = InputStringBuilder.this).clear();
                parent.append(src, offset, length);
            }

            public void setString(String inputText) {
                InputStringBuilder parent;
                (parent = InputStringBuilder.this).clear();
                parent.append(inputText);
            }

            public void setInitialInputMode(String characterSubset) {
                (InputStringBuilder.this).setInputMode(AvailableInputModes.instance.get(characterSubset));
            }

            public int setMaxSize(int maximumSize) {
                if(maximumSize > InputStringBuilder.CAPACITY) maximumSize = InputStringBuilder.CAPACITY;
                (InputStringBuilder.this).setMaximumLength(maximumSize);
                return maximumSize;
            }

            public int size() {
                return (InputStringBuilder.this).length();
            }

            public int getConstraints() {
                return (InputStringBuilder.this).constraints;
            }

            public int getCaretPosition() {
                return (InputStringBuilder.this).caretPosition;
            }

            public int getChars(char[] dst) {
                int result;
                InputStringBuilder parent;
                Array.copy((parent = InputStringBuilder.this).realstr, 0, dst, 0, result = parent.length());
                return result;
            }

            public int getMaxSize() {
                return (InputStringBuilder.this).maximumLength;
            }

            public String getString() {
                return (InputStringBuilder.this).getInputText();
            }
        };
    }

    protected void insertChar(int position, char character) {
        insertChar(position, this.length, character);
    }

    protected void insertArray(int position, char[] src, int offset, int length) {
        insertArray(position, this.length, src, offset, length);
    }

    protected void insertString(int position, String string) {
        insertString(position, this.length, string);
    }

    protected void appendChar(char character) {
        int oldLen = this.length;
        insertChar(oldLen, oldLen, character);
    }

    protected void appendArray(char[] src, int offset, int length) {
        int oldLen = this.length;
        insertArray(oldLen, oldLen, src, offset, length);
    }

    protected void appendString(String string) {
        int oldLen = this.length;
        insertString(oldLen, oldLen, string);
    }

    protected void deletePortion(int beginPosition, int endPosition) {
        int oldLength = length;
        if(beginPosition < 0) beginPosition = 0;
        if(beginPosition > oldLength) beginPosition = oldLength;
        if(endPosition < beginPosition) endPosition = beginPosition;
        if(endPosition > oldLength) endPosition = oldLength;
        if(beginPosition < endPosition)
        {
            int delta = oldLength - endPosition;
            int oldPosition;
            char[] buffer;
            Array.copy(buffer = content, endPosition, buffer, beginPosition, delta);
            Array.copy(buffer = realstr, endPosition, buffer, beginPosition, delta);
            this.length = oldLength - (delta = endPosition - beginPosition);
            if((oldPosition = caretPosition) >= endPosition)
            {
                caretPosition = oldPosition - delta;
            }
            else if(oldPosition >= beginPosition)
            {
                caretPosition = beginPosition;
            }
            modified = true;
        }
    }

    private void insertChar(int position, int oldLen, char character) {
        int constraints;
        int newLen;
        int remLen;
        int oldPos;
        int newPos;
        char[] buffer;
        if((newLen = oldLen + 1) <= maximumLength)
        {
            remLen = oldLen - (oldPos = position < 0 ? 0 : position > oldLen ? oldLen : position);
            newPos = oldPos + 1;
            Array.copy(buffer = realstr, oldPos, buffer, newPos, remLen);
            buffer[oldPos] = character;
            if(!isConstraintsMatch(constraints = this.constraints, buffer, 0, newLen))
            {
                Array.copy(buffer, newPos, buffer, oldPos, remLen);
            } else
            {
                if((constraints & Input.PASSWORD) == 0) Array.copy(buffer, 0, content, 0, CAPACITY);
                this.length = newLen;
                if(oldPos <= (oldPos = caretPosition)) caretPosition = oldPos + 1;
                modified = true;
            }
        }
    }

    private void insertArray(int position, int oldLen, char[] src, int offset, int length) {
        int constraints;
        int newLen;
        int remLen;
        int oldPos;
        int newPos;
        char[] buffer;
        if(length > 0 && (newLen = oldLen + length) <= maximumLength)
        {
            remLen = oldLen - (oldPos = position < 0 ? 0 : position > oldLen ? oldLen : position);
            newPos = oldPos + length;
            Array.copy(buffer = realstr, oldPos, buffer, newPos, remLen);
            Array.copy(src, offset, buffer, oldPos, length);
            if(!isConstraintsMatch(constraints = this.constraints, buffer, 0, newLen))
            {
                Array.copy(buffer, newPos, buffer, oldPos, remLen);
            } else
            {
                if((constraints & Input.PASSWORD) == 0) Array.copy(buffer, 0, content, 0, CAPACITY);
                this.length = newLen;
                if(oldPos <= (oldPos = caretPosition)) caretPosition = oldPos + length;
                modified = true;
            }
        }
    }

    private void insertString(int position, int oldLen, String string) {
        int length;
        int constraints;
        int newLen;
        int remLen;
        int oldPos;
        int newPos;
        char[] buffer;
        if((length = string.length()) > 0 && (newLen = oldLen + length) <= maximumLength)
        {
            remLen = oldLen - (oldPos = position < 0 ? 0 : position > oldLen ? oldLen : position);
            newPos = oldPos + length;
            Array.copy(buffer = realstr, oldPos, buffer, newPos, remLen);
            string.getChars(0, length, buffer, oldPos);
            if(!isConstraintsMatch(constraints = this.constraints, buffer, 0, newLen))
            {
                Array.copy(buffer, newPos, buffer, oldPos, remLen);
            } else
            {
                if((constraints & Input.PASSWORD) == 0) Array.copy(buffer, 0, content, 0, CAPACITY);
                this.length = newLen;
                if(oldPos <= (oldPos = caretPosition)) caretPosition = oldPos + length;
                modified = true;
            }
        }
    }
}
