// 
// Decompiled by Procyon v0.6.0
// 

package ch.randelshofer.fastdoubleparser;

import ch.randelshofer.fastdoubleparser.chr.FormatCharSet;
import ch.randelshofer.fastdoubleparser.chr.CharSetOfNone;
import ch.randelshofer.fastdoubleparser.chr.CharTrie;
import ch.randelshofer.fastdoubleparser.chr.CharSet;
import ch.randelshofer.fastdoubleparser.chr.CharDigitSet;

abstract class AbstractConfigurableFloatingPointBitsFromCharSequence extends AbstractFloatValueParser
{
    private final CharDigitSet digitSet;
    private final CharSet minusSignChar;
    private final CharSet plusSignChar;
    private final CharSet decimalSeparator;
    private final CharSet groupingSeparator;
    private final CharTrie nanTrie;
    private final CharTrie infinityTrie;
    private final CharTrie exponentSeparatorTrie;
    private final CharSet formatChar;
    
    public AbstractConfigurableFloatingPointBitsFromCharSequence(final NumberFormatSymbols symbols, final boolean ignoreCase) {
        this.decimalSeparator = CharSet.copyOf(symbols.decimalSeparator(), ignoreCase);
        this.groupingSeparator = CharSet.copyOf(symbols.groupingSeparator(), ignoreCase);
        this.digitSet = CharDigitSet.copyOf(symbols.digits());
        this.minusSignChar = CharSet.copyOf(symbols.minusSign(), ignoreCase);
        this.exponentSeparatorTrie = CharTrie.copyOf(symbols.exponentSeparator(), ignoreCase);
        this.plusSignChar = CharSet.copyOf(symbols.plusSign(), ignoreCase);
        this.nanTrie = CharTrie.copyOf(symbols.nan(), ignoreCase);
        this.infinityTrie = CharTrie.copyOf(symbols.infinity(), ignoreCase);
        this.formatChar = (NumberFormatSymbolsInfo.containsFormatChars(symbols) ? new CharSetOfNone() : new FormatCharSet());
    }
    
    abstract long nan();
    
    abstract long negativeInfinity();
    
