package org.multibit.hd.ui.controllers;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.eventbus.Subscribe;
import org.bitcoinj.core.Coin;
import org.multibit.hd.core.config.Configurations;
import org.multibit.hd.core.dto.RAGStatus;
import org.multibit.hd.core.events.ExchangeRateChangedEvent;
import org.multibit.hd.core.events.SlowTransactionSeenEvent;
import org.multibit.hd.core.managers.WalletManager;
import org.multibit.hd.core.services.CoreServices;
import org.multibit.hd.core.utils.Coins;
import org.multibit.hd.ui.audio.Sounds;
import org.multibit.hd.ui.events.controller.AddAlertEvent;
import org.multibit.hd.ui.events.controller.RemoveAlertEvent;
import org.multibit.hd.ui.events.view.ViewEvents;
import org.multibit.hd.ui.models.AlertModel;
import java.math.BigDecimal;
import java.util.List;
/**
* <p>Controller for the header view </p>
* <ul>
* <li>Handles interaction between the model and the view</li>
* </ul>
*/
public class HeaderController extends AbstractController {
private final List<AlertModel> alertModels = Lists.newArrayList();
/**
* <p>Trigger a refresh of the header view to ensure alert panels are shown</p>
*/
public void refresh() {
if (!alertModels.isEmpty()) {
// The alert structure has changed so inform the view
ViewEvents.fireAlertAddedEvent(alertModels.get(0));
}
}
/**
* <p>Called when the exchange rate changes</p>
*
* @param event The exchange rate change event
*/
@Subscribe
public void onExchangeRateChangedEvent(final ExchangeRateChangedEvent event) {
// Build the exchange string
final Optional<Coin> availableCoin = WalletManager.INSTANCE.getCurrentWalletBalance();
final Optional<Coin> estimatedCoin = WalletManager.INSTANCE.getCurrentWalletBalanceWithUnconfirmed();
final BigDecimal localBalance;
if (event.getRate() != null) {
localBalance = Coins.toLocalAmount(availableCoin.or(Coin.ZERO), event.getRate());
} else {
localBalance = null;
}
// Post the event
ViewEvents.fireBalanceChangedEvent(
availableCoin.or(Coin.ZERO),
estimatedCoin.or(Coin.ZERO),
localBalance,
event.getRateProvider()
);
}
/**
* <p>Called when there are payments seen that may change the balance</p>
*
* @param event The slow transaction seen event
*/
@Subscribe
public void onSlowTransactionSeenEvent(SlowTransactionSeenEvent event) {
Optional<ExchangeRateChangedEvent> exchangeRateChangedEventOptional = CoreServices.getApplicationEventService().getLatestExchangeRateChangedEvent();
if (exchangeRateChangedEventOptional.isPresent()) {
onExchangeRateChangedEvent(exchangeRateChangedEventOptional.get());
} else {
// No exchange rate available but fire an event anyhow to force a balance change event
onExchangeRateChangedEvent(new ExchangeRateChangedEvent(null, null, Optional.<String>absent(), null));
}
}
/**
* <p>Handles the presentation of a new alert</p>
*
* @param event The balance change event
*/
@Subscribe
public synchronized void onAddAlertEvent(AddAlertEvent event) {
boolean ignoreRepeated = false;
// Check for repeated text
for (AlertModel alertModel : alertModels) {
if (alertModel.getLocalisedMessage().equals(event.getAlertModel().getLocalisedMessage())) {
// Ignore duplicated message
ignoreRepeated = true;
break;
}
}
if (!ignoreRepeated) {
// Add this to the list
alertModels.add(event.getAlertModel());
// Play a beep on the first alert for RED or AMBER
RAGStatus severity = event.getAlertModel().getSeverity();
if (RAGStatus.RED.equals(severity)
|| RAGStatus.AMBER.equals(severity)) {
Sounds.playBeep(Configurations.currentConfiguration.getSound());
}
// Adjust the models to reflect the new M of N values
updateRemaining();
}
// The alert structure has changed so inform the view
ViewEvents.fireAlertAddedEvent(alertModels.get(0));
}
/**
* <p>Handles the representation of the balance based on the current configuration</p>
*
* @param event The balance change event
*/
@Subscribe
public synchronized void onRemoveAlertEvent(RemoveAlertEvent event) {
if (!alertModels.isEmpty()) {
// Remove the topmost alert model
alertModels.remove(0);
// Adjust the models to reflect the new M of N values
updateRemaining();
// The alert structure has changed so inform the view
if (!alertModels.isEmpty()) {
ViewEvents.fireAlertAddedEvent(alertModels.get(0));
} else {
// Use an empty event to signal that the event should be hidden
ViewEvents.fireAlertRemovedEvent();
}
}
}
/**
* <p>Updates the "remaining" values for alert models</p>
*/
private void updateRemaining() {
Preconditions.checkNotNull(alertModels, "'alertModels' must be present");
// Update the "remaining" based on the position in the list
for (int i = 0; i < alertModels.size(); i++) {
AlertModel alertModel = alertModels.get(i);
Preconditions.checkNotNull(alertModel, "'alertModel' must be present");
alertModel.setRemaining(alertModels.size() - 1);
}
}
}