package co.smartreceipts.android.model.impl;
import android.os.Parcel;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import co.smartreceipts.android.model.Price;
import co.smartreceipts.android.model.PriceCurrency;
import co.smartreceipts.android.model.factory.ExchangeRateBuilderFactory;
import co.smartreceipts.android.model.gson.ExchangeRate;
import co.smartreceipts.android.model.utils.ModelUtils;
/**
* Defines an immutable implementation of the {@link co.smartreceipts.android.model.Price} interface
* for a collection of other price objects.
*
* @author williambaumann
*/
public final class ImmutableNetPriceImpl extends AbstractPriceImpl {
private static final int ROUNDING_PRECISION = Price.ROUNDING_PRECISION + 2;
private final List<Price> mPrices;
private final Map<PriceCurrency, BigDecimal> mCurrencyToPriceMap;
private final BigDecimal mTotalPrice;
private final BigDecimal mPossiblyIncorrectTotalPrice;
private final PriceCurrency mCurrency;
private final ExchangeRate mExchangeRate;
private final boolean mAreAllExchangeRatesValid;
public ImmutableNetPriceImpl(@NonNull PriceCurrency baseCurrency, @NonNull List<Price> prices) {
mCurrency = baseCurrency;
mPrices = Collections.unmodifiableList(prices);
mCurrencyToPriceMap = new HashMap<>();
BigDecimal possiblyIncorrectTotalPrice = new BigDecimal(0);
BigDecimal totalPrice = new BigDecimal(0);
boolean areAllExchangeRatesValid = true;
for (final Price price : prices) {
final BigDecimal priceToAdd;
final PriceCurrency currencyForPriceToAdd;
if (price.getExchangeRate().supportsExchangeRateFor(baseCurrency)) {
priceToAdd = price.getPrice().multiply(price.getExchangeRate().getExchangeRate(baseCurrency));
totalPrice = totalPrice.add(priceToAdd);
currencyForPriceToAdd = baseCurrency;
} else {
// If not, let's just hope for the best with whatever we have to add
priceToAdd = price.getPrice();
currencyForPriceToAdd = price.getCurrency();
areAllExchangeRatesValid = false;
}
possiblyIncorrectTotalPrice = possiblyIncorrectTotalPrice.add(priceToAdd);
final BigDecimal priceForCurrency = mCurrencyToPriceMap.containsKey(currencyForPriceToAdd) ? mCurrencyToPriceMap.get(currencyForPriceToAdd).add(priceToAdd) : priceToAdd;
mCurrencyToPriceMap.put(currencyForPriceToAdd, priceForCurrency);
}
mTotalPrice = totalPrice.setScale(ROUNDING_PRECISION, RoundingMode.HALF_UP);
mPossiblyIncorrectTotalPrice = possiblyIncorrectTotalPrice.setScale(ROUNDING_PRECISION, RoundingMode.HALF_UP);
mAreAllExchangeRatesValid = areAllExchangeRatesValid;
mExchangeRate = new ExchangeRateBuilderFactory().setBaseCurrency(baseCurrency).build();
}
private ImmutableNetPriceImpl(@NonNull Parcel in) {
this(PriceCurrency.getInstance(in.readString()), restorePricesFromParcel(in));
}
private static List<Price> restorePricesFromParcel(Parcel in) {
final int size = in.readInt();
final List<Price> prices = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
final Price price = in.readParcelable(Price.class.getClassLoader());
prices.add(price);
}
return prices;
}
@Override
public float getPriceAsFloat() {
if (mAreAllExchangeRatesValid) {
return mTotalPrice.floatValue();
} else {
return mPossiblyIncorrectTotalPrice.floatValue();
}
}
@NonNull
@Override
public BigDecimal getPrice() {
if (mAreAllExchangeRatesValid) {
return mTotalPrice;
} else {
return mPossiblyIncorrectTotalPrice;
}
}
@NonNull
@Override
public String getDecimalFormattedPrice() {
if (mAreAllExchangeRatesValid) {
return ModelUtils.getDecimalFormattedValue(mTotalPrice);
} else {
return ModelUtils.getDecimalFormattedValue(mPossiblyIncorrectTotalPrice);
}
}
@NonNull
@Override
public String getCurrencyFormattedPrice() {
if (mAreAllExchangeRatesValid) {
return ModelUtils.getCurrencyFormattedValue(mTotalPrice, mCurrency);
} else {
final List<String> currencyStrings = new ArrayList<>();
for (PriceCurrency currency : mCurrencyToPriceMap.keySet()) {
currencyStrings.add(ModelUtils.getCurrencyFormattedValue(mCurrencyToPriceMap.get(currency), currency));
}
return TextUtils.join("; ", currencyStrings);
}
}
@NonNull
@Override
public String getCurrencyCodeFormattedPrice() {
if (mAreAllExchangeRatesValid) {
return ModelUtils.getCurrencyCodeFormattedValue(mTotalPrice, mCurrency);
} else {
final List<String> currencyStrings = new ArrayList<>();
for (PriceCurrency currency : mCurrencyToPriceMap.keySet()) {
currencyStrings.add(ModelUtils.getCurrencyCodeFormattedValue(mCurrencyToPriceMap.get(currency), currency));
}
return TextUtils.join("; ", currencyStrings);
}
}
@NonNull
@Override
public PriceCurrency getCurrency() {
return mCurrency;
}
@NonNull
@Override
public String getCurrencyCode() {
return mCurrency.getCurrencyCode();
}
@NonNull
@Override
public ExchangeRate getExchangeRate() {
return mExchangeRate;
}
public boolean areAllExchangeRatesValid() {
// TODO: Figure out how to expose this better
return mAreAllExchangeRatesValid;
}
@Override
public String toString() {
return getCurrencyFormattedPrice();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mCurrency.getCurrencyCode());
dest.writeInt(mPrices.size());
for (Price price : mPrices) {
dest.writeParcelable(price, 0);
}
}
public static final Creator<ImmutableNetPriceImpl> CREATOR = new Creator<ImmutableNetPriceImpl>() {
public ImmutableNetPriceImpl createFromParcel(Parcel source) {
return new ImmutableNetPriceImpl(source);
}
public ImmutableNetPriceImpl[] newArray(int size) {
return new ImmutableNetPriceImpl[size];
}
};
}