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

import fr.cenra.rhomeo.Rhomeo;
import fr.cenra.rhomeo.api.data.Dataset;
import fr.cenra.rhomeo.api.data.Warning;
import fr.cenra.rhomeo.core.Session;
import java.util.HashSet;
import java.util.ResourceBundle;
import java.util.Set;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Callback;
import javax.annotation.PostConstruct;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import org.geotoolkit.font.FontAwesomeIcons;
import org.geotoolkit.gui.javafx.util.TaskManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
 *
 * @author Alexis Manin (Geomatys)
 */
@Component
@Scope("prototype")
public class FXValidationPane extends StackPane {

    @Autowired
    private Session session;

    @Autowired
    private Validator validator;

    @FXML
    private Label uiStateIcon;

    @FXML
    private Label uiStateLabel;

    @FXML
    private Button uiValidate;

    @FXML
    private Hyperlink uiErrorContainer;

    @FXML
    private Hyperlink uiWarningContainer;

    @FXML
    private ProgressIndicator uiProgress;

    private final ListView<ConstraintViolation> uiErrorList;
    private final ListView<ConstraintViolation> uiWarningList;

    private final ObservableList errors;
    private final ObservableList warnings;

    private final BooleanBinding noError;

    private Stage errorStage;
    private Stage warningStage;

    public FXValidationPane() {
        super();
        Rhomeo.loadFXML(this, FXValidationPane.class);
        errors = FXCollections.observableArrayList();
        warnings = FXCollections.observableArrayList();
        uiErrorList = new ListView<>(errors);
        uiErrorList.setMaxHeight(200);
        final Insets insets = new Insets(4);
        uiErrorList.setPadding(insets);
        uiWarningList = new ListView<>(warnings);
        uiWarningList.setPadding(insets);
        uiWarningList.setMaxHeight(200);

        final Callback factory = (list) -> new ConstraintViolationListCell();
        uiErrorList.setCellFactory(factory);
        uiWarningList.setCellFactory(factory);

        // Update display on error detection
        noError = Bindings.isEmpty(errors);
        uiErrorContainer.visibleProperty().bind(noError.not());
        uiErrorContainer.managedProperty().bind(uiErrorContainer.visibleProperty());
        final BooleanBinding noWarning = Bindings.isEmpty(warnings);
        uiWarningContainer.visibleProperty().bind(noWarning.not());
        uiWarningContainer.managedProperty().bind(uiWarningContainer.visibleProperty());
        noError.addListener((obs) -> {
            if (session.getDataset().getItems().isEmpty()) {
                uiStateIcon.setText(null);
                uiStateLabel.setText(null);
            } else if (noError.get()) {
                uiStateIcon.setText(FontAwesomeIcons.ICON_CHECK);
                uiStateIcon.setStyle("-fx-font-family: FontAwesome; -fx-font-size: 24; -fx-text-fill:#9CB35B");
                uiStateLabel.setText(ResourceBundle.getBundle(FXValidationPane.class.getCanonicalName()).getString("dataset.valid"));
            } else {
                uiStateIcon.setText(FontAwesomeIcons.ICON_TIMES);
                uiStateIcon.setStyle("-fx-font-family: FontAwesome; -fx-font-size: 24; -fx-text-fill:#D85F70");
                uiStateLabel.setText(ResourceBundle.getBundle(FXValidationPane.class.getCanonicalName()).getString("dataset.invalid"));
            }
        });
    }

    @PostConstruct
    private void init() {
        uiValidate.disableProperty().bind(Bindings.isEmpty(session.getDataset().getItems()));
    }

    @FXML
    void validate(ActionEvent event) {
        TaskManager.INSTANCE.submit("Validation du lot de données", () -> {
            Rhomeo.fxRun(false, () -> {
                setDisable(true);
                uiProgress.setVisible(true);
            });
            try {
                Set<ConstraintViolation<Dataset>> violations = validator.validate(session.getDataset());

                final HashSet tmpErrors = new HashSet();
                final HashSet tmpWarnings = new HashSet();
                for (final ConstraintViolation violation : violations) {
                    if (findWarning(violation)) {
                        tmpWarnings.add(violation);
                    } else {
                        tmpErrors.add(violation);
                    }
                }

                Rhomeo.fxRun(false, () -> {
                    errors.setAll(tmpErrors);
                    warnings.setAll(tmpWarnings);
                    uiStateLabel.setVisible(true);
                    noError.invalidate();
                });
            } finally {
                Rhomeo.fxRun(false, () -> {
                    setDisable(false);
                    uiProgress.setVisible(false);
                });
            }
        });
    }

    private static boolean findWarning(final ConstraintViolation violation) {
        for (final Object p : violation.getConstraintDescriptor().getPayload()) {
            if (p == Warning.class) {
                return true;
            }
        }

        return false;
    }

    public ListView<ConstraintViolation> getErrorList() {
        return uiErrorList;
    }

    public ListView<ConstraintViolation> getWarningList() {
        return uiWarningList;
    }

    /**
     * Empty this panel content.
     */
    public void reset() {
        warnings.clear();
        errors.clear();
        noError.invalidate();
    }

    @FXML
    private void showWarnings(final ActionEvent e) {
        if (warningStage == null) {
            warningStage = createErrorStage("Avertissements", uiWarningList);
        }

        warningStage.show();
        warningStage.requestFocus();
    }

    @FXML
    private void showErrors(final ActionEvent e) {
        if (errorStage == null) {
            errorStage = createErrorStage("Erreurs", uiErrorList);
        }

        errorStage.show();
        errorStage.requestFocus();
    }

    private Stage createErrorStage(final String title, final ListView<ConstraintViolation> errors) {
        final Stage s = new Stage(StageStyle.UNIFIED);
        if (title != null)
            s.setTitle(title);
        s.initModality(Modality.NONE);
        if (getScene() != null)
            s.initOwner(getScene().getWindow());

        final Scene scene = new Scene(errors);
        s.setScene(scene);
        s.setAlwaysOnTop(false);
        s.sizeToScene();
        return s;
    }
}