    public final long parseFloatingPointLiteral(final CharSequence str, final int offset, final int length) {
        final int endIndex = AbstractNumberParser.checkBounds(str.length(), offset, length);
        int index = this.skipFormatCharacters(str, offset, endIndex);
        if (index == endIndex) {
            return 9221120237041090561L;
        }
        char ch = str.charAt(index);
        boolean isNegative = this.minusSignChar.containsKey(ch);
        boolean isSignificandSigned = false;
        if (isNegative || this.plusSignChar.containsKey(ch)) {
            isSignificandSigned = true;
            if (++index == endIndex) {
                return 9221120237041090561L;
            }
        }
        long significand = 0L;
        final int significandStartIndex = index;
        int decimalSeparatorIndex = -1;
        int integerDigitCount = -1;
        int groupingCount = 0;
        boolean illegal = false;
        while (index < endIndex) {
            ch = str.charAt(index);
            final int digit = this.digitSet.toDigit(ch);
            if (digit < 10) {
                significand = 10L * significand + digit;
            }
            else if (this.decimalSeparator.containsKey(ch)) {
                illegal |= (integerDigitCount >= 0);
                decimalSeparatorIndex = index;
                integerDigitCount = index - significandStartIndex - groupingCount;
            }
            else {
                if (!this.groupingSeparator.containsKey(ch)) {
                    break;
                }
                illegal |= (decimalSeparatorIndex != -1);
                ++groupingCount;
            }
            ++index;
        }
        final int significandEndIndex = index;
        int digitCount;
        int exponent;
        if (integerDigitCount < 0) {
            digitCount = (integerDigitCount = significandEndIndex - significandStartIndex - groupingCount);
            decimalSeparatorIndex = significandEndIndex;
            exponent = 0;
        }
        else {
            digitCount = significandEndIndex - significandStartIndex - 1 - groupingCount;
            exponent = integerDigitCount - digitCount;
        }
        illegal |= (digitCount == 0 && significandEndIndex > significandStartIndex);
        if (!illegal && digitCount == 0) {
            return this.parseNaNOrInfinity(str, index, endIndex, isNegative, isSignificandSigned);
        }
        if (index < endIndex && !isSignificandSigned) {
            final boolean isNegative2 = this.minusSignChar.containsKey(ch);
            if (isNegative2 || this.plusSignChar.containsKey(ch)) {
                isNegative |= isNegative2;
                ++index;
            }
        }
        int expNumber = 0;
        boolean isExponentSigned = false;
        if (digitCount > 0) {
            final int count = this.exponentSeparatorTrie.match(str, index, endIndex);
            if (count > 0) {
                index += count;
                index = this.skipFormatCharacters(str, index, endIndex);
                ch = AbstractNumberParser.charAt(str, index, endIndex);
                boolean isExponentNegative = this.minusSignChar.containsKey(ch);
                if (isExponentNegative || this.plusSignChar.containsKey(ch)) {
                    ch = AbstractNumberParser.charAt(str, ++index, endIndex);
                    isExponentSigned = true;
                }
                int digit2 = this.digitSet.toDigit(ch);
                illegal |= (digit2 >= 10);
                do {
                    if (expNumber < 1024) {
                        expNumber = 10 * expNumber + digit2;
                    }
                    ch = AbstractNumberParser.charAt(str, ++index, endIndex);
                    digit2 = this.digitSet.toDigit(ch);
                } while (digit2 < 10);
                if (!isExponentSigned) {
                    isExponentNegative = this.minusSignChar.containsKey(ch);
                    if (isExponentNegative || this.plusSignChar.containsKey(ch)) {
                        ++index;
                    }
                }
                if (isExponentNegative) {
                    expNumber = -expNumber;
                }
                exponent += expNumber;
            }
        }
        if (illegal || index < endIndex) {
            return 9221120237041090561L;
        }
        boolean isSignificandTruncated;
        int exponentOfTruncatedSignificand;
        if (digitCount > 19) {
            int truncatedDigitCount = 0;
            significand = 0L;
            for (index = significandStartIndex; index < significandEndIndex; ++index) {
                ch = str.charAt(index);
                final int digit3 = this.digitSet.toDigit(ch);
                if (digit3 < 10) {
                    if (Long.compareUnsigned(significand, 1000000000000000000L) >= 0) {
                        break;
                    }
                    significand = 10L * significand + digit3;
                    ++truncatedDigitCount;
                }
            }
            isSignificandTruncated = (index < significandEndIndex);
            exponentOfTruncatedSignificand = integerDigitCount - truncatedDigitCount + expNumber;
        }
        else {
            isSignificandTruncated = false;
            exponentOfTruncatedSignificand = 0;
        }
        return this.valueOfFloatLiteral(str, significandStartIndex, decimalSeparatorIndex, decimalSeparatorIndex + 1, significandEndIndex, isNegative, significand, exponent, isSignificandTruncated, exponentOfTruncatedSignificand, expNumber, offset, endIndex);
    }
    
    private int skipFormatCharacters(final CharSequence str, int index, final int endIndex) {
        while (index < endIndex && this.formatChar.containsKey(str.charAt(index))) {
            ++index;
        }
        return index;
    }
    
    private long parseNaNOrInfinity(final CharSequence str, int index, final int endIndex, boolean isNegative, final boolean isSignificandSigned) {
        final int nanMatch = this.nanTrie.match(str, index, endIndex);
        if (nanMatch > 0) {
            index += nanMatch;
            if (index < endIndex && !isSignificandSigned) {
                final char ch = str.charAt(index);
                if (this.minusSignChar.containsKey(ch) || this.plusSignChar.containsKey(ch)) {
                    ++index;
                }
            }
            return (index == endIndex) ? this.nan() : 9221120237041090561L;
        }
        final int infinityMatch = this.infinityTrie.match(str, index, endIndex);
        if (infinityMatch > 0) {
            index += infinityMatch;
            if (index < endIndex && !isSignificandSigned) {
                final char ch2 = str.charAt(index);
                isNegative = this.minusSignChar.containsKey(ch2);
                if (isNegative || this.plusSignChar.containsKey(ch2)) {
                    ++index;
                }
            }
            if (index == endIndex) {
                return isNegative ? this.negativeInfinity() : this.positiveInfinity();
            }
        }
        return 9221120237041090561L;
    }
    
    abstract long positiveInfinity();
    
    abstract long valueOfFloatLiteral(final CharSequence p0, final int p1, final int p2, final int p3, final int p4, final boolean p5, final long p6, final int p7, final boolean p8, final int p9, final int p10, final int p11, final int p12);
    
    protected double slowPathToDouble(final CharSequence str, final int integerStartIndex, final int integerEndIndex, final int fractionStartIndex, final int fractionEndIndex, final boolean isSignificandNegative, final int exponentValue) {
        return SlowDoubleConversionPath.toDouble(str, this.digitSet, integerStartIndex, integerEndIndex, fractionStartIndex, fractionEndIndex, isSignificandNegative, exponentValue);
    }
}
