package org.runite.client;

import org.rs09.client.config.GameConfig;
import org.rs09.client.data.HashTable;
import org.rs09.client.util.ArrayUtils;

import java.applet.Applet;
import java.awt.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

public final class RSString implements Interface3 {

    static HashTable<LinkableRSString> interned;
    byte[] buffer;
    int length;
    private boolean mutable = true;

    /**
     * @return A RSString consisting of the actual bytes in the provided string.
     */
    public static RSString of(String string) {
        byte[] bytes = string.getBytes(StandardCharsets.ISO_8859_1);

        RSString rs = new RSString();
        rs.buffer = bytes;
        rs.length = 0;

        for (int i = 0; i < bytes.length; ++i) {
            if (bytes[i] != 0) {
                bytes[rs.length++] = bytes[i];
            }
        }

        return rs;
    }

    /**
     * @return A RSString that is interned and parsed.
     */
    public static RSString parse(String string) {
        byte[] bytes = Objects.requireNonNull(string).replace("RuneScape", GameConfig.SERVER_NAME).getBytes();

        int length = bytes.length;
        RSString jagString = new RSString();
        jagString.buffer = new byte[length];

        int i = 0;
        while (length > i) {
            int ascii = bytes[i++] & 0xFF;
            if (ascii <= 45 && ascii >= 40) {
                if (length <= i) {
                    break;
                }

                int var7 = bytes[i++] & 0xff;
                jagString.buffer[jagString.length++] = (byte) (-48 + var7 + 43 * (-40 + ascii));
            } else if (ascii != 0) {
                jagString.buffer[jagString.length++] = (byte) ascii;
            }
        }
        jagString.method1576();
        return jagString.intern();
    }

    static RSString stringCombiner(RSString[] var0) {
        if (var0.length >= 2) {
            return Class67.method1261(0, var0.length, var0);
        } else {
            throw new IllegalArgumentException();
        }
    }

    static RSString stringAnimator(int var1) {
        try {
            return Unsorted.method1723((byte) -117, false, var1);
        } catch (RuntimeException var3) {
            throw ClientErrorException.clientError(var3, "jj.C(" + var1 + ')');
        }
    }

    public final URL toURL() throws MalformedURLException {
        return new URL(new String(this.buffer, 0, this.length));
    }

    public final boolean equalsStringIgnoreCase(RSString other) {
        if (other == null) {
            return false;
        } else if (this.length == other.length) {

            for (int i = 0; i < this.length; ++i) {
                byte var5 = this.buffer[i];
                if (var5 >= 65 && var5 <= 90 || var5 >= -64 && var5 <= -34 && var5 != -41) {
                    var5 = (byte) (var5 + 32);
                }

                byte var6 = other.buffer[i];
                if (65 <= var6 && var6 <= 90 || -64 <= var6 && var6 <= -34 && var6 != -41) {
                    var6 = (byte) (var6 + 32);
                }

                if (var6 != var5) {
                    return false;
                }
            }

            return true;
        } else {
            return false;
        }
    }

    // TODO Refactor to support regular equals method
    public final boolean equalsString(RSString other) {
        if (other == null) {
            return false;
        } else if (this == other) {
            return true;
        } else if (this.length == other.length) {

            byte[] otherBuffer = other.buffer;
            byte[] thisBuffer = this.buffer;

            for (int i = 0; i < this.length; ++i) {
                if (thisBuffer[i] != otherBuffer[i]) {
                    return false;
                }
            }

            return true;
        } else {
            return false;
        }
    }

    public final int parseInt(int radix) {
        if (radix < 1 || radix > 36) {
            radix = 10;
        }

        boolean negate = false;
        boolean success = false;
        int value = 0;

        for (int i = 0; this.length > i; ++i) {
            int currentNumber = this.buffer[i] & 0xff;

            if (i == 0) {
                if (currentNumber == '-') {
                    negate = true;
                    continue;
                }

                if (currentNumber == '+') {
                    continue;
                }
            }

            if (currentNumber >= '0' && '9' >= currentNumber) {
                currentNumber -= '0';
            } else if ('A' <= currentNumber && currentNumber <= 'Z') {
                currentNumber -= 55;
            } else {
                if (currentNumber < 'a' || currentNumber > 'z') {
                    throw new NumberFormatException("invalid character in number with value: " + currentNumber + " ('" + ((char) currentNumber) + "'). This string: " + toString());
                }

                currentNumber -= 87;
            }

            if (currentNumber >= radix) {
                throw new NumberFormatException("got radix " + radix + ", but found number value " + currentNumber);
            }

            if (negate) {
                currentNumber = -currentNumber;
            }

            int newValue = currentNumber + value * radix;
            if (newValue / radix != value) {
                throw new NumberFormatException("integer overflow");
            }

            value = newValue;
            success = true;
        }

        if (success) {
            return value;
        } else {
            throw new NumberFormatException("failed to parse number");
        }
    }

