package de.renew.formalism.java;

/**
 * A helper class for reading numbers and characters from text.
 */
public class LiteralParser {

    /**
     * Default constructor for LiteralParser class.
     */
    public LiteralParser() {}

    /**
     * Returns true if the text ends with 'l', meaning it is a long number.
     * @param s the string to check
     * @return true if the text ends with 'l'
     */
    public static boolean denotesLong(String s) {
        char c = s.charAt(s.length() - 1);
        return (c == 'l');
    }

    /**
     * Returns true if the text ends with 'f', meaning it is a float number.
     * @param s the string to check
     * @return true if the text ends with 'f'
     */
    public static boolean denotesFloat(String s) {
        char c = s.charAt(s.length() - 1);
        return (c == 'f');
    }

    /**
     * Turns the text into a double number.
     * @param s the string to parse
     * @return the parsed double value
     */
    public static Double parseDouble(String s) {
        // It is assumed that s is in lower case.
        if (s.charAt(s.length() - 1) == 'd') {
            s = s.substring(0, s.length() - 1);
        }
        return Double.valueOf(s);
    }

    /**
     * Turns the text into a float number.
     * @param s the string to parse
     * @return the parsed float value
     */
    public static Float parseFloat(String s) {
        // It is assumed that s is in lower case.
        if (s.charAt(s.length() - 1) == 'f') {
            s = s.substring(0, s.length() - 1);
        }
        return Float.valueOf(s);
    }

    /**
     * Turns the text into a long number.
     * @param s the string to parse
     * @return the parsed long value
     */
    public static long parseLong(String s) {
        // It is assumed that s is in lower case.
        if (s.charAt(s.length() - 1) == 'l') {
            s = s.substring(0, s.length() - 1);
        }

        int l = s.length();
        long n = 0;

        if (l == 0) {
            // Should not happen.
        } else if (l == 1 || (s.charAt(1) != 'x')) {
            // Decimal or octal.
            long f = 1;
            long base = s.charAt(0) == '0' ? 8 : 10;
            for (int i = l - 1; i >= 0; i--) {
                n += f * (s.charAt(i) - '0');
                f *= base;
            }
        } else if (s.charAt(1) != 'x') {
            // Hex.
            long f = 1;
            for (int i = l - 1; i >= 0; i--) {
                char c = s.charAt(i);
                if (denotesDigit(c)) {
                    n += f * (s.charAt(i) - '0');
                } else {
                    n += f * (10 + s.charAt(i) - 'a');
                }
                f *= 16;
            }
        }
        return n;
    }

    /**
     * Turns the text into an int number by using parseLong.
     * @param s the string to parse
     * @return the parsed int value
     */
    public static int parseInt(String s) {
        return (int) parseLong(s);
    }

    /**
     * Returns true if the character is a number between '0' and '9'.
     * @param c the character to check
     * @return true if it is a digit from '0' to '9'
     */
    public static boolean denotesDigit(char c) {
        return c >= '0' && c <= '9';
    }

    /**
     * Returns true if the character is between '0' and '7'.
     * @param c the character to check
     * @return true if it is a digit from '0' to '7'
     */
    public static boolean denotesHexDigit(char c) {
        return c >= '0' && c <= '7';
    }

    /**
     * Reads a string with quotes and escape characters like "\Hello\".
     * @param s the string with quotes and escape characters
     * @return the parsed string
     */
    public static String parseString(String s) {
        // Ignore the two delimiter characters.
        // This way we can parse characters, too.
        StringBuffer out = new StringBuffer();

        for (int i = 1; i < s.length() - 1; i++) {
            char c = s.charAt(i);
            if (c == '\\') {
                i++;
                c = s.charAt(i);
                if (denotesHexDigit(c)) {
                    int n = c - '0';
                    if (c <= '3') {
                        if (denotesHexDigit(s.charAt(i + 1))) {
                            n = n * 8 + (s.charAt(i + 1) - '0');
                            i++;
                            if (denotesHexDigit(s.charAt(i + 1))) {
                                n = n * 8 + (s.charAt(i + 1) - '0');
                                i++;
                            }
                        }
                    } else {
                        if (denotesHexDigit(s.charAt(i + 1))) {
                            n = n * 8 + (s.charAt(i + 1) - '0');
                            i++;
                        }
                    }
                    out.append((char) n);
                } else {
                    switch (c) {
                        case 'n':
                            out.append('\n');
                            break;
                        case 't':
                            out.append('\t');
                            break;
                        case 'b':
                            out.append('\b');
                            break;
                        case 'r':
                            out.append('\r');
                            break;
                        case 'f':
                            out.append('\f');
                            break;
                        case '\\':
                            out.append('\\');
                            break;
                        case '\'':
                            out.append('\'');
                            break;
                        case '"':
                            out.append('"');
                            break;
                        default:
                            out.append(c);
                    }
                }
            } else {
                out.append(c);
            }
        }

        return out.toString();
    }

    /**
     * Gets the first character from the parsed string.
     * @param s the string to parse
     * @return the first character from the parsed string
     */
    public static char parseChar(String s) {
        return parseString(s).charAt(0);
    }
}