
/**
 * Copyright © CENRA (2016)
 *
 * remi.clement@espaces-naturels.fr
 *
 * This software is a multi-platform and portable application based on
 * the scientifical toolbox to monitor wetlands. With this toolbox it’s
 * possible to calculate indicators «RhoMéO» to monitor quality,
 * fonctionalities and urban and agricultural pressures of wetlands.
 * Indicators are calculated using naturalist data entered and/or imported,
 * and geographic data downloaded from a geographic server.
 *
 * This software is governed by the CeCILL license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 *
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 *
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.fx.p08;

import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.data.reference.GeoReferential;
import fr.cenra.rhomeo.core.data.reference.GeoReferentials;
import java.util.ArrayList;
import java.util.StringJoiner;
import java.util.logging.Level;
import javafx.application.Platform;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import org.geotoolkit.gui.javafx.util.FXTableView;
import org.geotoolkit.gui.javafx.util.TaskManager;

/**
 * Table to download referential data for a site.
 *
 * @author Johann Sorel (Geomatys)
 */
public class FXGeoReferentialTable extends VBox {

    private static final Image CHECKED = new Image("/fr/cenra/rhomeo/fx/p08/u74.png");

    private final GeoReferentials geoRef;
    private final Site site;
    private final TableView<GeoReferential> table = new FXTableView<>();
    private final Button downloadButton = new Button("Télécharger");
    private final ProgressIndicator progress = new ProgressIndicator();
    private final String[] requiredTypes;

    final TableColumn<GeoReferential,Boolean> colAvailable;

    public FXGeoReferentialTable(GeoReferentials geoRef,Site site, String ... requieredTypes){
        this.geoRef = geoRef;
        this.site = site;
        this.requiredTypes = requieredTypes;

        progress.setProgress(-1);
        progress.setVisible(false);
        progress.setPrefSize(32, 32);
        progress.setMinSize(32, 32);

        downloadButton.getStyleClass().add("create-button");
        downloadButton.setStyle("-fx-text-fill: #FFFFFF;");
        downloadButton.managedProperty().bind(downloadButton.visibleProperty());
        downloadButton.setPadding(new Insets(5, 20, 5, 20));
        final HBox box = new HBox(progress,downloadButton);
        box.setPadding(new Insets(5, 5, 5, 5));
        box.setAlignment(Pos.CENTER_RIGHT);
        getChildren().addAll(table, box);

        //table structure
        final TableColumn<GeoReferential, Integer> colAnnees = new TableColumn<>("Années disponibles");
        colAvailable = new TableColumn<>("Années téléchargées sur le poste");
        final TableColumn<GeoReferential,Boolean> colDownload = new TableColumn<>("Années à télécharger");
        colAnnees.setCellValueFactory(param -> new SimpleIntegerProperty(param.getValue().getYear()).asObject());
        colAvailable.setCellValueFactory((TableColumn.CellDataFeatures<GeoReferential, Boolean> param) -> param.getValue().completed(requiredTypes));
        colAvailable.setCellFactory((TableColumn<GeoReferential, Boolean> param) -> new AvailableCell());
        colDownload.setCellValueFactory((TableColumn.CellDataFeatures<GeoReferential, Boolean> param) -> param.getValue().toDownloadProperty());
        colDownload.setCellFactory((TableColumn<GeoReferential, Boolean> param) -> new DownloadCell());

        table.getColumns().addAll(colAnnees, colAvailable, colDownload);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

        downloadButton.setOnAction(this::downloadRef);

        updateRefTable();
    }

    private void updateRefTable(){
        final Task<ObservableList<GeoReferential>> update = new Task<ObservableList<GeoReferential>>() {
            @Override
            protected ObservableList<GeoReferential> call() throws Exception {
                updateTitle("Mise à jour des références");
                final ObservableList<GeoReferential> referentials = geoRef.getReferentials(site);
                //remove referentials which don't have all required data
                for (int i = referentials.size() - 1; i >= 0; i--) {
                    if (!referentials.get(i).areAvailable(requiredTypes)) {
                        referentials.remove(i);
                    }
                }
                return referentials;
            }
        };

        update.setOnSucceeded(evt -> Platform.runLater(() -> {
            table.setItems(update.getValue());
        }));

        update.setOnFailed(evt -> Platform.runLater(() -> {
            final Throwable ex = evt.getSource().getException();
            table.setPlaceholder(new Label(ex.getLocalizedMessage()));
            table.setItems(FXCollections.emptyObservableList());
            RhomeoCore.LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
        }));

        progress.visibleProperty().bind(update.runningProperty());
        TaskManager.INSTANCE.submit(update);
    }

