/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.nifi.attribute.expression.language.evaluation.util;

import java.util.regex.Pattern;

public class NumberParsing {

    public static enum ParseResultType {
        NOT_NUMBER, WHOLE_NUMBER, DECIMAL;
    }
    private static final String OptionalSign  = "[\\-\\+]?";

    private static final String Infinity = "(Infinity)";
    private static final String NotANumber = "(NaN)";

    // Base 10
    private static final String Base10Digits  = "\\d+";
    private static final String Base10Decimal  = "\\." + Base10Digits;
    private static final String OptionalBase10Decimal  = Base10Decimal + "?";

    private static final String Base10Exponent      = "[eE]" + OptionalSign + Base10Digits;
    private static final String OptionalBase10Exponent = "(" + Base10Exponent + ")?";

    // Hex
    private static final String HexIdentifier = "0[xX]";

    private static final String HexDigits     = "[0-9a-fA-F]+";
    private static final String HexDecimal = "\\." + HexDigits;
    private static final String OptionalHexDecimal = HexDecimal + "?";

    private static final String HexExponent      = "[pP]" + OptionalSign + Base10Digits;
    private static final String OptionalHexExponent = "(" + HexExponent + ")?";

    // Written according to the "Floating Point Literal" specification as outlined here: http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.2

    private static final String doubleRegex =
            OptionalSign +
            "(" +
                Infinity + "|" +
                NotANumber + "|" +
                "(" + Base10Digits + Base10Decimal + ")" + "|" +
                "(" + Base10Digits + OptionalBase10Decimal + Base10Exponent + ")" + "|" +
                "(" + Base10Decimal + OptionalBase10Exponent + ")" + "|" +
                // The case of a hex number with a decimal portion but no exponent is not supported by "parseDouble" and throws a NumberFormatException
                "(" + HexIdentifier + HexDigits + "\\.?" + HexExponent + ")" + "|" + // The case of a hex numeral with a "." but no decimal values is valid.
                "(" + HexIdentifier + HexDigits + OptionalHexDecimal + HexExponent + ")" + "|" +
                "(" + HexIdentifier + HexDecimal + OptionalHexExponent + ")" +
            ")";

    private static final String numberRegex =
            OptionalSign +
            "(" +
                Base10Digits + "|" +
                HexIdentifier + HexDigits +
            ")";

    private static final Pattern DOUBLE_PATTERN = Pattern.compile(doubleRegex);
    private static final Pattern NUMBER_PATTERN = Pattern.compile(numberRegex);

    private NumberParsing() { }

    public static ParseResultType parse(String input) {
        if (NUMBER_PATTERN.matcher(input).matches()) {
            return ParseResultType.WHOLE_NUMBER;
        } else if (DOUBLE_PATTERN.matcher(input).matches()) {
            return ParseResultType.DECIMAL;
        } else {
            return ParseResultType.NOT_NUMBER;
        }
    }
}
