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

import fr.cenra.rhomeo.Rhomeo;
import fr.cenra.rhomeo.api.InternationalResource;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.api.process.Indicator;
import fr.cenra.rhomeo.api.result.DashboardResultItem;
import fr.cenra.rhomeo.api.result.IndicatorValuesByYearItem;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.Session;
import fr.cenra.rhomeo.core.data.DashboardResultsManager;
import fr.cenra.rhomeo.core.result.ResultStorage;
import fr.cenra.rhomeo.core.util.ExportUtils;
import javafx.fxml.FXML;
import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.TablePosition;
import org.apache.commons.net.ftp.FTPClient;
import org.geotoolkit.gui.javafx.util.TaskManager;
import org.springframework.context.annotation.Scope;

/**
 * Dashboard panel displayed initially when opening the application.
 *
 * @author Cédric Briançon (Geomatys)
 */
@Component
@Scope("prototype")
public class FXDashboardPane extends BorderPane implements InternationalResource {
    /**
     * Body panel containing the data table and some action buttons.
     */
    @FXML private BorderPane uiDashboardBodyPane;

    @FXML
    private Button uiPublish;

    @FXML
    private Button uiExport;

    @Autowired
    private FXDashboardMenuPane menuPane;

    @Autowired
    private Session session;

    @Autowired
    private DashboardResultsManager resultsManager;

    @Autowired
    private ResultStorage resultStorage;

    FXDashboardResultsTable resultTable;

    public FXDashboardPane() {
        Rhomeo.loadFXML(this, FXDashboardPane.class);
    }

    /**
     * Initializes JavaFX components after spring components are done loading.
     */
    @PostConstruct
    private void postConstruct() {
        setTop(menuPane);

        resultTable = session.createPrototype(FXDashboardResultsTable.class, true);
        uiDashboardBodyPane.setCenter(resultTable);
        resultTable.siteProperty().bind(menuPane.siteProperty());

        uiDashboardBodyPane.visibleProperty().bind(menuPane.siteProperty().isNotNull().and(menuPane.newSiteProperty().not()));
        uiDashboardBodyPane.managedProperty().bind(uiDashboardBodyPane.visibleProperty());

        final ObservableList<TablePosition> selectedCells = resultTable.getSelectionModel().getSelectedCells();
        uiPublish.disableProperty().bind(Bindings.createBooleanBinding(() -> {
            if (selectedCells.size() != 1) {
                return true;
            }

            TablePosition<IndicatorValuesByYearItem, ?> pos = selectedCells.get(0);
            if (pos.getTableColumn() instanceof IndicatorSiteColumn) {
                final String indicName = ((IndicatorSiteColumn)pos.getTableColumn()).getIndicator().getName();
                final Boolean published = pos.getTableView().getItems().get(pos.getRow()).isPublished(indicName);
                return published == null? true : published;
            }

            return true;
        }, selectedCells));

        uiExport.disableProperty().bind(Bindings.createBooleanBinding(() -> {
            return (selectedCells.size() != 1 || !(selectedCells.get(0).getTableColumn() instanceof IndicatorSiteColumn));
        }, selectedCells));
    }

    public ObjectProperty<Site> siteProperty() {
        return menuPane.siteProperty();
    }

    @FXML
    public void publishData(final ActionEvent evt) {
        final ObservableList<TablePosition> selectedCells = resultTable.getSelectionModel().getSelectedCells();
        if (resultTable == null || selectedCells.size() != 1)
            return;

        final DashboardResultItem item = getSelectedItem();
        final IndicatorValuesByYearItem rowItem = resultTable.getItems().get(selectedCells.get(0).getRow());
        if (item == null || item.isPublished())
            return;

        TaskManager.INSTANCE.submit("Publication ".concat(item.getIndicator()), () -> {
            final FTPClient ftp = session.connectResultFTP();
            try {
                resultsManager.publishResults(menuPane.siteProperty().get(), Collections.singletonList(item), ftp);
            } finally {
                ftp.disconnect();
            }

            return true;
        }).setOnSucceeded((evt2) -> Platform.runLater(() -> {
            // Update publication state.
            rowItem.getValueForIndicator(item.getIndicator()).setPublished(true);
            resultTable.getItems().set(resultTable.getItems().indexOf(rowItem), rowItem);
        }));
    }

    @FXML
    public void exportData() {
        if (resultTable == null || resultTable.getSelectionModel().getSelectedCells().size() != 1)
            return;

        final DashboardResultItem item = getSelectedItem();
        if (item == null)
            return;

        final Indicator indic = ((IndicatorSiteColumn) resultTable.getSelectionModel().getSelectedCells().get(0).getTableColumn()).getIndicator();
        final String initialFileName = new StringBuilder(item.getSite()).append('_').append(item.getIndicator()).append('_').append(item.getYear()).append(".zip").toString();
        final FileChooser.ExtensionFilter zipFilter = new FileChooser.ExtensionFilter("Archive zip", "*.zip");
        final File outputFile = Rhomeo.askOutputFilePopup(initialFileName, this, zipFilter);
        if (outputFile == null) {
            return;
        }

        TaskManager.INSTANCE.submit("Export de résultats", () -> {
            try (final FileSystem fs = RhomeoCore.toZipFileSystem(outputFile.toPath())) {
                final Path root = fs.getRootDirectories().iterator().next();
                Optional<Path> opt = resultStorage.getMainResults(item);
                if (opt.isPresent())
                    Files.copy(opt.get(), root.resolve(opt.get().getFileName().toString()));
                opt = resultStorage.getAdditionalResults(item);
                if (opt.isPresent())
                    Files.copy(opt.get(), root.resolve(opt.get().getFileName().toString()));

                // Read metadata from file, and recover memory objects needed for
                // re-writing as text.
                final Map[] readMds = resultStorage.readMetadata(item)
                        .map(ctx -> ctx.toDataContext(session))
                        .map(ctx -> new Map[]{session.transform(ctx.getReferences()), ctx.getUserData()})
                        .orElse(new Map[]{Collections.EMPTY_MAP, Collections.EMPTY_MAP});

                ExportUtils.writeMetadataText(
                        root.resolve("metadata.txt"),
                        menuPane.siteProperty().get(),
                        indic.getProtocol(),
                        readMds[0],
                        Collections.singleton(indic),
                        readMds[1],
                        Collections.EMPTY_SET
                );
            }
            return true;
        });
    }

    /**
     *
     * @return A dashboard item reflecting data pointed by currently selected cell. Can be null.
     */
    private DashboardResultItem getSelectedItem() {
        if (resultTable == null) {
            return null;
        }
        final ObservableList<TablePosition> selectedCells = resultTable.getSelectionModel().getSelectedCells();
        if (resultTable.getSelectionModel().getSelectedCells().size() != 1) {
            return null;
        }

        TablePosition pos = selectedCells.get(0);
        if (!(pos.getTableColumn() instanceof IndicatorSiteColumn)) {
            return null;
        }

        final Site site = menuPane.siteProperty().get();
        final String siteName = site.getName();
        final IndicatorSiteColumn col = (IndicatorSiteColumn) pos.getTableColumn();
        IndicatorValuesByYearItem tmpItem = resultTable.getItems().get(pos.getRow());
        final Indicator indic = col.getIndicator();
        final String indicName = indic.getName();
        return new DashboardResultItem(
                tmpItem.getYear(), siteName, indicName, col.getCellData(tmpItem), tmpItem.isPublished(indicName)
        );
    }
}
