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

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.MultiPolygon;
import fr.cenra.rhomeo.api.data.Site;
import fr.cenra.rhomeo.api.process.Indicator;
import fr.cenra.rhomeo.api.process.ProcessContext;
import fr.cenra.rhomeo.api.result.Index;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import fr.cenra.rhomeo.api.process.Process;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.Session;
import fr.cenra.rhomeo.core.data.reference.GeoReferential;
import fr.cenra.rhomeo.core.data.reference.GeoReferentials;
import fr.cenra.rhomeo.core.util.GeometryUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.apache.sis.storage.DataStoreException;
import org.geotoolkit.data.FeatureCollection;
import org.geotoolkit.data.FeatureIterator;
import org.geotoolkit.feature.Feature;
import org.geotoolkit.feature.type.FeatureType;

/**
 *
 * @author Johann Sorel (Geomatys)
 */
public class I13Process extends Process {

    static final String SURFACE_KEY = "Cultures non impactantes";

    private final I13 indicator;

    @Autowired
    private GeoReferentials georefs;

    private final List<Integer> years = new ArrayList<>();
    private final List<Integer> noCount = new ArrayList<>();

    public I13Process(ProcessContext context, I13 indicator){
        super(context);
        this.indicator = indicator;
    }

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

    public List<Integer> getYears() {
        return years;
    }

    public List<Integer> getNoCount() {
        return noCount;
    }

    @Override
    protected Set<? extends Index> call() throws Exception {
        // HACK for validation
        try {
            Session.getInstance().getDataContext().getUserData().put(SURFACE_KEY, noCount);
        } catch (Exception e) {
            RhomeoCore.LOGGER.log(Level.FINE, "Cannot apply hack", e);
        }

        final Set<Index> indexes = new HashSet<>();
        for(Integer year : years){
            process(year,indexes);
        }

        return indexes;
    }

