package com.fasterxml.jackson.databind.node;

import java.math.BigDecimal;
import java.math.BigInteger;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.util.RawValue;

/**
 * Base class that specifies methods for getting access to
 * Node instances (newly constructed, or shared, depending
 * on type), as well as basic implementation of the methods.
 * Designed to be sub-classed if extended functionality (additions
 * to behavior of node types, mostly) is needed.
 */
public class JsonNodeFactory
    implements java.io.Serializable, // since 2.1
        JsonNodeCreator // since 2.3
{
    // with 2.2
    private static final long serialVersionUID = 1L;

    /**
     * Constant that defines maximum {@code JsonPointer} element index we
     * use for inserts.
     */
    protected final static int MAX_ELEMENT_INDEX_FOR_INSERT = 9999;
    
    private final boolean _cfgBigDecimalExact;

    private static final JsonNodeFactory decimalsNormalized
        = new JsonNodeFactory(false);
    private static final JsonNodeFactory decimalsAsIs
        = new JsonNodeFactory(true);

    /**
     * Default singleton instance that construct "standard" node instances:
     * given that this class is stateless, a globally shared singleton
     * can be used.
     */
    public final static JsonNodeFactory instance = decimalsNormalized;

    /**
     * Main constructor
     *
     * <p>The only argument to this constructor is a boolean telling whether
     * {@link DecimalNode} instances must be built with exact representations of
     * {@link BigDecimal} instances.</p>
     *
     * <p>This has quite an influence since, for instance, a BigDecimal (and,
     * therefore, a DecimalNode) constructed from input string {@code "1.0"} and
     * another constructed with input string {@code "1.00"} <b>will not</b> be
     * equal, since their scale differs (1 in the first case, 2 in the second
     * case).</p>
     *
     * <p>Note that setting the argument to {@code true} does <i>not</i>
     * guarantee a strict inequality between JSON representations: input texts
     * {@code "0.1"} and {@code "1e-1"}, for instance, yield two equivalent
     * BigDecimal instances since they have the same scale (1).</p>
     *
     * <p>The no-arg constructor (and the default {@link #instance}) calls this
     * constructor with {@code false} as an argument.</p>
     *
     * @param bigDecimalExact see description
     *
     * @see BigDecimal
     */
    public JsonNodeFactory(boolean bigDecimalExact)
    {
        _cfgBigDecimalExact = bigDecimalExact;
    }

    /**
     * Default constructor
     *
     * <p>This calls {@link #JsonNodeFactory(boolean)} with {@code false}
     * as an argument.</p>
     */
    protected JsonNodeFactory()
    {
        this(false);
    }

    /**
     * Return a factory instance with the desired behavior for BigDecimals
     * <p>See {@link #JsonNodeFactory(boolean)} for a full description.</p>
     *
     * @param bigDecimalExact see description
     * @return a factory instance
     */
    public static JsonNodeFactory withExactBigDecimals(boolean bigDecimalExact)
    {
        return bigDecimalExact ? decimalsAsIs : decimalsNormalized;
    }

    /*
    /**********************************************************
    /* Metadata/config access
    /**********************************************************
     */

    /**
     * @since 2.14
     */
    public int getMaxElementIndexForInsert() {
        return MAX_ELEMENT_INDEX_FOR_INSERT;
    }

    /*
    /**********************************************************
    /* Factory methods for literal values
    /**********************************************************
     */

    /**
     * Factory method for getting an instance of JSON boolean value
     * (either literal 'true' or 'false')
     */
    @Override
    public BooleanNode booleanNode(boolean v) {
        return v ? BooleanNode.getTrue() : BooleanNode.getFalse();
    }

    /**
     * Factory method for getting an instance of JSON null node (which
     * represents literal null value)
     */
    @Override
    public NullNode nullNode() { return NullNode.getInstance(); }

    public JsonNode missingNode() {
        return MissingNode.getInstance();
    }
    
    /*
    /**********************************************************
    /* Factory methods for numeric values
    /**********************************************************
     */

    /**
     * Factory method for getting an instance of JSON numeric value
     * that expresses given 8-bit value
     */
    @Override
    public NumericNode numberNode(byte v) { return IntNode.valueOf(v); }

    /**
     * Alternate factory method that will handle wrapper value, which may
     * be null.
     * Due to possibility of null, returning type is not guaranteed to be
     * {@link NumericNode}, but just {@link ValueNode}.
     */
    @Override
    public ValueNode numberNode(Byte value) {
        return (value == null) ? nullNode() : IntNode.valueOf(value.intValue());
    }

    /**
     * Factory method for getting an instance of JSON numeric value
     * that expresses given 16-bit integer value
     */
    @Override
    public NumericNode numberNode(short v) { return ShortNode.valueOf(v); }

    /**
     * Alternate factory method that will handle wrapper value, which may
     * be null.
     * Due to possibility of null, returning type is not guaranteed to be
     * {@link NumericNode}, but just {@link ValueNode}.
     */
    @Override
    public ValueNode numberNode(Short value) {
        return (value == null) ? nullNode() : ShortNode.valueOf(value);
    }

    /**
     * Factory method for getting an instance of JSON numeric value
     * that expresses given 32-bit integer value
     */
    @Override
    public NumericNode numberNode(int v) { return IntNode.valueOf(v); }

    /**
     * Alternate factory method that will handle wrapper value, which may
     * be null.
     * Due to possibility of null, returning type is not guaranteed to be
     * {@link NumericNode}, but just {@link ValueNode}.
     */
    @Override
    public ValueNode numberNode(Integer value) {
        return (value == null) ? nullNode() : IntNode.valueOf(value.intValue());
    }

    /**
     * Factory method for getting an instance of JSON numeric value
     * that expresses given 64-bit integer value
     */
    @Override
    public NumericNode numberNode(long v) {
        return LongNode.valueOf(v);
    }

    /**
     * Alternate factory method that will handle wrapper value, which may be null.
     * Due to possibility of null, returning type is not guaranteed to be
     * {@link NumericNode}, but just {@link ValueNode}.
     */
    @Override
    public ValueNode numberNode(Long v) {
        if (v == null) {
            return nullNode();
        }
        return LongNode.valueOf(v.longValue());
    }

    /**
     * Factory method for getting an instance of JSON numeric value
     * that expresses given unlimited range integer value
     */
    @Override
    public ValueNode numberNode(BigInteger v) {
        if (v == null) {
            return nullNode();
        }
        return BigIntegerNode.valueOf(v);
    }

    /**
     * Factory method for getting an instance of JSON numeric value
     * that expresses given 32-bit floating point value
     */
    @Override
    public NumericNode numberNode(float v) { return FloatNode.valueOf((float) v); }

    /**
     * Alternate factory method that will handle wrapper value, which may
     * be null.
     * Due to possibility of null, returning type is not guaranteed to be
     * {@link NumericNode}, but just {@link ValueNode}.
     */
    @Override
    public ValueNode numberNode(Float value) {
        return (value == null) ? nullNode() : FloatNode.valueOf(value.floatValue());
    }

    /**
     * Factory method for getting an instance of JSON numeric value
     * that expresses given 64-bit floating point value
     */
    @Override
    public NumericNode numberNode(double v) { return DoubleNode.valueOf(v); }

    /**
     * Alternate factory method that will handle wrapper value, which may
     * be null.
     * Due to possibility of null, returning type is not guaranteed to be
     * {@link NumericNode}, but just {@link ValueNode}.
     */
    @Override
    public ValueNode numberNode(Double value) {
        return (value == null) ? nullNode() : DoubleNode.valueOf(value.doubleValue());
    }

    /**
     * Factory method for getting an instance of JSON numeric value
     * that expresses given unlimited precision floating point value
     *
     * <p>In the event that the factory has been built to normalize decimal
     * values, the BigDecimal argument will be stripped off its trailing zeroes,
     * using {@link BigDecimal#stripTrailingZeros()}.</p>
     *
     * @see #JsonNodeFactory(boolean)
     */
    @Override
    public ValueNode numberNode(BigDecimal v)
    {
        if (v == null) {
            return nullNode();
        }

        /*
         * If the user wants the exact representation of this big decimal,
         * return the value directly
         */
        if (_cfgBigDecimalExact)
            return DecimalNode.valueOf(v);

        /*
         * If the user has asked to strip trailing zeroes, however, there is
         * this bug to account for:
         *
         * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6480539
         *
         * In short: zeroes are never stripped out of 0! We therefore _have_
         * to compare with BigDecimal.ZERO...
         */
        // 24-Mar-2021, tatu: But isn't it more efficient to use "signum()"?
        //   Especially as we now have a special case to consider
        if (v.signum() == 0) {
            return DecimalNode.ZERO;
        }
        // 24-Mar-2021, tatu: [dataformats-binary#264] barfs on a specific value...
        //   Must skip normalization in that particular case. Alas, haven't found
        //   another way to check it instead of getting "Overflow", catching
        try {
            v = v.stripTrailingZeros();
        } catch (ArithmeticException e) {
            // If we can't, we can't...
            ;
        }
        return DecimalNode.valueOf(v);
    }

    /*
    /**********************************************************
    /* Factory methods for textual values
    /**********************************************************
     */

    /**
     * Factory method for constructing a node that represents JSON
     * String value
     */
    @Override
    public TextNode textNode(String text) { return TextNode.valueOf(text); }

    /**
     * Factory method for constructing a node that represents given
     * binary data, and will get serialized as equivalent base64-encoded
     * String value
     */
    @Override
    public BinaryNode binaryNode(byte[] data) { return BinaryNode.valueOf(data); }

    /**
     * Factory method for constructing a node that represents given
     * binary data, and will get serialized as equivalent base64-encoded
     * String value
     */
    @Override
    public BinaryNode binaryNode(byte[] data, int offset, int length) {
        return BinaryNode.valueOf(data, offset, length);
    }

    /*
    /**********************************************************
    /* Factory method for structured values
    /**********************************************************
     */

    /**
     * Factory method for constructing an empty JSON Array node
     */
    @Override
    public ArrayNode arrayNode() { return new ArrayNode(this); }

    /**
     * Factory method for constructing a JSON Array node with an initial capacity
     *
     * @since 2.8
     */
    @Override
    public ArrayNode arrayNode(int capacity) { return new ArrayNode(this, capacity); }

    /**
     * Factory method for constructing an empty JSON Object ("struct") node
     */
    @Override
    public ObjectNode objectNode() { return new ObjectNode(this); }

    /**
     * Factory method for constructing a wrapper for POJO
     * ("Plain Old Java Object") objects; these will get serialized
     * using data binding, usually as JSON Objects, but in some
     * cases as JSON Strings or other node types.
     */
    @Override
    public ValueNode pojoNode(Object pojo) { return new POJONode(pojo); }

    @Override
    public ValueNode rawValueNode(RawValue value) {
        return new POJONode(value);
    }

    /*
    /**********************************************************
    /* Helper methods
    /**********************************************************
     */

    protected boolean _inIntRange(long l)
    {
        int i = (int) l;
        long l2 = (long) i;
        return (l2 == l);
    }
}

