<commit_before>/*
 * This file is part of Bitsquare.
 *
 * Bitsquare is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * Bitsquare is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
 */

package io.bitsquare.gui.main.trade.offerbook;

import io.bitsquare.gui.CachedViewCB;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.OverlayManager;
import io.bitsquare.gui.components.InputTextField;
import io.bitsquare.gui.components.Popups;
import io.bitsquare.gui.main.trade.TradeNavigator;
import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.gui.util.validation.OptionalBtcValidator;
import io.bitsquare.gui.util.validation.OptionalFiatValidator;
import io.bitsquare.locale.BSResources;
import io.bitsquare.locale.Country;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer;

import java.net.URL;

import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

import javax.inject.Inject;

import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.transformation.SortedList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.util.Callback;

import org.controlsfx.control.action.AbstractAction;
import org.controlsfx.control.action.Action;
import org.controlsfx.dialog.Dialog;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static javafx.beans.binding.Bindings.createStringBinding;

/**
 * TODO: The advanced filters are not impl. yet
 * The restrictions handling is open from the concept and is only implemented for countries yet.
 */
public class OfferBookViewCB extends CachedViewCB<OfferBookPM> {
    private static final Logger log = LoggerFactory.getLogger(OfferBookViewCB.class);

    private final Navigation navigation;
    private final OverlayManager overlayManager;
    private final OptionalBtcValidator optionalBtcValidator;
    private final OptionalFiatValidator optionalFiatValidator;

    private boolean detailsVisible;
    private boolean advancedScreenInited;

    private ImageView expand;
    private ImageView collapse;

    @FXML CheckBox showOnlyMatchingCheckBox;
    @FXML Label amountBtcLabel, priceDescriptionLabel, priceFiatLabel, volumeDescriptionLabel,
            volumeFiatLabel, extendedButton1Label, extendedButton2Label, extendedCheckBoxLabel;
    @FXML InputTextField volumeTextField, amountTextField, priceTextField;
    @FXML TableView<OfferBookListItem> table;
    @FXML Button createOfferButton, showAdvancedSettingsButton, openCountryFilterButton, openPaymentMethodsFilterButton;
    @FXML TableColumn<OfferBookListItem, OfferBookListItem> priceColumn, amountColumn, volumeColumn,
            directionColumn, countryColumn, bankAccountTypeColumn;

    ///////////////////////////////////////////////////////////////////////////////////////////
    // Constructor
    ///////////////////////////////////////////////////////////////////////////////////////////

    @Inject
    OfferBookViewCB(OfferBookPM presentationModel,
                    Navigation navigation,
                    OverlayManager overlayManager,
                    OptionalBtcValidator optionalBtcValidator,
                    OptionalFiatValidator optionalFiatValidator) {
        super(presentationModel);

        this.navigation = navigation;
        this.overlayManager = overlayManager;
        this.optionalBtcValidator = optionalBtcValidator;
        this.optionalFiatValidator = optionalFiatValidator;
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // Lifecycle
    ///////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // init table
        setAmountColumnCellFactory();
        setPriceColumnCellFactory();
        setVolumeColumnCellFactory();
        setCountryColumnCellFactory();
        setBankAccountTypeColumnCellFactory();
        setDirectionColumnCellFactory();

        table.getSortOrder().add(priceColumn);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        Label placeholder = new Label("Currently there are no offers available");
        placeholder.setWrapText(true);
        table.setPlaceholder(placeholder);

        setupBindings();
        setupValidators();
        setupComparators();

        expand = ImageUtil.getImageViewById(ImageUtil.EXPAND);
        collapse = ImageUtil.getImageViewById(ImageUtil.COLLAPSE);
        showAdvancedSettingsButton.setGraphic(expand);

        // for irc demo
        showAdvancedSettingsButton.setVisible(false);
        showAdvancedSettingsButton.setManaged(false);

        super.initialize(url, rb);
    }

    @Override
    public void activate() {
        super.activate();

        // setOfferBookInfo has been called before
        SortedList<OfferBookListItem> offerList = presentationModel.getOfferList();
        table.setItems(offerList);
        offerList.comparatorProperty().bind(table.comparatorProperty());
        priceColumn.setSortType((presentationModel.getDirection() == Direction.BUY) ?
                TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING);
        table.sort();
    }

