// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.opendata.modules.fr.datagouvfr.datasets.administration;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.text.WordUtils;
import org.openstreetmap.josm.data.coor.EastNorth;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.RelationMember;
import org.openstreetmap.josm.plugins.opendata.modules.fr.datagouvfr.datasets.DataGouvDataSetHandler;
import org.openstreetmap.josm.tools.Logging;
import org.openstreetmap.josm.tools.Pair;

/**
 * Handler for GeoFla 2.0. Compatibility for previous version 1.1 has been dropped.
 * See http://professionnels.ign.fr/sites/default/files/DC_GEOFLA_2-0.pdf
 */
public class GeoFlaHandler extends DataGouvDataSetHandler {

    private static final String ADMIN_LEVEL = "admin_level";

    /**
     * Constructs a new {@code GeoFlaHandler}.
     */
    public GeoFlaHandler() {
        setName("GEOFLA®");
        getShpHandler().setPreferMultipolygonToSimpleWay(true);
        try {
            setLocalPortalURL("http://professionnels.ign.fr/geofla#tab-3");
        } catch (MalformedURLException e) {
            Logging.error(e);
        }
    }

    @Override
    public String getLocalPortalIconName() {
        return ICON_IGN_24;
    }

    @Override
    public boolean acceptsFilename(String filename) {
        return isDepartementFile(filename) || isCommuneFile(filename) || isCantonFile(filename) || isArrondissementFile(filename);
    }

    protected boolean isDepartementFile(String filename) {
        return acceptsShpMifFilename(filename, "DEPARTEMENT") || acceptsShpMifFilename(filename, "LIMITE_DEPARTEMENT");
    }

    protected boolean isCommuneFile(String filename) {
        return acceptsShpFilename(filename, "COMMUNE") || acceptsShpFilename(filename, "LIMITE_COMMUNE");
    }

    protected boolean isCantonFile(String filename) {
        return acceptsShpFilename(filename, "CANTON") || acceptsShpFilename(filename, "LIMITE_CANTON");
    }

    protected boolean isArrondissementFile(String filename) {
        return acceptsShpFilename(filename, "ARRONDISSEMENT") || acceptsShpFilename(filename, "LIMITE_ARRONDISSEMENT");
    }

    @Override
    public void updateDataSet(DataSet ds) {
        final String filename = getAssociatedFile().getName();
        if (isDepartementFile(filename)) {
            setNationalPortalPath("GEOFLA®-Départements-30383060");
        } else if (isCommuneFile(filename)) {
            setNationalPortalPath("GEOFLA®-Communes-30383083");
        }
        for (OsmPrimitive p : ds.allPrimitives()) {
            if (hasKeyIgnoreCase(p, "Id_geofla", "Id_GéoFLA")) {
                String deptName = WordUtils.capitalizeFully(getAndRemoveIgnoreCase(p, "Nom_dept", "Nom_Département"));
                if ("Reunion".equals(deptName)) {
                    deptName = "La Réunion";
                }
                if (isDepartementFile(filename)) {
                    p.put("name", deptName);
                } else if (isCommuneFile(filename)) {
                    p.put("name", WordUtils.capitalizeFully(getAndRemoveIgnoreCase(p, "NOM_COM")));
                    replace(p, "INSEE_COM", "ref:INSEE");
                }
                getAndRemoveIgnoreCase(p, "NOM_REG");
                replace(p, "POPULATION", "population");
                p.put("boundary", "administrative");
                String nature = getIgnoreCase(p, "Nature");
                if ("Frontière internationale".equalsIgnoreCase(nature) || "Limite côtière".equalsIgnoreCase(nature)) {
                    p.put(ADMIN_LEVEL, "2");
                } else if ("Limite de région".equalsIgnoreCase(nature)) {
                    p.put(ADMIN_LEVEL, "4");
                } else if (isDepartementFile(filename) || "Limite de département".equalsIgnoreCase(nature)) {
                    p.put(ADMIN_LEVEL, "6");
                } else if (isArrondissementFile(filename) || "Limite d'arrondissement".equalsIgnoreCase(nature)) {
                    p.put(ADMIN_LEVEL, "7");
                } else if (isCommuneFile(filename)) {
                    p.put(ADMIN_LEVEL, "8");
                }
                if (p instanceof Relation) {
                    p.put("type", "boundary");
                }
                LatLon llCentroid = getLatLon(p, deptName, "centroid", "Centroïde");
                if (llCentroid != null) {
                    Node centroid = new Node(llCentroid);
                    ds.addPrimitive(centroid);
                    if (p instanceof Relation) {
                        ((Relation) p).addMember(new RelationMember("centroid", centroid));
                    }
                }
                LatLon llChefLieu = getLatLon(p, deptName, "chf_lieu", "Chef_Lieu");
                if (llChefLieu != null) {
                    Node chefLieu = new Node(llChefLieu);
                    ds.addPrimitive(chefLieu);
                    String name = WordUtils.capitalizeFully(getAndRemoveIgnoreCase(p, "Nom_chf", "Nom_Chef_lieu"));
                    if (name != null) {
                        if (isArrondissementFile(filename)) {
                            p.put("name", name);
                        }
                        chefLieu.put("name", name);
                    }
                    String population = p.get("population");
                    if (population != null) {
                        try {
                            int pop = Integer.parseInt(population);
                            if (pop < 2000) {
                                chefLieu.put("place", "village");
                            } else if (pop < 100000) {
                                chefLieu.put("place", "town");
                            } else {
                                chefLieu.put("place", "city");
                            }
                        } catch (NumberFormatException e) {
                            Logging.warn("Invalid population: "+population);
                        }
                    }
                    if (p instanceof Relation) {
                        ((Relation) p).addMember(new RelationMember("admin_centre", chefLieu));
                    }
                }
            }
        }
    }

