package foundation.omni;

import org.bitcoinj.core.Coin;

import javax.money.NumberValue;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;

/**
 * Numeric Value of Divisible Omni Token
 * An Omni Divisible token is typically represented to the user as a decimal amount and hence
 * you can have a fractional number of tokens. Internally it uses the same format as a divisible token.
 *
 */
public final class OmniDivisibleValue extends OmniValue {
    public static final BigDecimal   MIN_VALUE = new BigDecimal(0); // Minimum value of 1 in transactions?
    public static final BigDecimal   MAX_VALUE = new BigDecimal("92233720368.54775807");
    public static final long willetsPerCoin = Coin.COIN.value; // 10^8 (Omni equivalent of Satoshis
    private static final BigDecimal bdWilletsPerCoin = new BigDecimal(willetsPerCoin);

    /**
     * Create OmniDivisibleValue of the specified amount
     * @param amount Number of Omni tokens
     * @return
     */
    public static OmniDivisibleValue of(long amount) {
        return OmniDivisibleValue.of(BigDecimal.valueOf(amount));
    }

    /**
     * Create OmniDivisibleValue of the specified amount
     * @param amount Number of Omni tokens
     * @return
     */
    public static OmniDivisibleValue of(BigDecimal amount) {
        return new OmniDivisibleValue(amount.multiply(bdWilletsPerCoin).longValueExact());
    }

    /**
     * Create OmniDivisibleValue from willets/internal/wire format
     *
     * @param willets number of willets
     * @return OmniIndivisibleValue equal to number of willets
     */
    public static OmniDivisibleValue ofWillets(long willets) {
        return new OmniDivisibleValue(willets);
    }


    private OmniDivisibleValue(long value) {
        super(value);
    }

    @Override
    public PropertyType getPropertyType() {
        return PropertyType.DIVISIBLE;
    }

    @Override
    public Class<?> getNumberType() {
        return Long.class;
    }

    @Override
    public int getPrecision() {
        throw new UnsupportedOperationException("Operation not supported");
    }

    @Override
    public int getScale() {
        throw new UnsupportedOperationException("Operation not supported");
    }

    @Override
    public int intValueExact() {
        BigDecimal bd = bigDecimalValue();
        return bd.intValueExact();
    }

    @Override
    public long longValueExact() {
        BigDecimal bd = bigDecimalValue();
        return bd.longValueExact();
    }

    @Override
    public double doubleValueExact() {
        throw new UnsupportedOperationException("Operation not supported");
    }

    @Override
    public <T extends Number> T numberValue(Class<T> numberType) {
        throw new UnsupportedOperationException("Operation not supported");
    }

    @Override
    public NumberValue round(MathContext mathContext) {
        throw new UnsupportedOperationException("Operation not supported");
    }

    @Override
    public <T extends Number> T numberValueExact(Class<T> numberType) {
        throw new UnsupportedOperationException("Operation not supported");
    }

    @Override
    public long getAmountFractionNumerator() {
        throw new UnsupportedOperationException("Operation not supported");
    }

    @Override
    public long getAmountFractionDenominator() {
        throw new UnsupportedOperationException("Operation not supported");
    }

    @Override
    public byte byteValue() {
        BigDecimal bd = bigDecimalValue();
        return bd.byteValueExact();
    }

    @Override
    public short shortValue() {
        BigDecimal bd = bigDecimalValue();
        return bd.shortValueExact();
    }

    @Override
    public int intValue() {
        return intValueExact();
    }

    @Override
    public long longValue() {
        return longValueExact();
    }

    @Override
    public float floatValue() {
        throw new UnsupportedOperationException("Operation not supported");
    }

    @Override
    public double doubleValue() {
        return doubleValueExact();
    }

    @Override
    public String toString() {
        BigDecimal bd = bigDecimalValue();
        return bd.toString();
    }

    public BigDecimal bigDecimalValue() {
        BigDecimal willets = new BigDecimal(value);
        //TODO: Add rounding mode?
        return willets.divide(bdWilletsPerCoin);
    }
}

@@ -170,16 +170,47 @@
 Context;
+%0Aimport java.math.RoundingMode;
 %0A%0A/**%0A *
@@ -703,24 +703,197 @@
 54775807%22);%0A
+    public static final MathContext DEFAULT_CONTEXT = new MathContext(0, RoundingMode.UNNECESSARY);%0A    public static final int DEFAULT_SCALE = Coin.SMALLEST_UNIT_EXPONENT;%0A
     public s
@@ -975,16 +975,20 @@
  of 
-S
+s
 atoshi
-s
+ unit
 %0A   
@@ -1186,32 +1186,71 @@
 s%0A     * @return
+ OmniDivisibleValue representing amount
 %0A     */%0A    pub
@@ -1492,16 +1492,55 @@
  @return
+ OmniDivisibleValue representing amount
 %0A     */
@@ -2333,199 +2333,124 @@
     
-throw new UnsupportedOperationException(%22Operation not supported%22);%0A    %7D%0A%0A    @Override%0A    public int getScale() %7B%0A        throw new UnsupportedOperationException(%22Operation not supported%22)
+return DEFAULT_CONTEXT.getPrecision();%0A    %7D%0A%0A    @Override%0A    public int getScale() %7B%0A        return DEFAULT_SCALE
 ;%0A  
@@ -4503,43 +4503,8 @@
 e);%0A
-        //TODO: Add rounding mode?%0A
     
@@ -4545,16 +4545,57 @@
 sPerCoin
+, DEFAULT_SCALE, RoundingMode.UNNECESSARY
 );%0A    %7D

Use MathContext, Scale, Precision more correctly