package wallettemplate;

import com.subgraph.orchid.TorClient;
import com.subgraph.orchid.TorInitializationListener;
import javafx.animation.TranslateTransition;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TabPane;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.util.Duration;
import javafx.util.StringConverter;
import network.thunder.core.communication.objects.messages.impl.message.gossip.objects.PubkeyIPObject;
import network.thunder.core.communication.objects.messages.impl.results.NullResultCommand;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.DownloadProgressTracker;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.utils.MonetaryFormat;
import org.fxmisc.easybind.EasyBind;
import wallettemplate.controls.NotificationBarPane;
import wallettemplate.utils.BitcoinUIModel;

import java.util.ResourceBundle;

/**
 * Gets created auto-magically by FXMLLoader via reflection. The widget fields are set to the GUI controls they're named
 * after. This class handles all the updates and event handling for the main UI.
 */
public class MainController {
    public HBox controlsBox;

    @FXML
    private ResourceBundle resources;

    @FXML
    private TabPane tabPane;

    @FXML
    private Font x1;

    @FXML
    private Button receiveMoneyBtn;

    @FXML
    private Button sendMoneyOutBtn;

    @FXML
    private ListView blockchainTxList;

    @FXML
    private Label balance;

    @FXML
    private Color x2;

    @FXML
    private Label balance1;

    @FXML
    private Color x22;

    @FXML
    private Font x11;

    @FXML
    private Button openChannel;

    @FXML
    private Button thunderReceiveMoneyBtn;

    @FXML
    private Button thunderSendMoneyOutBtn;

    @FXML
    private Button thunderRefreshBtn;

    @FXML
    private Button startListenButton;

    @FXML
    private Button fetchIPButton;

    @FXML
    private Button syncButton;

    @FXML
    private ListView nodesList;

    @FXML
    private ListView channelNetworkList;

    @FXML
    private ListView channelList;

    @FXML
    private ListView thunderTxListIncluded;

    @FXML
    private ListView thunderTxListSettled;

    @FXML
    private ListView thunderTxListOpen;

    @FXML
    private ListView thunderTxListRefunded;

    @FXML
    private Label thunderBalance;

    @FXML
    private Color x21;

    @FXML
    private Color x211;

    @FXML
    void fetchIPs (ActionEvent event) {
        Main.thunderContext.fetchNetworkIPs(new NullResultCommand());
    }

    @FXML
    void startListen (ActionEvent event) {
        Main.thunderContext.startListening(new NullResultCommand());
    }

    @FXML
    void syncThunder (ActionEvent event) {
        if (selectedNode != null) {
            Main.thunderContext.openChannel(selectedNode.pubkey, new NullResultCommand());
        }
    }

    @FXML
    void onReloadButton (ActionEvent event) {
        Main.thunderContext.getSyncData(result -> {
            System.out.println("Sync finished: " + result.getMessage());
        });
    }

    @FXML
    void refresh (ActionEvent event) {
        model.update();
    }

    private PubkeyIPObject selectedNode = null;

    private BitcoinUIModel model = new BitcoinUIModel();
    private NotificationBarPane.Item syncItem;

    // Called by FXMLLoader.
    public void initialize () {

    }

    public void onBitcoinSetup () {

        model.init();
        balance.textProperty().bind(EasyBind.map(model.balanceProperty(), Coin::toFriendlyString));
        // Don't let the user click send money when the wallet is empty.
        sendMoneyOutBtn.disableProperty().bind(model.balanceProperty().isEqualTo(Coin.ZERO));

        TorClient torClient = Main.bitcoin.peerGroup().getTorClient();
        if (torClient != null) {
            SimpleDoubleProperty torProgress = new SimpleDoubleProperty(-1);
            String torMsg = "Initialising Tor";
            syncItem = Main.instance.notificationBar.pushItem(torMsg, torProgress);
            torClient.addInitializationListener(new TorInitializationListener() {
                @Override
                public void initializationProgress (String message, int percent) {
                    Platform.runLater(() -> {
                        syncItem.label.set(torMsg + ": " + message);
                        torProgress.set(percent / 100.0);
                    });
                }

                @Override
                public void initializationCompleted () {
                    Platform.runLater(() -> {
                        syncItem.cancel();
                        showBitcoinSyncMessage();

                    });
                }
            });
        } else {

            showBitcoinSyncMessage();
        }
        model.syncProgressProperty().addListener(x -> {
            if (model.syncProgressProperty().get() >= 1.0) {
                readyToGoAnimation();

                if (syncItem != null) {
                    syncItem.cancel();
                    syncItem = null;
                }
            } else if (syncItem == null) {
                showBitcoinSyncMessage();
            }
        });

        Bindings.bindContent(blockchainTxList.getItems(), model.transactions);
        Bindings.bindContent(nodesList.getItems(), model.ipList);
        Bindings.bindContent(channelNetworkList.getItems(), model.channelNetworkList);
        Bindings.bindContent(channelList.getItems(), model.channelList);

        thunderBalance.textProperty().bind(EasyBind.map(model.balanceThunder, Coin::toFriendlyString));

        nodesList.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<PubkeyIPObject>() {
            @Override
            public void changed (ObservableValue<? extends PubkeyIPObject> observable, PubkeyIPObject oldValue, PubkeyIPObject newValue) {
                System.out.println("ListView selection changed from oldValue = "
                        + oldValue + " to newValue = " + newValue);
                selectedNode = newValue;
            }
        });