    public final void drawString(Graphics g, int y, int x) {
        String string = new String(this.buffer, 0, this.length, StandardCharsets.ISO_8859_1);
        g.drawString(string, x, y);
    }

    final void append(RSString var1) {
        if (this.mutable) {
            if (var1.length + this.length > this.buffer.length) {
                int var3;
                for (var3 = 1; var3 < var1.length + this.length; var3 += var3) {
                }

                byte[] var4 = new byte[var3];
                ArrayUtils.arraycopy(this.buffer, 0, var4, 0, this.length);
                this.buffer = var4;
            }

            ArrayUtils.arraycopy(var1.buffer, 0, this.buffer, this.length, var1.length);
            this.length += var1.length;
        } else {
            throw new IllegalArgumentException();
        }
    }

    public final RSString toLowercase() {
        RSString lower = new RSString();
        lower.length = this.length;
        lower.buffer = new byte[this.length];

        for (int i = 0; i < this.length; ++i) {
            byte ch = this.buffer[i];
            if (65 <= ch && ch <= 90 || ch >= -64 && ch <= -34 && ch != -41) {
                ch = (byte) (ch + 32);
            }

            lower.buffer[i] = ch;
        }

        return lower;
    }

    /**
     * Returns a copy of this string with proper capitalization. Generally, this returns an all-lower string. Characters
     * are changed to uppercase based on the following rules:
     * - The character is the first character in the string
     * - The character is the first non-whitespace character after a '.', '!' or '?' character
     * <p>
     * Characters may be either lowercase or uppercase if the previous character is a whitespace character.
     *
     * @return A copy of this string with capitalization based on the rules above.
     */
    public final RSString properlyCapitalize() {
        byte state = 2;
        RSString newString = new RSString();
        newString.length = this.length;
        newString.buffer = new byte[this.length];

        for (int i = 0; i < this.length; ++i) {
            byte ch = this.buffer[i];
            if ((ch < 97 || 122 < ch) && (ch < -32 || ch > -2 || ch == -9)) { // if not lowercase chars
                if ((ch < 65 || ch > 90) && (ch < -64 || ch > -34 || ch == -41)) { // if not uppercase chars
                    if (ch != 46 && 33 != ch && ch != 63) { // if not `.`, `!` or `?`
                        if (32 == ch) { // if ' '
                            if (state != 2) {
                                state = 1;
                            }
                        } else { // if not ' '
                            state = 1;
                        }
                    } else { // if `.`, `!` or `?`
                        state = 2;
                    }
                } else { // if uppercase char
                    if (0 == state) {
                        ch = (byte) (ch + 32);
                    }

                    state = 0;
                }
            } else {
                if (state == 2) {
                    ch = (byte) (ch - 32);
                }

                state = 0;
            }

            newString.buffer[i] = ch;
        }

        return newString;
    }

    public final long hash() {
        long hash = 0L;
        for (int i = 0; i < this.length; ++i) {
            hash = (long) (this.buffer[i] & 0xff) + (hash << 5) - hash;
        }

        return hash;
    }

    public final int length() {
        return this.length;
    }

    public final RSString method1542(RSString var2, int var3, int var4) {
        if (!this.mutable) {
            throw new IllegalArgumentException();
        } else if (0 <= var3 && var3 <= var4 && var2.length >= var4) {
            if (this.length + (var4 - var3) > this.buffer.length) {
                int var5;
                for (var5 = 1; var5 < this.length + var2.length; var5 += var5) {
                }

                byte[] var6 = new byte[var5];
                ArrayUtils.arraycopy(this.buffer, 0, var6, 0, this.length);
                this.buffer = var6;
            }

            ArrayUtils.arraycopy(var2.buffer, var3, this.buffer, this.length, -var3 + var4);
            this.length += var4 + -var3;
            return this;
        } else {
            throw new IllegalArgumentException();
        }
    }

