package org.javamoney.moneta;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Objects;
import java.util.Optional;

import javax.money.CurrencyUnit;
import javax.money.MonetaryAmount;
import javax.money.Monetary;
import javax.money.format.AmountFormatContext;
import javax.money.format.AmountFormatContextBuilder;
import javax.money.format.MonetaryAmountFormat;
import javax.money.format.MonetaryParseException;

/**
 * class to format and parse a text string such as 'EUR 25.25' or vice versa.
 * @author Otavio Santana
 */
final class ToStringMonetaryAmountFormat implements MonetaryAmountFormat {

    private static final String CONTEXT_PREFIX = "ToString_";

    private final ToStringMonetaryAmountFormatStyle style;

    private final AmountFormatContext context;

    private ToStringMonetaryAmountFormat(ToStringMonetaryAmountFormatStyle style) {
        this.style = Objects.requireNonNull(style);
        context = AmountFormatContextBuilder.of(CONTEXT_PREFIX + style).build();
    }

    public static ToStringMonetaryAmountFormat of(
            ToStringMonetaryAmountFormatStyle style) {
        return new ToStringMonetaryAmountFormat(style);
    }

    @Override
    public String queryFrom(MonetaryAmount amount) {
        if (Objects.isNull(amount)) {
            return null;
        }
        return amount.toString();
    }

    @Override
    public AmountFormatContext getContext() {
        return context;
    }

    @Override
    public void print(Appendable appendable, MonetaryAmount amount)
            throws IOException {
        appendable.append(Optional.ofNullable(amount)
                .map(MonetaryAmount::toString).orElse("null"));

    }

    @Override
    public MonetaryAmount parse(CharSequence text)
            throws MonetaryParseException {
        ParserMonetaryAmount amount = parserMonetaryAmount(text);
        return style.to(amount);
    }

    private ParserMonetaryAmount parserMonetaryAmount(CharSequence text) {
        String[] array = Objects.requireNonNull(text).toString().split(" ");
        CurrencyUnit currencyUnit = Monetary.getCurrency(array[0]);
        BigDecimal number = new BigDecimal(array[1]);
        return new ParserMonetaryAmount(currencyUnit, number);
    }

    private class ParserMonetaryAmount {
        ParserMonetaryAmount(CurrencyUnit currencyUnit, BigDecimal number) {
            this.currencyUnit = currencyUnit;
            this.number = number;
        }

        private final CurrencyUnit currencyUnit;
        private final BigDecimal number;
    }

    /**
     * indicates with implementation will used to format or parser in
     * ToStringMonetaryAmountFormat
     */
    enum ToStringMonetaryAmountFormatStyle {
        MONEY {
            @Override
            MonetaryAmount to(ParserMonetaryAmount amount) {
                return Money.of(amount.number, amount.currencyUnit);
            }
        },
        FAST_MONEY {
            @Override
            MonetaryAmount to(ParserMonetaryAmount amount) {
                return FastMoney.of(amount.number, amount.currencyUnit);
            }
        },
        ROUNDED_MONEY {
            @Override
            MonetaryAmount to(ParserMonetaryAmount amount) {
                return RoundedMoney.of(amount.number, amount.currencyUnit);
            }
        };

        abstract MonetaryAmount to(ParserMonetaryAmount amount);
    }

}

@@ -514,16 +514,312 @@
  versa.%0A
+ * This class will used to toString and parse in all implementation on Moneta.%0A * %7B@link Money#toString()%7D%0A * %7B@link Money#parse(CharSequence)%7D%0A * %7B@link FastMoney#toString()%7D%0A * %7B@link FastMoney#parse(CharSequence)%7D%0A * %7B@link RoundedMoney#toString()%7D%0A * %7B@link RoundedMoney#parse(CharSequence)%7D%0A
  * @auth
@@ -840,16 +840,23 @@
 ana%0A */%0A
+public 
 final cl
@@ -2991,16 +2991,23 @@
  */%0A    
+public 
 enum ToS
@@ -3039,16 +3039,56 @@
 Style %7B%0A
+    %09/**%0A    %09 * %7B@link Money%7D%0A    %09 */%0A
         
@@ -3264,32 +3264,79 @@
    %7D%0A        %7D,%0A
+        /**%0A    %09 * %7B@link FastMoney%7D%0A    %09 */%0A
         FAST_MON
@@ -3502,32 +3502,32 @@
 ;%0A            %7D%0A
-
         %7D,%0A     
@@ -3513,32 +3513,82 @@
    %7D%0A        %7D,%0A
+        /**%0A    %09 * %7B@link RoundedMoney%7D%0A    %09 */%0A
         ROUNDED_

Add more documentation