'use strict';

import UI from "./ui.js";

// Global leaflet
/**
 * @namespace GIS
 */
const GIS = {};

GIS.CENTER_COORDS = [40.5492, 14.2317];
GIS.INIT_ZOOM = 14;
const optionsVincoli = {
    color: '#222',
    opacity: 0.8,
    weight: 1,
    fillColor: '#987db7',
    fillOpacity: 0.8
};
const optionsSiti = {
    color: '#800040',
    opacity: 1,
    weight: 2.5,
    fillColor: '#800040',
    fillOpacity: 0.8
};
const optionsPaesistici = {
    color: '#222',
    opacity: 1,
    weight: 1.5,
    fillColor: '#ff8000',
    fillOpacity: 0.8
};

const BASE_URL = location.href;
const API_URL = 'http://localhost:3002/gis';
/**
 * Capitalize a text string
 * @todo Move to utils
 * @param {?string} text 
 * @returns {?string} The capitalized string or null
 */
function capitalize(text) {
    let capital = text;
    if (text) {
        let words = text.split(' ');
        capital = '';

        for (let w of words) {
            w = w[0].toUpperCase() + w.slice(1);
            capital += w + ' ';
        }
        capital.trimEnd();
    }

    return capital;
}
/**
 * @param {string} mapId
 * @param {number} zoomLevel
 * @returns {{map: Map, sites: Layer}}
 */
GIS.initMap = async function (mapId, zoomLevel = this.INIT_ZOOM) {
    L.LayerGroup.include({
        customGetLayer : function (id) {
            for (let l in this._layers) {
                if (this._layers[l].id === id) {
                    return this._layers[l];
                }
            }
        }
    });
    
    let map = L.map(mapId, {
        attributionControl: false,
        minZoom: 11,
    }).setView(this.CENTER_COORDS, zoomLevel);

    map.crs = L.CRS.EPSG4326;

    const {baseMap, archeo, sitesGroup} = await this.initLayers(map);

    L.control.layers(
            baseMap,
            archeo,
            {collapsed: false}
        )
        .addTo(map);

    sitesGroup.eachLayer(async layer => {
        const fromStorage = localStorage.getItem(layer.id);
        let data = {};
        let coords = layer.getBounds().getCenter();

        if (fromStorage !== 'undefined') {
            try {
                data = JSON.parse(fromStorage);
                const lat = data?.lat ?? coords[0];
                const lon = data?.lon ?? coords[1];
                coords = [lat, lon];
            } catch (error) {
                console.log(error);
            }
        } else {
            data = await GIS._fetchData(layer.id);
        }

        const marker = L.marker(coords)
            .addTo(map)
            .bindTooltip(Object.keys(archeo).find(k => archeo[k] === layer))
            .openTooltip();
        
        if (typeof data === 'object') {
            marker.on('click', () => UI.openModal(data));
        }
    });

    // TODO Horrible?
    return {map: map, sites: sitesGroup};
}
/**
 * Adds layers to map and returns an object
 * with {baseMap, archeoLayers, sitesLayerGroup}
 * 
 * @param {L.Map} map 
 * @returns {{baseMap: {"OpenStreetMap": L.TileLayer}, archeo: object, sitesGroup: L.LayerGroup}}
 */
GIS.initLayers = async function(map) {
    let layerMater = await this.loadLayer('matermania.geojson', optionsSiti, false);
    let layerArsenale = await this.loadLayer('arsenale.geojson', optionsSiti, false);
    let layerGradola = await this.loadLayer('gradola.geojson', optionsSiti, false);

    layerMater.id = 'matermania';
    layerGradola.id = 'gradola';
    layerArsenale.id = 'arsenale';
    let layerVincoli = await this.loadLayer('vincoli.geojson', optionsVincoli);
    // TODO named parameters??
    let layerPaesistici = await this.loadLayer('paesistici.geojson', optionsPaesistici);
    let osmap = new L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxNativeZoom : 22,
        maxZoom: 22,
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    });

    const sitesGroup = new L.LayerGroup([layerMater, layerGradola, layerArsenale]);
    const baseGroup = new L.LayerGroup([osmap]);
    baseGroup.addTo(map);
    sitesGroup.addTo(map);
    const baseMap = {
        "OpenStreetMap" : osmap
    };
    const archeo = {
        "Villa di Gradola" : layerGradola,
        "Grotta di Matermania" : layerMater,
        "Grotta dell'Arsenale" : layerArsenale,
        "Vincoli archeologici" : layerVincoli,
        "Vincoli paesistici" : layerPaesistici,
    };

    return {baseMap, archeo, sitesGroup};
}
/**
 * @todo Distinguere tipo di geojson per contenuto popup 
 * @param {string} geoJSON 
 * @param {{color, opacity, weight, fillColor, fillOpacity}} options Style options for features
 * @param {boolean} popup Should the features have a popup? 
 */
GIS.loadLayer = async function (geoJSON, options, popup = true) {
    const geo = await fetch(`${BASE_URL}/geojson/${geoJSON}`)
        .then(res => res.json())
        .catch(error => console.error(`Can't load layer ${geoJSON}. Reason: ${error}`));
    
    const layerId = geoJSON.replace('.geojson', '');
    
    this.cacheDBData(layerId);

    // Show data from feature in popUp?
    const layer = new L.geoJson(geo, {
        style: function () {
            let style = options;
            return style;
        },
        onEachFeature: function (feature, layer) {
            if (popup) {
                layer.bindPopup(GIS.featurePopup(geoJSON, feature));
            }
            else {
                layer.on("click", async () => {
                    const fromStorage = localStorage.getItem(layerId);
                    let data = {};

                    if (fromStorage !== 'undefined') {
                        try {
                            data = JSON.parse(fromStorage);
                        } catch (error) {
                            console.log(error);
                        }
                    } else {
                        await GIS._fetchData(layerId);
                    }
                    
                    if (typeof data === 'object') {
                        UI.openModal(data);
                    }
                });
            }
        }
    });

    return layer;
}
/**
 * Cache data from DB in local storage
 * for a given layer
 * @param {string} layerId
 */
GIS.cacheDBData = async function (layerId) {
    const data = await this._fetchData(layerId);
    localStorage.setItem(layerId, JSON.stringify(data));
}
/**
 * Generate proper content for features popup
 * @todo Hard-coded names!!
 * 
 * @param {string} layerName
 * @param {object} feature
 * @returns {string} The popup's content
 */
GIS.featurePopup = function (layerName, feature) {
    const html = `
        <table class="table is-striped is-size-6 m-2">
            <tr><th>Oggetto</th><td>${feature.properties.OGGETTO}</td></tr>
            <tr><th>Anno</th><td>${feature.properties.ANNO}</td></tr>
            <tr><th>Comune</th><td>${capitalize(feature.properties.COMUNE)}</td></tr>
            <tr><th>Località</th><td>${capitalize(feature.properties.LOCALITA)}</td></tr>
            <tr><th>Proprietà</th><td>${capitalize(feature.properties.PROPRIETA)}</td></tr>
        </table>
    `;
    const content = {
        'vincoli.geojson' : html,
        'paesistici.geojson' : html,
    };

    return content[layerName];
}
/**
 * Fetch data from API
 * @param {string} recordId
 */
GIS._fetchData = async function (recordId) {
    const data = await fetch(`${API_URL}/${recordId}`)
        .then(res => res.json())
        .catch(err => console.log('Error fetching data from DB: ' + err));

    return data;
}

export default GIS;