/**
 * 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.edition;

import fr.cenra.rhomeo.Rhomeo;
import fr.cenra.rhomeo.api.data.Dataset;
import fr.cenra.rhomeo.api.data.Statement;
import fr.cenra.rhomeo.api.process.Indicator;
import fr.cenra.rhomeo.api.process.ProcessContext;
import fr.cenra.rhomeo.api.result.Index;
import fr.cenra.rhomeo.core.Session;
import fr.cenra.rhomeo.core.WorkflowStep;
import static fr.cenra.rhomeo.fx.edition.FXDatasetPane.setProcessContext;
import fr.cenra.rhomeo.protocol.p04.I04;
import fr.cenra.rhomeo.protocol.p04.I07;
import fr.cenra.rhomeo.protocol.p04.P04Statement;
import java.beans.IntrospectionException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import org.apache.sis.util.ArgumentChecks;
import org.geotoolkit.gui.javafx.util.TaskManager;

/**
 *
 * @author Alexis Manin (Geomatys)
 */
public class P04Editor extends BorderPane {

    @FXML
    private Button uiFinalize;

    @FXML
    private BorderPane uiTableContainer;

    @FXML
    private Button uiDelete;

    private final Session session;

    private final StatementTable<P04Statement> table;
    DoubleBinding sizeBinding = null;

    public P04Editor(final Session session) throws IntrospectionException {
        ArgumentChecks.ensureNonNull("Session", session);
        this.session = session;
        Rhomeo.loadFXML(this);
        table = new StatementTable<>(P04Statement.class, "trackingPoint", "date", "remarks");
        table.sortColumns("year", "habitat", "humine", "acids", "pt", "cot");
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        uiTableContainer.setCenter(table);
        if (session.getDataset() != null) {
            table.setItems(session.getDataset().getItems());
            table.setFixedCellSize(32);
            sizeBinding = Bindings.createDoubleBinding(() -> {
                final int elementNb = table.getItems().size() < 1? 1 : table.getItems().size();
                return table.getFixedCellSize() * elementNb + 30; // Header size is not counted in element numbers, so we add a hard-coded size for it.
            }, table.getItems(), table.fixedCellSizeProperty());
            table.prefHeightProperty().bind(sizeBinding);
        }

        uiDelete.disableProperty().bind(table.getSelectionModel().selectedItemProperty().isNull());
        uiFinalize.disableProperty().bind(Bindings.isEmpty(table.getItems()));
    }

    @FXML
    void addStatement(ActionEvent event) throws ReflectiveOperationException {
        session.getDataset().getItems().add(session.getDataContext().getProtocol().getDataType().newInstance());
    }

    @FXML
    void deleteSelection(ActionEvent event) {
        // Defensive copy
        final Collection<Statement> selectedItems = new ArrayList<>(table.getSelectionModel().getSelectedItems());
        // Delete strict reference, because if the user has added multiple new
        // objects in the table, they could be equal.
        if (!selectedItems.isEmpty()) {
            session.getDataset().getItems().removeIf(item -> {
                for (final Statement selected : selectedItems) {
                    if (selected == item)
                        return true;
                }
                return false;
            });
        }
    }

    @FXML
    void validate(ActionEvent event) {
        final Validator validator = session.getBean(Validator.class);
        final Set<ConstraintViolation<Dataset>> result = validator.validate(session.getDataset());
        if (result != null && !result.isEmpty()) {
            final StringBuilder builder = new StringBuilder("La saisie n'est pas valide : ");
            int index;
            for (final ConstraintViolation cv : result) {
                builder.append(System.lineSeparator()).append(" - ");
                index = -1;
                final Object leafBean = cv.getLeafBean();
                for (int i = 0 ; i < table.getItems().size() ; i++) {
                    if (leafBean == table.getItems().get(i)) {
                        index = i;
                        break;
                    }
                }

                if (index >= 0) {
                    builder.append("Ligne ").append(index + 1).append(" : ");
                }
                builder.append(cv.getMessage());
            }
            Rhomeo.showAlert(Alert.AlertType.WARNING, builder.toString());
            return;
        }

        // Get P04 indicators
        Indicator i04 = null;
        Indicator i07 = null;
        for (final Indicator i : session.getIndicators()) {
            if (I04.NAME.equals(i.getName())) {
                i04 = i;
                if (i07 != null)
                    break;
            } else if (I07.NAME.equals(i.getName())) {
                i07 = i;
                if (i04 != null)
                    break;
            }
        }

        // Ask indicators to convert seized data into indices.
        final List<fr.cenra.rhomeo.api.process.Process> processes = new ArrayList<>();
        if (i04 != null) {
            i04.createProcesses(new ProcessContext(session.getDataset(), session.getDataContext())).forEach(p -> {
                TaskManager.INSTANCE.submit(p);
                processes.add(p);
            });
        }

        if (i07 != null) {
            i07.createProcesses(new ProcessContext(session.getDataset(), session.getDataContext())).forEach(p -> {
                TaskManager.INSTANCE.submit(p);
                processes.add(p);
            });
        }

        // Aggregate / sort results.
        final Task<Set<Index>> t = new TaskManager.MockTask("Traitement des résultats...", () -> {
            Set<Index> indices = new HashSet<>();
            for (final fr.cenra.rhomeo.api.process.Process p : processes) {
                try {
                    indices.addAll(p.get());
                } catch (Exception ex) {
                    Rhomeo.LOGGER.log(Level.WARNING, "A process failed", ex);
                }
            }

            return indices;
        });

        // Proceed to finalization.
        t.setOnSucceeded((evt) -> {
            session.setResults(t.getValue());
            session.requestWorkflowStep(WorkflowStep.FINALIZATION);
        });

        t.setOnFailed((evt) -> Rhomeo.showAlert(Alert.AlertType.WARNING, "Une erreur s'est produite. Impossible de procéder à la finalisation."));

        TaskManager.INSTANCE.submit(t);

        setProcessContext(session);
    }
}
