/* UtilLib - Copyright (c) 2015-2018 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.format;

import java.math.BigDecimal;
import java.text.ParseException;
import net.mapeadores.util.money.ExtendedCurrency;
import net.mapeadores.util.money.MoneyUtils;
import net.mapeadores.util.primitives.Decimal;
import net.mapeadores.util.text.StringUtils;


/**
 *
 * @author Vincent Calame
 */
public class SumEngine {

    private SumEngine() {

    }

    public static String compute(String s, short sumCastType) {
        switch (sumCastType) {
            case FormatConstants.DECIMAL_CAST:
            case FormatConstants.PERCENTAGE_CAST:
                return computeDecimal(s);
            case FormatConstants.INTEGER_CAST:
                return computeInteger(s);
            case FormatConstants.MONEY_CAST:
                return computeMoney(s);
            default:
                return s;
        }
    }

    private static String computeDecimal(String s) {
        String[] tokens = StringUtils.getTechnicalTokens(s, ';', false);
        int length = tokens.length;
        if (length == 0) {
            return "";
        }
        BigDecimal result = new BigDecimal(0);
        for (int i = 0; i < length; i++) {
            SignedToken token = checkToken(tokens[i]);
            try {
                BigDecimal bigDecimal = StringUtils.parseBigDecimal(token.token);
                if (!token.positive) {
                    bigDecimal = bigDecimal.negate();
                }
                result = result.add(bigDecimal);
            } catch (NumberFormatException nfe) {
                return "#Not number: " + tokens[i];
            }
        }
        return result.toString();
    }

    private static String computeMoney(String s) {
        String[] tokens = StringUtils.getTechnicalTokens(s, ';', false);
        int length = tokens.length;
        if (length == 0) {
            return "";
        }
        ExtendedCurrency currency = null;
        BigDecimal result = new BigDecimal(0);
        for (String token : tokens) {
            String[] money = MoneyUtils.splitMoney(token);
            if (money == null) {
                return "#Not money: " + token;
            }
            ExtendedCurrency currentCurrency;
            try {
                currentCurrency = ExtendedCurrency.parse(money[1]);
            } catch (ParseException pe) {
                return "#Invalid currency: " + money[1];
            }
            if (currency == null) {
                currency = currentCurrency;
            } else if (!(currentCurrency.equals(currency))) {
                return "#Different currencies: " + currency.getCurrencyCode() + " / " + currentCurrency.getCurrencyCode();
            }
            SignedToken signedToken = checkToken(money[0]);
            try {
                BigDecimal bigDecimal = StringUtils.parseBigDecimal(signedToken.token);
                if (!signedToken.positive) {
                    bigDecimal = bigDecimal.negate();
                }
                result = result.add(bigDecimal);
            } catch (NumberFormatException nfe) {
                return "#ERROR: " + token;
            }
        }
        return result.toString() + " " + currency.getCurrencyCode();
    }

    private static String computeInteger(String s) {
        String[] tokens = StringUtils.getTechnicalTokens(s, ';', false);
        int length = tokens.length;
        if (length == 0) {
            return "";
        }
        long result = 0;
        for (int i = 0; i < length; i++) {
            SignedToken token = checkToken(tokens[i]);
            try {
                Decimal decimal = StringUtils.parseDecimal(token.token);
                long l = decimal.getPartieEntiere();
                if (!token.positive) {
                    l = -l;
                }
                result = result + l;
            } catch (NumberFormatException nfe) {
                return "#Not number: " + tokens[i];
            }
        }
        return String.valueOf(result);
    }

    private static SignedToken checkToken(String token) {
        int length = token.length();
        int lastIndex = -1;
        boolean positive = true;
        for (int i = 0; i < length; i++) {
            char carac = token.charAt(i);
            if (carac == '-') {
                positive = !positive;
            } else if (carac != ' ') {
                lastIndex = i;
                break;
            }
        }
        if (lastIndex > 0) {
            token = token.substring(lastIndex).trim();
        }
        return new SignedToken(positive, token);
    }


    private static class SignedToken {

        private final boolean positive;
        private final String token;

        private SignedToken(boolean positive, String token) {
            this.positive = positive;
            this.token = token;
        }

    }

}