    protected static boolean hasKeyIgnoreCase(OsmPrimitive p, String ... strings) {
        return getIgnoreCase(p, strings) != null;
    }

    protected static String getIgnoreCase(OsmPrimitive p, String ... strings) {
        String result = null;
        for (String s : strings) {
            if (result == null) result = p.get(s);
            if (result == null) result = p.get(s.toUpperCase());
            if (result == null) result = p.get(s.toLowerCase());
        }
        return result;
    }

    protected static void removeIgnoreCase(OsmPrimitive p, String ... strings) {
        for (String s : strings) {
            p.remove(s);
            p.remove(s.toUpperCase());
            p.remove(s.toLowerCase());
        }
    }

    protected static String getAndRemoveIgnoreCase(OsmPrimitive p, String ... strings) {
        String result = getIgnoreCase(p, strings);
        removeIgnoreCase(p, strings);
        return result;
    }

    protected static LatLon getLatLon(OsmPrimitive p, String dptName, String shortAttribute, String longAttribute) {
        String x = getAndRemoveIgnoreCase(p, "X_"+shortAttribute, "Abscisse_"+longAttribute);
        String y = getAndRemoveIgnoreCase(p, "Y_"+shortAttribute, "Ordonnée_"+longAttribute);
        if (x != null && y != null) {
            try {
                String dptCode = getIgnoreCase(p, "Code_dept", "Code_Département");
                if (dptCode != null && dptCode.equals("97") && dptName != null) {
                    if (dptName.equals("Guadeloupe")) {
                        dptCode = "971";
                    } else if (dptName.equals("Martinique")) {
                        dptCode = "972";
                    } else if (dptName.equals("Guyane")) {
                        dptCode = "973";
                    } else if (dptName.equals("La Réunion")) {
                        dptCode = "974";
                    } else if (dptName.equals("Mayotte")) {
                        dptCode = "976";
                    } else {
                        Logging.error("Unknown French department: "+dptName);
                    }
                }
                return getLatLonByDptCode(new EastNorth(Double.parseDouble(x), Double.parseDouble(y)), dptCode, false);
            } catch (NumberFormatException e) {
                Logging.error(e);
            }
        }
        return null;
    }

    private Pair<String, URL> getGeoflaURL(String name, String urlSuffix) throws MalformedURLException {
        return new Pair<>(name, new URL("https://wxs-telechargement.ign.fr/oikr5jryiph0iwhw36053ptm/telechargement/inspire/"+urlSuffix));
    }