    private void downloadRef(ActionEvent event) {

        final Task<?> task = new Task() {
            @Override
            protected Object call() throws Exception {
                int selectedYears = 0;
                final ArrayList<Integer> failedYears = new ArrayList();
                for (GeoReferential ref : table.getItems()) {
                    if (ref.toDownloadProperty().get()) {
                        selectedYears++;
                        updateTitle("Téléchargement ".concat(String.valueOf(ref.getYear())));
                        try {
                            geoRef.downloadReferential(site, ref.getYear());
                        } catch (IllegalArgumentException e) {
                            failedYears.add(ref.getYear());
                        }
                    }
                }

                if (selectedYears < 1) {
                    Platform.runLater(() -> {
                        final Alert alert = new Alert(Alert.AlertType.WARNING, "Aucune année séléctionnée pour le téléchargement.", ButtonType.CLOSE);
                        alert.setResizable(true);
                        alert.show();
                    });

                } else if (!failedYears.isEmpty()) {
                    final String msg;
                    if (failedYears.size() < selectedYears) {
                        final StringJoiner builder = new StringJoiner(", ", "Aucun référentiel n'est disponible pour la zone demandée en ", ".");
                        failedYears.stream()
                                .map(year -> String.valueOf(year))
                                .forEach(year -> builder.add(year));
                        msg = builder.toString();
                    } else {
                        msg = "Aucun référentiel n'est disponible pour les années demandées";
                    }
                    Platform.runLater(() -> {
                        final Alert alert = new Alert(Alert.AlertType.WARNING, msg, ButtonType.CLOSE);
                        alert.setResizable(true);
                        alert.show();
                    });
                }
                return true;
            }
        };

        progress.visibleProperty().bind(task.runningProperty());
        downloadButton.visibleProperty().bind(task.runningProperty().not());
        // TODO : display message on illegal argument exception : no data available.
        final EventHandler e = (EventHandler) (Event event1) -> updateRefTable();

        task.setOnSucceeded(e);
        task.setOnFailed(evt -> {
            if (evt.getSource().getException() instanceof IllegalArgumentException) {
                final Alert alert = new Alert(Alert.AlertType.WARNING, "Aucun référentiel n'est disponible pour la zone demandée en ", ButtonType.CLOSE);
                alert.setResizable(true);
                alert.show();
            }
            e.handle(evt);
        });
        task.setOnCancelled(e);
        TaskManager.INSTANCE.submit(task);
    }

    public ObservableList<GeoReferential> getItems() {
        return table.getItems();
    }

    private static class AvailableCell extends TableCell<GeoReferential, Boolean>{

        public AvailableCell() {
        }

        @Override
        protected void updateItem(Boolean item, boolean empty) {
            super.updateItem(item, empty);
            if(Boolean.TRUE.equals(item)){
                setGraphic(new ImageView(CHECKED));
            }else{
                setText(null);
                setGraphic(null);
            }
        }
    }

    private class DownloadCell extends TableCell<GeoReferential, Boolean> {

        private final CheckBox checkBox = new CheckBox();

        private DownloadCell() {
            setGraphic(checkBox);

            checkBox.setOnAction((ActionEvent event) -> {
                TableRow row = tableRowProperty().getValue();
                if (row == null)
                    return;
                final GeoReferential r = (GeoReferential) row.getItem();
                if (r != null) {
                    r.toDownloadProperty().set(checkBox.isSelected());
                }
            });
        }

        @Override
        protected void updateItem(Boolean item, boolean empty) {
            super.updateItem(item, empty);

            checkBox.setVisible(!empty);
            checkBox.setSelected(Boolean.TRUE.equals(item));

            TableRow row = tableRowProperty().getValue();
            if (row == null)
                return;
            final GeoReferential r = (GeoReferential) row.getItem();
            if (r != null) {
                checkBox.setVisible(!colAvailable.getCellData(r));
            }
        }
    }
}