        blockchainTxList.setCellFactory(param1 -> new TextFieldListCell(new StringConverter<Transaction>() {
            @Override
            public String toString (Transaction tx) {
                Coin value = tx.getValue(Main.bitcoin.wallet());
                if (value.isPositive()) {
                    return tx.getConfidence().getDepthInBlocks() + " Incoming payment of " + MonetaryFormat.BTC.format(value);
                } else if (value.isNegative()) {
                    Address address = tx.getOutput(0).getAddressFromP2PKHScript(Main.params);
                    if (address == null) {
                        return tx.getConfidence().getDepthInBlocks() + " Outbound payment to ThunderChannel of " + value.toFriendlyString().substring(1);
                    }
                    return tx.getConfidence().getDepthInBlocks() + " Outbound payment to " + address;
                }
                return "Payment with id " + tx.getHash();
            }

            @Override
            public Transaction fromString (String string) {
                return null;
            }
        }));
        blockchainTxList.setCellFactory(param -> new TextFieldListCell(new StringConverter<Transaction>() {
            @Override
            public String toString (Transaction tx) {
                Coin value = tx.getValue(Main.bitcoin.wallet());
                if (value.isPositive()) {
                    return tx.getConfidence().getDepthInBlocks() + " Incoming payment of " + MonetaryFormat.BTC.format(value);
                } else if (value.isNegative()) {
                    Address address = tx.getOutput(0).getAddressFromP2PKHScript(Main.params);
                    if (address == null) {
                        return tx.getConfidence().getDepthInBlocks() + " Outbound payment to ThunderChannel of " + value.toFriendlyString().substring
                                (1);
                    }
                    return tx.getConfidence().getDepthInBlocks() + " Outbound payment to " + address;
                }
                return "Payment with id " + tx.getHash();
            }

            @Override
            public Transaction fromString (String string) {
                return null;
            }
        }));

    }

    private void showBitcoinSyncMessage () {
    }

    public void sendMoneyOut (ActionEvent event) {
        // Hide this UI and show the send money UI. This UI won't be clickable until the user dismisses send_money.
        Main.instance.overlayUI("send_money_blockchain.fxml");
    }

    public void settingsClicked (ActionEvent event) {
        Main.OverlayUI<WalletSettingsController> screen = Main.instance.overlayUI("wallet_settings.fxml");
        screen.controller.initialize(null);
    }

    public void restoreFromSeedAnimation () {
        // Buttons slide out ...
        TranslateTransition leave = new TranslateTransition(Duration.millis(1200), controlsBox);
        leave.setByY(80.0);
        leave.play();
    }

    public void readyToGoAnimation () {
        // Buttons slide in and clickable address appears simultaneously.
    }

    public DownloadProgressTracker progressBarUpdater () {
        return model.getDownloadProgressTracker();
    }

    public void receiveMoney (ActionEvent actionEvent) {
        Main.instance.overlayUI("receive_money_blockchain.fxml");
    }

    @FXML
    void thunderReceiveMoney (ActionEvent event) {
        Main.instance.overlayUI("receive_money_request.fxml");
    }

    @FXML
    void thunderSendMoneyOut (ActionEvent event) {
        Main.instance.overlayUI("send_money.fxml");
    }

    @FXML
    void openChannel (ActionEvent event) {
        if (selectedNode != null) {
            Main.thunderContext.openChannel(selectedNode.pubkey, new NullResultCommand());
        }
    }

    public void closeChannel (ActionEvent actionEvent) {
        //TODO
    }

    @FXML
    void thunderRefresh (ActionEvent event) {
    }
}