    @Override
    public List<Pair<String, URL>> getDataURLs() {
        List<Pair<String, URL>> result = new ArrayList<>();
        try {
            // Communes
            result.add(getGeoflaURL("2014 Communes France Métropolitaine",        "GEOFLA_THEME-COMMUNE_2014_GEOFLA_2-0_COMMUNE_SHP_LAMB93_FXX_2014-12-05/file/GEOFLA_2-0_COMMUNE_SHP_LAMB93_FXX_2014-12-05.7z"));
            result.add(getGeoflaURL("2014 Communes Guadeloupe",                   "GEOFLA_THEME-COMMUNE_2014_GEOFLA_2-0_COMMUNE_SHP_UTM20W84GUAD_D971_2014-12-08/file/GEOFLA_2-0_COMMUNE_SHP_UTM20W84GUAD_D971_2014-12-08.7z"));
            result.add(getGeoflaURL("2014 Communes Martinique",                   "GEOFLA_THEME-COMMUNE_2014_GEOFLA_2-0_COMMUNE_SHP_UTM20W84MART_D972_2014-12-08/file/GEOFLA_2-0_COMMUNE_SHP_UTM20W84MART_D972_2014-12-08.7z"));
            result.add(getGeoflaURL("2014 Communes Guyane",                       "GEOFLA_THEME-COMMUNE_2014_GEOFLA_2-0_COMMUNE_SHP_UTM22RGFG95_D973_2014-12-05/file/GEOFLA_2-0_COMMUNE_SHP_UTM22RGFG95_D973_2014-12-05.7z"));
            result.add(getGeoflaURL("2014 Communes Réunion",                      "GEOFLA_THEME-COMMUNE_2014_GEOFLA_2-0_COMMUNE_SHP_RGR92UTM40S_D974_2014-12-05/file/GEOFLA_2-0_COMMUNE_SHP_RGR92UTM40S_D974_2014-12-05.7z"));
            result.add(getGeoflaURL("2014 Communes Mayotte",                      "GEOFLA_THEME-COMMUNE_2014_GEOFLA_2-0_COMMUNE_SHP_RGM04UTM38S_D976_2014-12-05/file/GEOFLA_2-0_COMMUNE_SHP_RGM04UTM38S_D976_2014-12-05.7z"));
            // Cantons
            result.add(getGeoflaURL("2014 Cantons France Métropolitaine",         "GEOFLA_THEME-CANTON_2014_GEOFLA_2-0_CANTON_SHP_LAMB93_FXX_2014-12-05/file/GEOFLA_2-0_CANTON_SHP_LAMB93_FXX_2014-12-05.7z"));
            result.add(getGeoflaURL("2014 Cantons Guadeloupe",                    "GEOFLA_THEME-CANTON_2014_GEOFLA_2-0_CANTON_SHP_UTM20W84GUAD_D971_2014-12-08/file/GEOFLA_2-0_CANTON_SHP_UTM20W84GUAD_D971_2014-12-08.7z"));
            result.add(getGeoflaURL("2014 Cantons Martinique",                    "GEOFLA_THEME-CANTON_2014_GEOFLA_2-0_CANTON_SHP_UTM20W84MART_D972_2014-12-08/file/GEOFLA_2-0_CANTON_SHP_UTM20W84MART_D972_2014-12-08.7z"));
            result.add(getGeoflaURL("2014 Cantons Guyane",                        "GEOFLA_THEME-CANTON_2014_GEOFLA_2-0_CANTON_SHP_UTM22RGFG95_D973_2014-12-05/file/GEOFLA_2-0_CANTON_SHP_UTM22RGFG95_D973_2014-12-05.7z"));
            result.add(getGeoflaURL("2014 Cantons Réunion",                       "GEOFLA_THEME-CANTON_2014_GEOFLA_2-0_CANTON_SHP_RGR92UTM40S_D974_2014-12-05/file/GEOFLA_2-0_CANTON_SHP_RGR92UTM40S_D974_2014-12-05.7z"));
            result.add(getGeoflaURL("2014 Cantons Mayotte",                       "GEOFLA_THEME-CANTON_2014_GEOFLA_2-0_CANTON_SHP_RGM04UTM38S_D976_2014-12-05/file/GEOFLA_2-0_CANTON_SHP_RGM04UTM38S_D976_2014-12-05.7z"));
            // Arrondissements
            result.add(getGeoflaURL("2014 Arrondissements France Métropolitaine", "GEOFLA_THEME-ARRONDISSEMENT_2014_GEOFLA_2-0_ARRONDISSEMENT_SHP_LAMB93_FXX_2014-12-05/file/GEOFLA_2-0_ARRONDISSEMENT_SHP_LAMB93_FXX_2014-12-05.7z"));
            result.add(getGeoflaURL("2014 Arrondissements Guadeloupe",            "GEOFLA_THEME-ARRONDISSEMENT_2014_GEOFLA_2-0_ARRONDISSEMENT_SHP_UTM20W84GUAD_D971_2014-12-08/file/GEOFLA_2-0_ARRONDISSEMENT_SHP_UTM20W84GUAD_D971_2014-12-08.7z"));
            result.add(getGeoflaURL("2014 Arrondissements Martinique",            "GEOFLA_THEME-ARRONDISSEMENT_2014_GEOFLA_2-0_ARRONDISSEMENT_SHP_UTM20W84MART_D972_2014-12-08/file/GEOFLA_2-0_ARRONDISSEMENT_SHP_UTM20W84MART_D972_2014-12-08.7z"));
            result.add(getGeoflaURL("2014 Arrondissements Guyane",                "GEOFLA_THEME-ARRONDISSEMENT_2014_GEOFLA_2-0_ARRONDISSEMENT_SHP_UTM22RGFG95_D973_2014-12-05/file/GEOFLA_2-0_ARRONDISSEMENT_SHP_UTM22RGFG95_D973_2014-12-05.7z"));
            result.add(getGeoflaURL("2014 Arrondissements Réunion",               "GEOFLA_THEME-ARRONDISSEMENT_2014_GEOFLA_2-0_ARRONDISSEMENT_SHP_RGR92UTM40S_D974_2014-12-05/file/GEOFLA_2-0_ARRONDISSEMENT_SHP_RGR92UTM40S_D974_2014-12-05.7z"));
            // Départements
            result.add(getGeoflaURL("2014 Départements France Métropolitaine",    "GEOFLA_THEME-DEPARTEMENTS_2014_GEOFLA_2-0_DEPARTEMENT_SHP_LAMB93_FXX_2014-12-05/file/GEOFLA_2-0_DEPARTEMENT_SHP_LAMB93_FXX_2014-12-05.7z"));
            result.add(getGeoflaURL("2014 Départements Guadeloupe",               "GEOFLA_THEME-DEPARTEMENTS_2014_GEOFLA_2-0_DEPARTEMENT_SHP_UTM20W84GUAD_D971_2014-12-08/file/GEOFLA_2-0_DEPARTEMENT_SHP_UTM20W84GUAD_D971_2014-12-08.7z"));
            result.add(getGeoflaURL("2014 Départements Martinique",               "GEOFLA_THEME-DEPARTEMENTS_2014_GEOFLA_2-0_DEPARTEMENT_SHP_UTM20W84MART_D972_2014-12-08/file/GEOFLA_2-0_DEPARTEMENT_SHP_UTM20W84MART_D972_2014-12-08.7z"));
            result.add(getGeoflaURL("2014 Départements Guyane",                   "GEOFLA_THEME-DEPARTEMENTS_2014_GEOFLA_2-0_DEPARTEMENT_SHP_UTM22RGFG95_D973_2014-12-05/file/GEOFLA_2-0_DEPARTEMENT_SHP_UTM22RGFG95_D973_2014-12-05.7z"));
            result.add(getGeoflaURL("2014 Départements Réunion",                  "GEOFLA_THEME-DEPARTEMENTS_2014_GEOFLA_2-0_DEPARTEMENT_SHP_RGR92UTM40S_D974_2014-12-05/file/GEOFLA_2-0_DEPARTEMENT_SHP_RGR92UTM40S_D974_2014-12-05.7z"));
            result.add(getGeoflaURL("2014 Départements Mayotte",                  "GEOFLA_THEME-DEPARTEMENTS_2014_GEOFLA_2-0_DEPARTEMENT_SHP_RGM04UTM38S_D976_2014-12-05/file/GEOFLA_2-0_DEPARTEMENT_SHP_RGM04UTM38S_D976_2014-12-05.7z"));
        } catch (MalformedURLException e) {
            Logging.error(e);
        }
        return result;
    }
}
