/**
 * 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.data.Protocol;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.api.process.Indicator;
import fr.cenra.rhomeo.api.result.IndicatorValuesByYearItem;
import fr.cenra.rhomeo.core.Session;
import fr.cenra.rhomeo.core.data.DashboardResultsManager;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.image.ImageView;
import org.geotoolkit.gui.javafx.util.FXTableCell;
import org.geotoolkit.gui.javafx.util.FXTableView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.TableCell;
import fr.cenra.rhomeo.api.ui.DashboardMenuItem;
import fr.cenra.rhomeo.core.RhomeoRuntimeException;
import java.text.DecimalFormat;
import java.util.Optional;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javax.annotation.PostConstruct;

/**
 * Dashboard table presenting results for the selected site.
 *
 * @author Cédric Briançon (Geomatys)
 */
@Component("fr.cenra.rhomeo.fx.FXDashboardResultsTable")
@Scope("prototype")
public class FXDashboardResultsTable extends FXTableView<IndicatorValuesByYearItem> {

    @Autowired
    private Session session;

    @Autowired
    private DashboardResultsManager resultsManager;

    @Autowired
    private List<DashboardMenuItem> menuItems;

    private final ObjectProperty<Site> siteProperty = new SimpleObjectProperty<>();

    /**
     * Generate a table for the specified site.
     *
     * @param byCell true if the table has to use the selection by cell mode, false for generic by line selection.
     */
    public FXDashboardResultsTable(final boolean byCell) {
        setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

        if (byCell) {
            getSelectionModel().setCellSelectionEnabled(true);
            getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        }

        siteProperty.addListener(this::siteChanged);
    }


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

    /**
     * Initializes JavaFX components after spring components are done loading.
     */
    @PostConstruct
    public void postConstruct() {
        final ContextMenu menu = new ContextMenu();
        setContextMenu(menu);
        for (final DashboardMenuItem menuItem : menuItems) {
            final Optional<MenuItem> item = menuItem.createItem(this);
            if(item.isPresent()){
                menu.getItems().add(item.get());
            }
        }
    }

    private void siteChanged(final ObservableValue<? extends Site> obs, final Site oldSite, final Site newSite) {
        getColumns().clear();
        if (newSite == null) {
            setItems(null);
            setPlaceholder(new Label("Aucun site séléctionné"));
        } else {
            try {
                setItems(resultsManager.getDashboardResultsByYear(newSite));
            } catch (Exception ex) {
                throw new RhomeoRuntimeException(ex);
            }

            if (getItems().isEmpty()) {
                setPlaceholder(new Label("Aucun résultat attaché au site"));
            } else {
                final TableColumn<IndicatorValuesByYearItem, Integer> yearCol = new TableColumn<>("Année");
                yearCol.setCellValueFactory(value -> new SimpleIntegerProperty(value.getValue().getYear()).asObject());
                getColumns().add(yearCol);

                final List<Indicator> indicators = session.getIndicators();
                final List<Protocol> protocols = session.getProtocols();

                final List<IndicatorSiteColumn> columnsToAdd = new ArrayList<>();

                protocols.stream()
                        .filter(protocol -> protocol.isCompatible(newSite))
                        .sorted()
                        .forEach(protocol
                                -> indicators.stream()
                                .filter(indicator -> indicator.getProtocol() == protocol)
                                .sorted()
                                .forEach(indicator -> {
                                    final IndicatorSiteColumn siteColumn = new IndicatorSiteColumn(indicator);
                                    siteColumn.setCellValueFactory(this::getCellValueFactory);
                                    siteColumn.setCellFactory(this::getCellFactory);
                                    columnsToAdd.add(siteColumn);
                                })
                        );
                Collections.sort(columnsToAdd);
                getColumns().addAll(columnsToAdd);
            }
        }
    }

    private TableCell<IndicatorValuesByYearItem, Double> getCellFactory(final TableColumn<IndicatorValuesByYearItem, Double> input) {
        // hack #61 display integers for I04/I07
        final DecimalFormat format;
        if (input instanceof IndicatorSiteColumn &&
                (((IndicatorSiteColumn)input).getIndicator().getName().matches("I0(4|7)"))) {
            format = Rhomeo.SIMPLE_DECIMAL_FORMAT;
        } else {
            format = Rhomeo.SECOND_DECIMAL_FORMAT;
        }
        final FXTableCell cell = new FXTableCell<IndicatorValuesByYearItem, Double>() {

            @Override
            protected void updateItem(Double item, boolean empty) {
                super.updateItem(item, empty);
                setContentDisplay(ContentDisplay.RIGHT);
                if (empty || item == null || Double.isNaN(item)) {
                    setText(null);
                    setGraphic(null);
                } else {
                    final IndicatorValuesByYearItem itemLine = getItems().get(getIndex());
                    if (itemLine == null) {
                        setText(null);
                        setGraphic(null);
                    } else {
                        setText(format.format(item));
                        if (input instanceof IndicatorSiteColumn) {
                            final Boolean isPublished = itemLine.isPublished(((IndicatorSiteColumn) input).getIndicator().getName());
                            if (isPublished != null && !isPublished) {
                                setGraphic(new ImageView(Rhomeo.ICON_NOT_PUBLISHED));
                            }
                        }
                    }
                }
            }
        };
        cell.setAlignment(Pos.CENTER_RIGHT);
        return cell;
    }

    private ObservableValue<Double> getCellValueFactory(final TableColumn.CellDataFeatures<IndicatorValuesByYearItem, Double> feature) {
        final TableColumn<IndicatorValuesByYearItem, Double> col = feature.getTableColumn();
        final String indicName;
        if (col instanceof IndicatorSiteColumn) {
            indicName = ((IndicatorSiteColumn) col).getIndicator().getName();
        } else {
            indicName = null;
        }

        final IndicatorValuesByYearItem.PublishedValue valueForIndicator
                = indicName == null ? null : feature.getValue().getValueForIndicator(indicName);

        if (valueForIndicator == null || valueForIndicator.getValue() == null) {
            return new SimpleObjectProperty<>();
        }

        return new SimpleObjectProperty<>(valueForIndicator.getValue());
    }
}
