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

package ch.randelshofer.fastdoubleparser;

import ch.randelshofer.fastdoubleparser.bte.ByteTrie;
import ch.randelshofer.fastdoubleparser.bte.ByteSet;
import ch.randelshofer.fastdoubleparser.bte.ByteDigitSet;

abstract class AbstractConfigurableFloatingPointBitsFromByteArrayAscii extends AbstractFloatValueParser
{
    private final ByteDigitSet digitSet;
    private final ByteSet minusSign;
    private final ByteSet plusSign;
    private final ByteSet decimalSeparator;
    private final ByteSet groupingSeparator;
    private final ByteTrie nan;
    private final ByteTrie infinity;
    private final ByteTrie exponentSeparator;
    
    public AbstractConfigurableFloatingPointBitsFromByteArrayAscii(final NumberFormatSymbols symbols, final boolean ignoreCase) {
        this.decimalSeparator = ByteSet.copyOf(symbols.decimalSeparator(), ignoreCase);
        this.groupingSeparator = ByteSet.copyOf(symbols.groupingSeparator(), ignoreCase);
        this.digitSet = ByteDigitSet.copyOf(symbols.digits());
        this.minusSign = ByteSet.copyOf(symbols.minusSign(), ignoreCase);
        this.exponentSeparator = ByteTrie.copyOf(symbols.exponentSeparator(), ignoreCase);
        this.plusSign = ByteSet.copyOf(symbols.plusSign(), ignoreCase);
        this.nan = ByteTrie.copyOf(symbols.nan(), ignoreCase);
        this.infinity = ByteTrie.copyOf(symbols.infinity(), ignoreCase);
    }
    
    abstract long nan();
    
    abstract long negativeInfinity();
    
    public final long parseFloatingPointLiteral(final byte[] str, final int offset, final int length) {
        final int endIndex = AbstractNumberParser.checkBounds(str.length, offset, length);
        int index = offset;
        byte ch = str[index];
        boolean isNegative = this.minusSign.containsKey(ch);
        boolean isSignificandSigned = false;
        if (isNegative || this.plusSign.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[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 (index < endIndex && !isSignificandSigned) {
            isNegative = this.minusSign.containsKey(ch);
            if (isNegative || this.plusSign.containsKey(ch)) {
                ++index;
            }
        }
        int expNumber = 0;
        boolean isExponentSigned = false;
        if (digitCount > 0) {
            final int count = this.exponentSeparator.match(str, index, endIndex);
            if (count > 0) {
                index += count;
                ch = AbstractNumberParser.charAt(str, index, endIndex);
                boolean isExponentNegative = this.minusSign.containsKey(ch);
                if (isExponentNegative || this.plusSign.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.minusSign.containsKey(ch);
                    if (isExponentNegative || this.plusSign.containsKey(ch)) {
                        ++index;
                    }
                }
                if (isExponentNegative) {
                    expNumber = -expNumber;
                }
                exponent += expNumber;
            }
        }
        if (!illegal && digitCount == 0) {
            return this.parseNaNOrInfinity(str, index, endIndex, isNegative, isSignificandSigned);
        }
        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[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 long parseNaNOrInfinity(final byte[] str, int index, final int endIndex, boolean isNegative, final boolean isSignificandSigned) {
        final int nanMatch = this.nan.match(str, index, endIndex);
        if (nanMatch > 0) {
            index += nanMatch;
            if (index < endIndex && !isSignificandSigned) {
                final byte ch = str[index];
                if (this.minusSign.containsKey(ch) || this.plusSign.containsKey(ch)) {
                    ++index;
                }
            }
            return (index == endIndex) ? this.nan() : 9221120237041090561L;
        }
        final int infinityMatch = this.infinity.match(str, index, endIndex);
        if (infinityMatch > 0) {
            index += infinityMatch;
            if (index < endIndex && !isSignificandSigned) {
                final byte ch2 = str[index];
                isNegative = this.minusSign.containsKey(ch2);
                if (isNegative || this.plusSign.containsKey(ch2)) {
                    ++index;
                }
            }
            if (index == endIndex) {
                return isNegative ? this.negativeInfinity() : this.positiveInfinity();
            }
        }
        return 9221120237041090561L;
    }
    
    abstract long positiveInfinity();
    
    abstract long valueOfFloatLiteral(final byte[] 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 byte[] 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);
    }
}