    final boolean isInteger() {
        return this.isInteger(10);
    }

    final RSString method1544(boolean var1) {
        try {
            RSString var2 = new RSString();
            var2.length = this.length;
            var2.buffer = new byte[var2.length];
            if (var1) {
                for (int var3 = 0; this.length > var3; ++var3) {
                    var2.buffer[this.length - var3 + -1] = this.buffer[var3];
                }

                return var2;
            } else {
                return null;
            }
        } catch (RuntimeException var4) {
            throw ClientErrorException.clientError(var4, "na.FB(" + var1 + ')');
        }
    }

    final RSString longToRSString() {
        try {
            RSString var2 = new RSString();
            var2.length = this.length;
            var2.buffer = new byte[this.length];
            boolean var3 = true;
            int var4 = 0;

            for (; var4 < this.length; ++var4) {
                byte var5 = this.buffer[var4];
                if (var5 == 95) {
                    var3 = true;
                    var2.buffer[var4] = 32;
                } else if (97 <= var5 && var5 <= 122 && var3) {
                    var3 = false;
                    var2.buffer[var4] = (byte) (-32 + var5);
                } else {
                    var2.buffer[var4] = var5;
                    var3 = false;
                }
            }

            return var2;
        } catch (RuntimeException var6) {
            throw ClientErrorException.clientError(var6, "na.G(" + (byte) -50 + ')');
        }
    }

    final int method1546(RSString var2) {
        int var3 = 0;
        int var4 = 0;
        int var6 = var2.length;
        int var5 = this.length;
        int var7 = this.length;
        int var8 = var2.length;
        int var9 = 0;
        int var10 = 0;

        while (var5 != 0 && var6 != 0) {
            if (var3 == 156 || var3 == 230) {
                var3 = 101;
            } else if (140 == var3 || var3 == 198) {
                var3 = 69;
            } else if (var3 == 223) {
                var3 = 115;
            } else {
                var3 = this.buffer[var9] & 0xFF;
                ++var9;
            }

            if (Unsorted.method2103(var3, -116)) {
                ++var7;
            } else {
                --var5;
            }

            if (var4 == 156 || 230 == var4) {
                var4 = 101;
            } else if (var4 == 140 || var4 == 198) {
                var4 = 69;
            } else if (223 == var4) {
                var4 = 115;
            } else {
                var4 = 255 & var2.buffer[var10];
                ++var10;
            }

            if (Unsorted.method2103(var4, -86)) {
                ++var8;
            } else {
                --var6;
            }

            if (Class158.anIntArray2004[var4] > Class158.anIntArray2004[var3]) {
                return -1;
            }

            if (Class158.anIntArray2004[var3] > Class158.anIntArray2004[var4]) {
                return 1;
            }
        }

        return var8 <= var7 ? (var7 > var8 ? 1 : 0) : -1;
    }

    final URL method1547(URL var1) throws MalformedURLException {
        try {

            return new URL(var1, new String(this.buffer, 0, this.length));
        } catch (RuntimeException var4) {
            throw ClientErrorException.clientError(var4, "na.EB(" + (var1 != null ? "{...}" : "null") + ',' + true + ')');
        }
    }

    final RSString method1548(int var2) {
        try {
            if (var2 > 0 && var2 <= 255) {
                RSString var3 = new RSString();
                var3.buffer = new byte[1 + this.length];
                var3.length = this.length + 1;
                ArrayUtils.arraycopy(this.buffer, 0, var3.buffer, 0, this.length);
                var3.buffer[this.length] = (byte) var2;
                return var3;
            } else {
                throw new IllegalArgumentException("invalid char");
            }
        } catch (RuntimeException var4) {
            throw ClientErrorException.clientError(var4, "na.OA(" + false + ',' + var2 + ')');
        }
    }

    final boolean endsWith(RSString var2) {
        if (var2.length > this.length) {
            return false;
        } else {
            int var3 = -var2.length + this.length;

            for (int var4 = 0; var4 < var2.length; ++var4) {
                if (this.buffer[var3 + var4] != var2.buffer[var4]) {
                    return false;
                }
            }

            return true;
        }
    }