    private void process(int year, Set<Index> indexes) throws IOException, DataStoreException{

        final Site site = ctx.getDataCtx().getSite();
        final MultiPolygon siteGeom = ctx.getDataCtx().getSite().getGeometry();
        final Geometry siteGeomBuffer = GeometryUtils.computeBuffer(siteGeom);
        final double siteGeomBufferArea = siteGeomBuffer.getArea();
        final GeoReferential georef = georefs.getLocalReferential(site, year);

        /* =====================================================================
        bv AS (
           SELECT
                   sites.id_site,
           st_union(carthage_zh.geom) AS geom_bv
           FROM referentiels_geo.carthage_zh,expertise.sites
           WHERE st_intersects(sites.geom, carthage_zh.geom)
           GROUP BY sites.id_site
           ),
        */
        final Geometry geom_bv = georef
                .unionIntersecting(GeoReferentials.TYPE_ZONE_HYDRO, siteGeom)
                .orElseThrow(() -> new IllegalStateException("Aucune zone hydrographique valide."));

        final double area_bv = geom_bv.getArea();

        /* =====================================================================
        t25 AS (
            SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '1'::text AS id_cult,
                t1_1.surf_1 AS surf_cult,
                t1_1.surf_1 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_1 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '2'::text AS id_cult,
                t1_1.surf_2 AS surf_cult,
                t1_1.surf_2 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_2 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '3'::text AS id_cult,
                t1_1.surf_3 AS surf_cult,
                t1_1.surf_3 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_3 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '4'::text AS id_cult,
                t1_1.surf_4 AS surf_cult,
                t1_1.surf_4 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_4 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '5'::text AS id_cult,
                t1_1.surf_5 AS surf_cult,
                t1_1.surf_5 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_5 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '6'::text AS id_cult,
                t1_1.surf_6 AS surf_cult,
                t1_1.surf_6 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_6 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '7'::text AS id_cult,
                t1_1.surf_7 AS surf_cult,
                t1_1.surf_7 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_7 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '8'::text AS id_cult,
                t1_1.surf_8 AS surf_cult,
                t1_1.surf_8 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_8 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '9'::text AS id_cult,
                t1_1.surf_9 AS surf_cult,
                t1_1.surf_9 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_9 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '10'::text AS id_cult,
                t1_1.surf_10 AS surf_cult,
                t1_1.surf_10 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_10 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '11'::text AS id_cult,
                t1_1.surf_11 AS surf_cult,
                t1_1.surf_11 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_11 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '13'::text AS id_cult,
                t1_1.surf_13 AS surf_cult,
                t1_1.surf_13 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_13 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '15'::text AS id_cult,
                t1_1.surf_15 AS surf_cult,
                t1_1.surf_15 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_15 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '16'::text AS id_cult,
                t1_1.surf_16 AS surf_cult,
                t1_1.surf_16 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_16 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '20'::text AS id_cult,
                t1_1.surf_20 AS surf_cult,
                t1_1.surf_20 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_20 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '21'::text AS id_cult,
                t1_1.surf_21 AS surf_cult,
                t1_1.surf_21 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_21 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '22'::text AS id_cult,
                t1_1.surf_22 AS surf_cult,
                t1_1.surf_22 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_22 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '23'::text AS id_cult,
                t1_1.surf_23 AS surf_cult,
                t1_1.surf_23 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_23 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '24'::text AS id_cult,
                t1_1.surf_24 AS surf_cult,
                t1_1.surf_24 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_24 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '25'::text AS id_cult,
                t1_1.surf_25 AS surf_cult,
                t1_1.surf_25 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_25 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '27'::text AS id_cult,
                t1_1.surf_27 AS surf_cult,
                t1_1.surf_27 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_27 IS NOT NULL
            UNION
             SELECT t1_1.id_ilot,
                t1_1.surf_ilot,
                '28'::text AS id_cult,
                t1_1.surf_28 AS surf_cult,
                t1_1.surf_28 / t1_1.surf_ilot AS pourc
               FROM referentiels_geo.rpg01_2014 t1_1
              WHERE t1_1.surf_28 IS NOT NULL
        )
        */

        /* =====================================================================
        WITH t25sum AS (
            SELECT
                    t25.id_ilot,
                sum(t25.pourc) AS pourc_impact
            FROM t25
            GROUP BY t25.id_ilot
        )
        */

        final FeatureCollection rpgCol = georef.getFeatureCollection(GeoReferentials.TYPE_RPG, null);
        final FeatureType featureType = rpgCol.getFeatureType();
        final Map<Integer, Float> pourc = new HashMap<>();
        try(FeatureIterator ite = rpgCol.iterator()) {
            while(ite.hasNext()) {
                final Feature feature = ite.next();
                final int id_ilot = ((Number) feature.getProperty("id_ilot").getValue()).intValue();
                final Number surf_ilot = (Number) feature.getProperty("surf_ilot").getValue();
                float p = 0;
                for(int i=1;i<29;i++) {
                    if(noCount.contains(i) || featureType.getDescriptor("surf_"+i)==null) continue;
                    final Object surfObj = feature.getProperty("surf_"+i).getValue();
                    float surf;
                    if(surfObj instanceof Number && (surf = ((Number)surfObj).floatValue()) > 0.0) {
                        p += surf / surf_ilot.floatValue();
                    }
                }

                if (p > 0) {
                    pourc.merge(id_ilot, p, (oldVal, newVal) -> oldVal + newVal);
                }
            }
        }

        /* =====================================================================
        ilots_sites AS (
         SELECT --t1.id_ilot,
            --t1.surf_ilot,
            t2.id_site,
            st_area(t2.geom_sitebuffer) AS surf_site,
            st_area(st_intersection((st_dump(st_union(t1.geom))).geom::geometry(Polygon,2154), t2.geom_sitebuffer)) AS surf_inters_site,
            st_area(st_intersection((st_dump(st_union(t1.geom))).geom::geometry(Polygon,2154), t2.geom_sitebuffer)) * t25sum.pourc_impact AS surf_impact_site,
            st_area(bv.geom_bv) AS surf_bv,
            st_area(st_intersection((st_dump(st_union(t1.geom))).geom::geometry(Polygon,2154), bv.geom_bv)) AS surf_inters_bv,
            st_area(st_intersection((st_dump(st_union(t1.geom))).geom::geometry(Polygon,2154), bv.geom_bv)) * t25sum.pourc_impact AS surf_impact_bv
           FROM referentiels_geo.rpg01_2014 t1
             JOIN t25sum ON t1.id_ilot = t25sum.id_ilot,
            expertise.sites t2,
            bv
          WHERE st_intersects(t1.geom, bv.geom_bv) AND bv.id_site = t2.id_site
          GROUP BY t2.id_site, t2.geom_sitebuffer, bv.geom_bv, t25sum.pourc_impact
        )
        */

        /* =====================================================================
        CREATE OR REPLACE VIEW indicateurs.indic_pression_agri AS
            SELECT ilots_sites.id_site,
               round((sum(ilots_sites.surf_impact_site) * 100::double precision / ilots_sites.surf_site)::numeric, 2) AS pourc_impact_site,
               round((sum(ilots_sites.surf_impact_bv) * 100::double precision / ilots_sites.surf_bv)::numeric, 2) AS pourc_impact_bv
              FROM ilots_sites
             GROUP BY ilots_sites.id_site, ilots_sites.surf_site, ilots_sites.surf_bv;
        */
        double pourc_impact_site = 0;
        double pourc_impact_bv = 0;
        final FeatureCollection rpgBufferCol = georef.getFeatureCollection(GeoReferentials.TYPE_RPG, null);
        try(FeatureIterator ite = rpgBufferCol.iterator()){
            while(ite.hasNext()){
                final Feature feature = ite.next();
                final int id_ilot = ((Number) feature.getProperty("id_ilot").getValue()).intValue();
                final Geometry geom = (Geometry) feature.getDefaultGeometryProperty().getValue();
                final Float pourc_impact = pourc.get(id_ilot);
                if(pourc_impact==null) continue;
                final double surf_inters_site = siteGeomBuffer.intersection(geom).getArea();
                final double surf_impact_site = surf_inters_site * pourc_impact;
                final double surf_inters_bv = geom_bv.intersection(geom).getArea();
                final double surf_impact_bv = surf_inters_bv * pourc_impact;

                pourc_impact_site += surf_impact_site;
                pourc_impact_bv += surf_impact_bv;
            }
        }

        pourc_impact_site = pourc_impact_site * 100.0 / siteGeomBufferArea;
        pourc_impact_bv   = pourc_impact_bv   * 100.0 / area_bv;

        indexes.add(indicator.agriSiteSpi  .createIndex(pourc_impact_site, year));
        indexes.add(indicator.agriBvSpi    .createIndex(pourc_impact_bv, year));
    }
}
