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

import fr.cenra.rhomeo.api.process.Indicator;
import fr.cenra.rhomeo.api.process.Process;
import fr.cenra.rhomeo.api.result.Index;
import fr.cenra.rhomeo.core.data.reference.Taxref;
import fr.cenra.rhomeo.protocol.TaxonOperator;
import fr.cenra.rhomeo.protocol.validation.DeterminedSpecies;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.sis.util.ArgumentChecks;
/**
 *
 * @author Alexis Manin (Geomatys)
 */
public class SimpsonProcess extends Process {

    final I11 indic;
    final TaxonOperator<P07Statement> op;

    SimpsonProcess(final I11 indic, final TaxonOperator op) {
        super(op.getProcessContext());
        this.op = op;
        ArgumentChecks.ensureNonNull("Indicator", indic);
        this.indic = indic;
    }

    @Override
    public Indicator getIndicator() {
        return indic;
    }

    @Override
    protected Set<? extends Index> call() throws Exception {
        updateTitle("Calcul de Simpson");

        updateMessage("Analyse des données");

        final HashSet<Index> result = new HashSet<>();
        final List<P07Statement> data = op.getProcessData();

        P07Statement st = data.get(0);
        int year = st.getDate().getYear();
        int totalObs = 0;
        Taxref taxref;
        final Map<Integer, Integer> numberByCdRef = new HashMap<>();

        updateMessage("Calcul de l'année ".concat(String.valueOf(year)));

        for (int i = 0 ; i < data.size() ; i++) {
            updateProgress(i, data.size());
            st = data.get(i);
            if (st.getDate().getYear() > year) {
                result.add(computeSimpson(year, numberByCdRef, totalObs));
                // Reset variables for next year.
                year = st.getDate().getYear();
                totalObs = 0;
                numberByCdRef.clear();

                updateMessage("Calcul de l'année ".concat(String.valueOf(year)));
            }

            /* Update current year statistics with input statement. Only fully
             * determined species are counted.
             */
            taxref = op.getTaxref(st.getCd_nom());
            if (taxref != null && DeterminedSpecies.Validator.isDetermined(taxref)) {
                if (taxref.getRang().equalsIgnoreCase("sses")) {
                    taxref = op.getTaxref(taxref.getCd_taxsup());
                }
                final int nb = st.getNumber();
                numberByCdRef.merge(taxref.getCd_ref(), nb, Integer::sum);
                totalObs += nb;
            }
        }

        result.add(computeSimpson(year, numberByCdRef, totalObs));
        return result;
    }

    /**
     * Compute Simpson index for a specific year.
     * @param year The year to create an index for.
     * @param numberByCdRef Number of observed species, grouped by cd_ref.
     * @param totalObs Total number of observation for this year. Should be equal
     * to the sum of observed species. We could have computed it from first parameter,
     * but for optimisation purpose, we let user give it.
     * @return Simpson diversity Index computed from input variables.
     */
    private Index<Double> computeSimpson(final int year, final Map<Integer, Integer> numberByCdRef, final int totalObs) {
        if (totalObs < 2) { // not enough observations to compute index
            return indic.simpsonSpi.createIndex(0d, year);
        }

        // Compute simpson index
        final double denom = totalObs * (totalObs - 1);
        double simpson = 0;
        for (final Integer nbObs : numberByCdRef.values()) {
            if (nbObs > 1) {
                simpson += nbObs * (nbObs - 1) / denom;
            }
        }

        // Real index is not simpson, but inverse simpson.
        return indic.simpsonSpi.createIndex(1 - simpson, year);
    }
}