    @SuppressWarnings("EmptyMethod")
    @Override
    public void deactivate() {
        super.deactivate();

    }

    @SuppressWarnings("EmptyMethod")
    @Override
    public void terminate() {
        super.terminate();
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // Public methods
    ///////////////////////////////////////////////////////////////////////////////////////////

    public void enableCreateOfferButton() {
        createOfferButton.setDisable(false);
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // Setter
    ///////////////////////////////////////////////////////////////////////////////////////////

    public void setDirection(Direction direction) {
        presentationModel.setDirection(direction);
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // UI handlers
    ///////////////////////////////////////////////////////////////////////////////////////////

    @FXML
    void createOffer() {
        if (presentationModel.isRegistered()) {
            createOfferButton.setDisable(true);
            ((TradeNavigator) parent).createOffer(presentationModel.getAmountAsCoin(),
                    presentationModel.getPriceAsCoin());
        }
        else {
            openSetupScreen();
        }
    }

    @FXML
    void onToggleShowAdvancedSettings() {
        detailsVisible = !detailsVisible;
        if (detailsVisible) {
            showAdvancedSettingsButton.setText(BSResources.get("Hide extended filter options"));
            showAdvancedSettingsButton.setGraphic(collapse);
            showDetailsScreen();
        }
        else {
            showAdvancedSettingsButton.setText(BSResources.get("Show more filter options"));
            showAdvancedSettingsButton.setGraphic(expand);
            hideDetailsScreen();
        }
    }

    @FXML
    void onShowOnlyMatching() {
        Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
    }

    @FXML
    void onOpenCountryFilter() {
        Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
    }

    @FXML
    void onOpenPaymentMethodsFilter() {
        Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // Private methods
    ///////////////////////////////////////////////////////////////////////////////////////////

    private void openSetupScreen() {
        overlayManager.blurContent();
        List<Action> actions = new ArrayList<>();
        actions.add(new AbstractAction(BSResources.get("shared.ok")) {
            @Override
            public void handle(ActionEvent actionEvent) {
                getProperties().put("type", "OK");
                Dialog.Actions.OK.handle(actionEvent);
                overlayManager.removeBlurContent();
                navigation.setItemsForReturning(navigation.getCurrentItems());
                navigation.navigationTo(Navigation.Item.MAIN, Navigation.Item.ACCOUNT,
                        Navigation.Item.ACCOUNT_SETUP);
            }
        });
        Popups.openInfoPopup("You don't have setup a trading account.",
                "You need to setup your trading account before you can trade.",
                actions);
    }

    private void takeOffer(Offer offer) {

        if (presentationModel.isRegistered()) {
            if (offer.getDirection() == Direction.BUY) {
                ((TradeNavigator) parent).takeOffer(presentationModel.getAmountAsCoin(),
                        presentationModel.getPriceAsCoin(), offer);
            }
            else {
                Popups.openInfoPopup("Not implemented yet",
                        "At the moment you can only take offers in the Sell BTC screen.");
            }
        }
        else {
            openSetupScreen();
        }
    }

    private void openRestrictionsWarning(String restrictionsInfo) {
        overlayManager.blurContent();
        List<Action> actions = new ArrayList<>();
        actions.add(new AbstractAction(BSResources.get("shared.yes")) {
            @Override
            public void handle(ActionEvent actionEvent) {
                getProperties().put("type", "YES");
                Dialog.Actions.YES.handle(actionEvent);
                overlayManager.removeBlurContent();
            }
        });
        actions.add(new AbstractAction(BSResources.get("shared.no")) {
            @Override
            public void handle(ActionEvent actionEvent) {
                getProperties().put("type", "NO");
                Dialog.Actions.NO.handle(actionEvent);
                overlayManager.removeBlurContent();
            }
        });

        Action response = Popups.openConfirmPopup("Information",
                "You do not fulfill the requirements for that offer.",
                restrictionsInfo,
                actions);

        if (Popups.isYes(response))
            navigation.navigationTo(Navigation.Item.MAIN, Navigation.Item.ACCOUNT,
                    Navigation.Item.ACCOUNT_SETTINGS,
                    Navigation.Item.RESTRICTIONS);
        else
            table.getSelectionModel().clearSelection();
    }

    private void showDetailsScreen() {
        if (!advancedScreenInited) {
            advancedScreenInited = true;
        }

        toggleDetailsScreen(true);
    }

    private void hideDetailsScreen() {
        toggleDetailsScreen(false);
    }

    private void toggleDetailsScreen(boolean visible) {
        ((GridPane) root).setVgap(visible ? 5 : 0);

        extendedButton1Label.setVisible(visible);
        extendedButton1Label.setManaged(visible);

        extendedButton2Label.setVisible(visible);
        extendedButton2Label.setManaged(visible);

        extendedCheckBoxLabel.setVisible(visible);
        extendedCheckBoxLabel.setManaged(visible);

        openCountryFilterButton.setVisible(visible);
        openCountryFilterButton.setManaged(visible);

        openPaymentMethodsFilterButton.setVisible(visible);
        openPaymentMethodsFilterButton.setManaged(visible);

        showOnlyMatchingCheckBox.setVisible(visible);
        showOnlyMatchingCheckBox.setManaged(visible);
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // Setup
    ///////////////////////////////////////////////////////////////////////////////////////////

    private void setupBindings() {
        amountTextField.textProperty().bindBidirectional(presentationModel.amount);
        priceTextField.textProperty().bindBidirectional(presentationModel.price);
        volumeTextField.textProperty().bindBidirectional(presentationModel.volume);
        amountBtcLabel.textProperty().bind(presentationModel.btcCode);
        priceFiatLabel.textProperty().bind(presentationModel.fiatCode);
        volumeFiatLabel.textProperty().bind(presentationModel.fiatCode);
        priceDescriptionLabel.textProperty().bind(presentationModel.fiatCode);
        volumeDescriptionLabel.textProperty().bind(presentationModel.fiatCode);//Price per Bitcoin in EUR
        priceDescriptionLabel.textProperty().bind(createStringBinding(() ->
                        BSResources.get("Filter by price in {0}", presentationModel.fiatCode.get()),
                presentationModel.fiatCode));
        volumeDescriptionLabel.textProperty().bind(createStringBinding(() ->
                        BSResources.get("Filter by amount in {0}", presentationModel.fiatCode.get()),
                presentationModel.fiatCode));
        volumeTextField.promptTextProperty().bind(createStringBinding(() ->
                        BSResources.get("Amount in {0}", presentationModel.fiatCode.get()),
                presentationModel.fiatCode));
    }

    private void setupValidators() {
        amountTextField.setValidator(optionalBtcValidator);
        priceTextField.setValidator(optionalFiatValidator);
        volumeTextField.setValidator(optionalFiatValidator);
    }

    private void setupComparators() {
        priceColumn.setComparator((o1, o2) -> o1.getOffer().getPrice().compareTo(o2.getOffer().getPrice()));
        amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount()));
        volumeColumn.setComparator((o1, o2) ->
                o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume()));
        countryColumn.setComparator((o1, o2) -> o1.getOffer().getBankAccountCountry().getName().compareTo(o2.getOffer()
                .getBankAccountCountry().getName()));
        bankAccountTypeColumn.setComparator((o1, o2) -> o1.getOffer().getBankAccountType().compareTo(o2.getOffer()
                .getBankAccountType()));
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // CellFactories
    ///////////////////////////////////////////////////////////////////////////////////////////

    private void setAmountColumnCellFactory() {
        amountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
        amountColumn.setCellFactory(
                new Callback<TableColumn<OfferBookListItem, OfferBookListItem>, TableCell<OfferBookListItem,
                        OfferBookListItem>>() {
                    @Override<commit_msg>Reset offerbook filter values at screen change (#235)<commit_after>/*
 * This file is part of Bitsquare.
 *
 * Bitsquare is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * Bitsquare is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
 */

package io.bitsquare.gui.main.trade.offerbook;

import io.bitsquare.gui.CachedViewCB;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.OverlayManager;
import io.bitsquare.gui.components.InputTextField;
import io.bitsquare.gui.components.Popups;
import io.bitsquare.gui.main.trade.TradeNavigator;
import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.gui.util.validation.OptionalBtcValidator;
import io.bitsquare.gui.util.validation.OptionalFiatValidator;
import io.bitsquare.locale.BSResources;
import io.bitsquare.locale.Country;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer;

import java.net.URL;

import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

import javax.inject.Inject;

import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.transformation.SortedList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.util.Callback;

import org.controlsfx.control.action.AbstractAction;
import org.controlsfx.control.action.Action;
import org.controlsfx.dialog.Dialog;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static javafx.beans.binding.Bindings.createStringBinding;

/**
 * TODO: The advanced filters are not impl. yet
 * The restrictions handling is open from the concept and is only implemented for countries yet.
 */
public class OfferBookViewCB extends CachedViewCB<OfferBookPM> {
    private static final Logger log = LoggerFactory.getLogger(OfferBookViewCB.class);

    private final Navigation navigation;
    private final OverlayManager overlayManager;
    private final OptionalBtcValidator optionalBtcValidator;
    private final OptionalFiatValidator optionalFiatValidator;

    private boolean detailsVisible;
    private boolean advancedScreenInited;

    private ImageView expand;
    private ImageView collapse;

    @FXML CheckBox showOnlyMatchingCheckBox;
    @FXML Label amountBtcLabel, priceDescriptionLabel, priceFiatLabel, volumeDescriptionLabel,
            volumeFiatLabel, extendedButton1Label, extendedButton2Label, extendedCheckBoxLabel;
    @FXML InputTextField volumeTextField, amountTextField, priceTextField;
    @FXML TableView<OfferBookListItem> table;
    @FXML Button createOfferButton, showAdvancedSettingsButton, openCountryFilterButton, openPaymentMethodsFilterButton;
    @FXML TableColumn<OfferBookListItem, OfferBookListItem> priceColumn, amountColumn, volumeColumn,
            directionColumn, countryColumn, bankAccountTypeColumn;

    ///////////////////////////////////////////////////////////////////////////////////////////
    // Constructor
    ///////////////////////////////////////////////////////////////////////////////////////////

    @Inject
    OfferBookViewCB(OfferBookPM presentationModel,
                    Navigation navigation,
                    OverlayManager overlayManager,
                    OptionalBtcValidator optionalBtcValidator,
                    OptionalFiatValidator optionalFiatValidator) {
        super(presentationModel);

        this.navigation = navigation;
        this.overlayManager = overlayManager;
        this.optionalBtcValidator = optionalBtcValidator;
        this.optionalFiatValidator = optionalFiatValidator;
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // Lifecycle
    ///////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // init table
        setAmountColumnCellFactory();
        setPriceColumnCellFactory();
        setVolumeColumnCellFactory();
        setCountryColumnCellFactory();
        setBankAccountTypeColumnCellFactory();
        setDirectionColumnCellFactory();

        table.getSortOrder().add(priceColumn);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        Label placeholder = new Label("Currently there are no offers available");
        placeholder.setWrapText(true);
        table.setPlaceholder(placeholder);


        setupValidators();
        setupComparators();

        expand = ImageUtil.getImageViewById(ImageUtil.EXPAND);
        collapse = ImageUtil.getImageViewById(ImageUtil.COLLAPSE);
        showAdvancedSettingsButton.setGraphic(expand);

        // for irc demo
        showAdvancedSettingsButton.setVisible(false);
        showAdvancedSettingsButton.setManaged(false);

        super.initialize(url, rb);
    }

    @Override
    public void activate() {
        super.activate();

        amountTextField.setText("");
        priceTextField.setText("");
        volumeTextField.setText("");

        setupBindings();

        // setOfferBookInfo has been called before
        SortedList<OfferBookListItem> offerList = presentationModel.getOfferList();
        table.setItems(offerList);
        offerList.comparatorProperty().bind(table.comparatorProperty());
        priceColumn.setSortType((presentationModel.getDirection() == Direction.BUY) ?
                TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING);
        table.sort();
    }

    @SuppressWarnings("EmptyMethod")
    @Override
    public void deactivate() {
        super.deactivate();

        removeBindings();

    }

    @SuppressWarnings("EmptyMethod")
    @Override
    public void terminate() {
        super.terminate();
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // Public methods
    ///////////////////////////////////////////////////////////////////////////////////////////

    public void enableCreateOfferButton() {
        createOfferButton.setDisable(false);
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // Setter
    ///////////////////////////////////////////////////////////////////////////////////////////

    public void setDirection(Direction direction) {
        presentationModel.setDirection(direction);
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // UI handlers
    ///////////////////////////////////////////////////////////////////////////////////////////

    @FXML
    void createOffer() {
        if (presentationModel.isRegistered()) {
            createOfferButton.setDisable(true);
            ((TradeNavigator) parent).createOffer(presentationModel.getAmountAsCoin(),
                    presentationModel.getPriceAsCoin());
        }
        else {
            openSetupScreen();
        }
    }

    @FXML
    void onToggleShowAdvancedSettings() {
        detailsVisible = !detailsVisible;
        if (detailsVisible) {
            showAdvancedSettingsButton.setText(BSResources.get("Hide extended filter options"));
            showAdvancedSettingsButton.setGraphic(collapse);
            showDetailsScreen();
        }
        else {
            showAdvancedSettingsButton.setText(BSResources.get("Show more filter options"));
            showAdvancedSettingsButton.setGraphic(expand);
            hideDetailsScreen();
        }
    }

    @FXML
    void onShowOnlyMatching() {
        Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
    }

    @FXML
    void onOpenCountryFilter() {
        Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
    }

    @FXML
    void onOpenPaymentMethodsFilter() {
        Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // Private methods
    ///////////////////////////////////////////////////////////////////////////////////////////

    private void openSetupScreen() {
        overlayManager.blurContent();
        List<Action> actions = new ArrayList<>();
        actions.add(new AbstractAction(BSResources.get("shared.ok")) {
            @Override
            public void handle(ActionEvent actionEvent) {
                getProperties().put("type", "OK");
                Dialog.Actions.OK.handle(actionEvent);
                overlayManager.removeBlurContent();
                navigation.setItemsForReturning(navigation.getCurrentItems());
                navigation.navigationTo(Navigation.Item.MAIN, Navigation.Item.ACCOUNT,
                        Navigation.Item.ACCOUNT_SETUP);
            }
        });
        Popups.openInfoPopup("You don't have setup a trading account.",
                "You need to setup your trading account before you can trade.",
                actions);
    }

    private void takeOffer(Offer offer) {

        if (presentationModel.isRegistered()) {
            if (offer.getDirection() == Direction.BUY) {
                ((TradeNavigator) parent).takeOffer(presentationModel.getAmountAsCoin(),
                        presentationModel.getPriceAsCoin(), offer);
            }
            else {
                Popups.openInfoPopup("Not implemented yet",
                        "At the moment you can only take offers in the Sell BTC screen.");
            }
        }
        else {
            openSetupScreen();
        }
    }

    private void openRestrictionsWarning(String restrictionsInfo) {
        overlayManager.blurContent();
        List<Action> actions = new ArrayList<>();
        actions.add(new AbstractAction(BSResources.get("shared.yes")) {
            @Override
            public void handle(ActionEvent actionEvent) {
                getProperties().put("type", "YES");
                Dialog.Actions.YES.handle(actionEvent);
                overlayManager.removeBlurContent();
            }
        });
        actions.add(new AbstractAction(BSResources.get("shared.no")) {
            @Override
            public void handle(ActionEvent actionEvent) {
                getProperties().put("type", "NO");
                Dialog.Actions.NO.handle(actionEvent);
                overlayManager.removeBlurContent();
            }
        });

        Action response = Popups.openConfirmPopup("Information",
                "You do not fulfill the requirements for that offer.",
                restrictionsInfo,
                actions);

        if (Popups.isYes(response))
            navigation.navigationTo(Navigation.Item.MAIN, Navigation.Item.ACCOUNT,
                    Navigation.Item.ACCOUNT_SETTINGS,
                    Navigation.Item.RESTRICTIONS);
        else
            table.getSelectionModel().clearSelection();
    }

    private void showDetailsScreen() {
        if (!advancedScreenInited) {
            advancedScreenInited = true;
        }

        toggleDetailsScreen(true);
    }

    private void hideDetailsScreen() {
        toggleDetailsScreen(false);
    }

    private void toggleDetailsScreen(boolean visible) {
        ((GridPane) root).setVgap(visible ? 5 : 0);

        extendedButton1Label.setVisible(visible);
        extendedButton1Label.setManaged(visible);

        extendedButton2Label.setVisible(visible);
        extendedButton2Label.setManaged(visible);

        extendedCheckBoxLabel.setVisible(visible);
        extendedCheckBoxLabel.setManaged(visible);

        openCountryFilterButton.setVisible(visible);
        openCountryFilterButton.setManaged(visible);

        openPaymentMethodsFilterButton.setVisible(visible);
        openPaymentMethodsFilterButton.setManaged(visible);

        showOnlyMatchingCheckBox.setVisible(visible);
        showOnlyMatchingCheckBox.setManaged(visible);
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // Setup
    ///////////////////////////////////////////////////////////////////////////////////////////

    private void setupBindings() {
        amountTextField.textProperty().bindBidirectional(presentationModel.amount);
        priceTextField.textProperty().bindBidirectional(presentationModel.price);
        volumeTextField.textProperty().bindBidirectional(presentationModel.volume);
        amountBtcLabel.textProperty().bind(presentationModel.btcCode);
        priceFiatLabel.textProperty().bind(presentationModel.fiatCode);
        volumeFiatLabel.textProperty().bind(presentationModel.fiatCode);
        priceDescriptionLabel.textProperty().bind(presentationModel.fiatCode);
        volumeDescriptionLabel.textProperty().bind(presentationModel.fiatCode);//Price per Bitcoin in EUR
        priceDescriptionLabel.textProperty().bind(createStringBinding(() ->
                        BSResources.get("Filter by price in {0}", presentationModel.fiatCode.get()),
                presentationModel.fiatCode));
        volumeDescriptionLabel.textProperty().bind(createStringBinding(() ->
                        BSResources.get("Filter by amount in {0}", presentationModel.fiatCode.get()),
                presentationModel.fiatCode));
        volumeTextField.promptTextProperty().bind(createStringBinding(() ->
                        BSResources.get("Amount in {0}", presentationModel.fiatCode.get()),
                presentationModel.fiatCode));
    }

    private void removeBindings() {
        amountTextField.textProperty().unbind();
        priceTextField.textProperty().unbind();
        volumeTextField.textProperty().unbind();
        amountBtcLabel.textProperty().unbind();
        priceFiatLabel.textProperty().unbind();
        volumeFiatLabel.textProperty().unbind();
        priceDescriptionLabel.textProperty().unbind();
        volumeDescriptionLabel.textProperty().unbind();
        priceDescriptionLabel.textProperty().unbind();
        volumeDescriptionLabel.textProperty().unbind();
        volumeTextField.promptTextProperty().unbind();
    }

    private void setupValidators() {
        amountTextField.setValidator(optionalBtcValidator);
        priceTextField.setValidator(optionalFiatValidator);
        volumeTextField.setValidator(optionalFiatValidator);
    }

    private void setupComparators() {
        priceColumn.setComparator((o1, o2) -> o1.getOffer().getPrice().compareTo(o2.getOffer().getPrice()));
        amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount()));
        volumeColumn.setComparator((o1, o2) ->
                o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume()));
        countryColumn.setComparator((o1, o2) -> o1.getOffer().getBankAccountCountry().getName().compareTo(o2.getOffer()
                .getBankAccountCountry().getName()));
        bankAccountTypeColumn.setComparator((o1, o2) -> o1.getOffer().getBankAccountType().compareTo(o2.getOffer()
                .getBankAccountType()));
    }


    ///////////////////////////////////////////////////////////////////////////////////////////
    // CellFactories
    ///////////////////////////////////////////////////////////////////////////////////////////

    private void setAmountColumnCellFactory() {
        amountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
        amountColumn.setCellFactory(
                new Callback<TableColumn<OfferBookListItem, OfferBookListItem>, TableCell<OfferBookListItem,
                        OfferBookListItem>>() {
                    @Override
{'fraction_non_alphanumeric': 0.1775393283322255, 'fraction_numerical': 0.0022473332700281706, 'mean_word_length': 2.0727484925111845, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3635208', 'n_tokens_mistral': 7485, 'n_tokens_neox': 7185, 'n_words': 1398}