// 
// 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 AbstractConfigurableFloatingPointBitsFromCharArray extends AbstractFloatValueParser
{
    private final CharDigitSet digitSet;
    private final CharSet minusSign;
    private final CharSet plusSign;
    private final CharSet decimalSeparator;
    private final CharSet groupingSeparator;
    private final CharTrie nan;
    private final CharTrie infinity;
    private final CharTrie exponentSeparator;
    private final CharSet formatChar;
    
    public AbstractConfigurableFloatingPointBitsFromCharArray(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.minusSign = CharSet.copyOf(symbols.minusSign(), ignoreCase);
        this.exponentSeparator = CharTrie.copyOf(symbols.exponentSeparator(), ignoreCase);
        this.plusSign = CharSet.copyOf(symbols.plusSign(), ignoreCase);
        this.nan = CharTrie.copyOf(symbols.nan(), ignoreCase);
        this.infinity = 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 char[] 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[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 (!illegal && digitCount == 0) {
            return this.parseNaNOrInfinity(str, index, endIndex, isNegative, isSignificandSigned);
        }
        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;
                index = this.skipFormatCharacters(str, index, endIndex);
                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 || 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 int skipFormatCharacters(final char[] str, int index, final int endIndex) {
        while (index < endIndex && this.formatChar.containsKey(str[index])) {
            ++index;
        }
        return index;
    }
    
    private long parseNaNOrInfinity(final char[] 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 char 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 char 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 char[] 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 char[] 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);
    }
}