    final int indexOf(RSString var1, int var2) {
        try {
            return var2 <= 49 ? -20 : this.method1566(var1, 0);
        } catch (RuntimeException var4) {
            throw ClientErrorException.clientError(var4, "na.A(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ')');
        }
    }

    public final int parseInt() {
        return this.parseInt(10);
    }

    final void method1553(int var1) {
        try {
            if (!this.mutable) {
                throw new IllegalArgumentException();
            } else if (var1 < 0) {
                throw new IllegalArgumentException();
            } else {
                int var3;
                if (this.buffer.length < var1) {
                    for (var3 = 1; var1 > var3; var3 += var3) {
                    }

                    byte[] var4 = new byte[var3];
                    ArrayUtils.arraycopy(this.buffer, 0, var4, 0, this.length);
                    this.buffer = var4;
                }

                for (var3 = this.length; var1 > var3; ++var3) {
                    this.buffer[var3] = 32;
                }

                this.length = var1;
            }
        } catch (RuntimeException var5) {
            throw ClientErrorException.clientError(var5, "na.RA(" + var1 + ',' + false + ')');
        }
    }

    public final String toString() {
        if (buffer == null) {
            throw new RuntimeException();
        }
        return new String(buffer);
    }

    final void method1554(Applet var2) throws Throwable {
        try {
            String var3 = new String(this.buffer, 0, this.length);
            Class42.method1057(var2, var3);
        } catch (RuntimeException var4) {
            throw ClientErrorException.clientError(var4, "na.AA(" + true + ',' + (var2 != null ? "{...}" : "null") + ')');
        }
    }

    final int method1555(int var1, int var2) {
        try {
            byte var4 = (byte) var1;
            for (int var5 = var2; this.length > var5; ++var5) {
                if (this.buffer[var5] == var4) {
                    return var5;
                }
            }

            return -1;
        } catch (RuntimeException var6) {
            throw ClientErrorException.clientError(var6, "na.NA(" + var1 + ',' + var2 + ',' + 1536 + ')');
        }
    }

    final RSString substring(int start) {
        return this.substring(start, this.length, (byte) -74 ^ -74);
    }

    // TODO Why is `dstpos` a thing? what good does it serve?
    public final RSString substring(int start, int end, int dstpos) {
        RSString subs = new RSString();
        subs.length = end - start;
        subs.buffer = new byte[end - start];
        ArrayUtils.arraycopy(this.buffer, start, subs.buffer, dstpos, subs.length);
        return subs;
    }

    final boolean startsWith(RSString other) {
        if (other.length > this.length) {
            return false;
        }

        for (int i = 0; i < other.length; ++i) {
            if (other.buffer[i] != this.buffer[i]) {
                return false;
            }
        }

        return true;
    }

    public final boolean equals(Object var1) {
        try {
            if (var1 instanceof RSString) {
                return this.equalsString((RSString) var1);
            } else {
                throw new IllegalArgumentException();
            }
        } catch (RuntimeException var3) {
            throw ClientErrorException.clientError(var3, "na.equals(" + (var1 != null ? "{...}" : "null") + ')');
        }
    }

    final int method1559(RSString var1) {
        try {

            int var3;
            if (var1.length < this.length) {
                var3 = var1.length;
            } else {
                var3 = this.length;
            }

            for (int var4 = 0; var3 > var4; ++var4) {
                if ((255 & this.buffer[var4]) < (var1.buffer[var4] & 0xFF)) {
                    return -1;
                }

                if ((this.buffer[var4] & 0xFF) > (var1.buffer[var4] & 0xFF)) {
                    return 1;
                }
            }

            if (var1.length > this.length) {
                return -1;
            } else if (this.length <= var1.length) {
                return 0;
            } else {
                return 1;
            }
        } catch (RuntimeException var5) {
            throw ClientErrorException.clientError(var5, "na.QA(" + (var1 != null ? "{...}" : "null") + ',' + -1 + ')');
        }
    }

    final RSString method1560(RSString var1, RSString var3) {
        try {
            int var4 = this.length;
            int var5 = var1.length - var3.length;
            int var6 = 0;

            while (true) {
                int var7 = this.method1566(var3, var6);
                if (0 > var7) {
                    var6 = 0;
                    RSString var10 = Unsorted.emptyString(var4);

                    while (true) {
                        int var8 = this.method1566(var3, var6);
                        if (0 > var8) {
                            while (this.length > var6) {
                                Objects.requireNonNull(var10).appendCharacter(255 & this.buffer[var6++]);
                            }

                            if (false) {
                                this.method1567(-5, (byte) -91);
                            }

                            return var10;
                        }

                        while (var6 < var8) {
                            Objects.requireNonNull(var10).appendCharacter(this.buffer[var6++] & 0xFF);
                        }

                        Objects.requireNonNull(var10).append(var1);
                        var6 += var3.length;
                    }
                }

                var6 = var7 - -var3.length;
                var4 += var5;
            }
        } catch (RuntimeException var9) {
            throw ClientErrorException.clientError(var9, "na.IA(" + (var1 != null ? "{...}" : "null") + ',' + true + ',' + (var3 != null ? "{...}" : "null") + ')');
        }
    }

    public final int hashCode() {
        try {
            return this.method1574();
        } catch (RuntimeException var2) {
            throw ClientErrorException.clientError(var2, "na.hashCode()");
        }
    }

    private boolean isInteger(int radix) {
        if (radix < 1 || radix > 36) {
            radix = 10;
        }

        boolean success = false;
        boolean negate = false;
        int value = 0;

        for (int i = 0; i < this.length; ++i) {
            int current = this.buffer[i] & 0xFF;
            if (0 == i) {
                if (current == 45) {
                    negate = true;
                    continue;
                }

                if (current == 43) {
                    continue;
                }
            }

            if (current >= 48 && current <= 57) {
                current -= 48;
            } else if (current >= 65 && current <= 90) {
                current -= 55;
            } else {
                if (97 > current || current > 122) {
                    return false;
                }

                current -= 87;
            }

            if (radix <= current) {
                return false;
            }

            if (negate) {
                current = -current;
            }

            int newValue = current + radix * value;
            if (newValue / radix != value) {
                return false;
            }

            value = newValue;
            success = true;
        }

        return success;
    }

    final boolean method1562(byte var1, RSString var2) {
        try {
            if (this.length < var2.length) {
                return false;
            } else {
                if (var1 != -32) {
                    this.length = 13;
                }

                for (int var3 = 0; var2.length > var3; ++var3) {
                    byte var4 = this.buffer[var3];
                    byte var5 = var2.buffer[var3];
                    if (var5 >= 65 && var5 <= 90 || -64 <= var5 && -34 >= var5 && -41 != var5) {
                        var5 = (byte) (var5 + 32);
                    }

                    if (65 <= var4 && var4 <= 90 || var4 >= -64 && -34 >= var4 && var4 != -41) {
                        var4 = (byte) (var4 + 32);
                    }

                    if (var5 != var4) {
                        return false;
                    }
                }

                return true;
            }
        } catch (RuntimeException var6) {
            throw ClientErrorException.clientError(var6, "na.HB(" + var1 + ',' + (var2 != null ? "{...}" : "null") + ')');
        }
    }

    final RSString method1563(int var1) {
        try {
            if (var1 <= 86) {
                this.trim(117);
            }

            return this;
        } catch (RuntimeException var3) {
            throw ClientErrorException.clientError(var3, "na.K(" + var1 + ')');
        }
    }

    final RSString trim(int var1) {
        try {
            if (var1 != 1) {
                Unsorted.method1535(null, null, 23, 68, 126, false, false);
            }

            int var2;
            for (var2 = 0; var2 < this.length && (0 <= this.buffer[var2] && 32 >= this.buffer[var2] || (255 & this.buffer[var2]) == 160); ++var2) {
            }

            int var3;
            for (var3 = this.length; var3 > var2 && (this.buffer[var3 - 1] >= 0 && this.buffer[var3 - 1] <= 32 || (255 & this.buffer[var3 + -1]) == 160); --var3) {
            }

            if (var2 == 0 && var3 == this.length) {
                return this;
            } else {
                RSString var4 = new RSString();
                var4.length = var3 + -var2;
                var4.buffer = new byte[var4.length];

                if (var4.length >= 0) System.arraycopy(this.buffer, var2, var4.buffer, 0, var4.length);

                return var4;
            }
        } catch (RuntimeException var6) {
            throw ClientErrorException.clientError(var6, "na.KA(" + var1 + ')');
        }
    }

    final RSString method1565() {
        try {
            byte var4 = (byte) 47;
            RSString var6 = new RSString();
            byte var5 = (byte) 32;
            var6.length = this.length;
            var6.buffer = new byte[this.length];

            for (int var7 = 0; var7 < this.length; ++var7) {
                byte var8 = this.buffer[var7];
                if (var4 == var8) {
                    var6.buffer[var7] = var5;
                } else {
                    var6.buffer[var7] = var8;
                }
            }

            return var6;
        } catch (RuntimeException var9) {
            throw ClientErrorException.clientError(var9, "na.HA(" + 32 + ',' + 40 + ',' + 47 + ')');
        }
    }

    final int method1566(RSString var1, int var2) {
        try {
            int var4 = var1.length;
            if (var2 >= this.length) {
                return var4 == 0 ? this.length : -1;
            } else {
                if (var2 < 0) {
                    var2 = 0;
                }

                if (var4 == 0) {
                    return var2;
                } else {
                    int var7 = this.length - var4;
                    byte[] var5 = var1.buffer;
                    byte var6 = var5[0];
                    int var8 = var2;

                    while (var7 >= var8) {
                        if (this.buffer[var8] != var6) {
                            do {
                                ++var8;
                                if (var8 > var7) {
                                    return -1;
                                }
                            } while (this.buffer[var8] != var6);
                        }

                        boolean var9 = true;
                        int var10 = 1 + var8;
                        int var11 = 1;

                        while (true) {
                            if (var11 < var4) {
                                if (this.buffer[var10] == var5[var11]) {
                                    ++var10;
                                    ++var11;
                                    continue;
                                }

                                var9 = false;
                            }

                            if (var9) {
                                return var8;
                            }

                            ++var8;
                            break;
                        }
                    }

                    return -1;
                }
            }
        } catch (RuntimeException var12) {
            throw ClientErrorException.clientError(var12, "na.CB(" + (var1 != null ? "{...}" : "null") + ',' + var2 + ',' + -1 + ')');
        }
    }

    final RSString[] method1567(int var1, byte var2) {
        try {
            int var3 = 0;

            for (int var4 = 0; var4 < this.length; ++var4) {
                if (this.buffer[var4] == var1) {
                    ++var3;
                }
            }

            RSString[] var11 = new RSString[1 + var3];
            if (var3 == 0) {
                var11[0] = this;
            } else {
                int var5 = 0;
                int var6 = 0;

                for (int var7 = 0; var3 > var7; ++var7) {
                    int var9;
                    for (var9 = 0; this.buffer[var9 + var6] != var1; ++var9) {
                    }

                    var11[var5++] = this.substring(var6, var6 - -var9, 0);
                    var6 += 1 + var9;
                }

                var11[var3] = this.substring(var6, this.length, 0);
            }
            return var11;
        } catch (RuntimeException var10) {
            throw ClientErrorException.clientError(var10, "na.GA(" + var1 + ',' + var2 + ')');
        }
    }

    final byte[] method1568() {
        try {
            byte[] var2 = new byte[this.length];
            ArrayUtils.arraycopy(this.buffer, 0, var2, 0, this.length);
            return var2;
        } catch (RuntimeException var3) {
            throw ClientErrorException.clientError(var3, "na.H(" + 0 + ')');
        }
    }

    final int charAt(int var1, byte var2) {
        try {
            return this.buffer[var1] & 0xFF;
        } catch (RuntimeException var4) {
            throw ClientErrorException.clientError(var4, "na.SA(" + var1 + ',' + var2 + ')');
        }
    }

    public final RSString intern() {
        final long hash = this.hash();
        synchronized (RSString.class) {
            LinkableRSString linkable;
            if (interned == null) {
                interned = new HashTable<>(4096);
            } else {
                for (linkable = interned.get(hash); null != linkable; linkable = interned.nextInBucket()) {
                    if (this.equalsString(linkable.value)) {
                        return linkable.value;
                    }
                }
            }

            linkable = new LinkableRSString();

            linkable.value = this;
            mutable = false;
            interned.put(hash, linkable);
        }
        return this;
    }

    final void appendCharacter(int character) {
        if (0 < character && character <= 255) {
            if (this.mutable) {
                if (this.length == this.buffer.length) {
                    // TODO Is this really just making a new buffer with the size + 1? Why do we have to loop for that?
                    int newBufferSize;
                    for (newBufferSize = 1; this.length >= newBufferSize; newBufferSize += newBufferSize) {
                    }

                    byte[] newBuffer = new byte[newBufferSize];
                    ArrayUtils.arraycopy(this.buffer, 0, newBuffer, 0, this.length);
                    this.buffer = newBuffer;
                }

                this.buffer[this.length++] = (byte) character;
            } else {
                throw new IllegalArgumentException();
            }
        } else {
            throw new IllegalArgumentException("invalid char:" + character);
        }
    }

    final RSString getParamValue(Applet applet) {
        String string = new String(this.buffer, 0, this.length);
        String paramValue = applet.getParameter(string);
        return null == paramValue ? null : of(paramValue);
    }

    final int method1574() {
        try {
            int var2 = 0;

            for (int var3 = 0; var3 < this.length; ++var3) {
                var2 = (255 & this.buffer[var3]) + -var2 + (var2 << 5);
            }

            return var2;
        } catch (RuntimeException var4) {
            throw ClientErrorException.clientError(var4, "na.J(" + false + ')');
        }
    }

    public final int method1575(FontMetrics var2) {
        try {
            String var3;
            var3 = new String(this.buffer, 0, this.length, StandardCharsets.ISO_8859_1);

            return var2.stringWidth(var3);
        } catch (RuntimeException var6) {
            throw ClientErrorException.clientError(var6, "na.V(" + -21018 + ',' + (var2 != null ? "{...}" : "null") + ')');
        }
    }

    final RSString method1576() {
        try {
            if (this.mutable) {

                if (this.buffer.length != this.length) {
                    byte[] var2 = new byte[this.length];
                    ArrayUtils.arraycopy(this.buffer, 0, var2, 0, this.length);
                    this.buffer = var2;
                }

                return this;
            } else {
                throw new IllegalArgumentException();
            }
        } catch (RuntimeException var3) {
            throw ClientErrorException.clientError(var3, "na.PA(" + (byte) 90 + ')');
        }
    }

    final Object method1577(Applet var2) throws Throwable {
        try {
            String var3 = new String(this.buffer, 0, this.length);
            /*Object var4 = Class42.method1055(var3, var2);
            if (var4 instanceof String) {
                byte[] var5 = ((String) var4).getBytes();
                var4 = Class3_Sub13_Sub3.method178(var5, var5.length, 0);
            }
*/
            return null;
        } catch (RuntimeException var6) {
            throw ClientErrorException.clientError(var6, "na.JA(" + -1857 + ',' + (var2 != null ? "{...}" : "null") + ')');
        }
    }

    public final long toLong() {
        long var2 = 0L;
        for (int var4 = 0; var4 < this.length && var4 < 12; ++var4) {
            byte var5 = this.buffer[var4];
            var2 *= 37L;
            if (65 <= var5 && 90 >= var5) {
                var2 += -65 + 1 + var5;
            } else if (var5 >= 97 && 122 >= var5) {
                var2 += -97 + var5 + 1;
            } else if (var5 >= 48 && var5 <= 57) {
                var2 += -48 + var5 + 27;
            }
        }

        while (var2 % 37L == 0 && var2 != 0L) {
            var2 /= 37L;
        }

        return var2;
    }

    final RSString method1579() {
        try {
            RSString var2 = Unsorted.method1052(this.toLong());
            return null == var2 ? TextCore.aClass94_1760 : var2;
        } catch (RuntimeException var3) {
            throw ClientErrorException.clientError(var3, "na.Q(" + -17 + ')');
        }
    }

    public final int method1580(byte[] var2, int var3, int var5) {
        try {
            ArrayUtils.arraycopy(this.buffer, 0, var2, var3, var5);

            return var5;
        } catch (RuntimeException var7) {
            throw ClientErrorException.clientError(var7, "na.LA(" + true + ',' + (var2 != null ? "{...}" : "null") + ',' + var3 + ',' + 0 + ',' + var5 + ')');
        }
    }

}
