'use strict';

import UI from "./ui.js";
import API_CONFIG from "./config.js";

const MAPBOX_TOKEN = 'pk.eyJ1Ijoibmljb3BhIiwiYSI6ImNseWNwZjJjbjFidzcya3BoYTU0bHg4NnkifQ.3036JnCXZTEMt6jVgMzVRw';
const BASE_URL = location.href;

let API_URL = '';
if (BASE_URL.includes('localhost')) {
    API_URL = API_CONFIG.dev;
} else {
    API_URL = API_CONFIG.prod;
}

window.API_URL = API_URL;

// 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: '#fa7861',
    fillOpacity: 0.8
};
const optionsSiti = {
    color: '#800040',
    opacity: 1,
    weight: 1.5,
    fillColor: '#800040',
    fillOpacity: 0.8
};
const optionsGrotta = {
    color: '#205dac',
    opacity: 1,
    weight: 1.5,
    fillColor: '#205dac',
    fillOpacity: 0.8
}
const optionsPaesistici = {
    color: '#222',
    opacity: 1,
    weight: 1.5,
    fillColor: '#88d28d',
    fillOpacity: 0.8
};
const optionsFabbricati = {
    color: '#222',
    opacity: 1,
    weight: 1.5,
    fillColor: '#5b5d5f',
    fillOpacity: 0.8
};

const MARKER_NAMES = {
    sites: {
        'gradola' : 'Villa di Gradola',
        'damecuta' : 'Villa di Damecuta',
        'matermania' : 'Grotta di Matermania',
        'arsenale' : 'Grotta dell\'Arsenale',
        'tiberio' : 'Bagni di Tiberio',
        'mura' : 'Mura greche',
        'san_michele' : 'Villa San Michele',
        'scala_fenicia' : 'Scala Fenicia',
        'grotta_azzurra' : 'Grotta Azzurra',
    },
};

/**
 * 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: 2,
    }).setView(this.CENTER_COORDS, zoomLevel);

    map.crs = L.CRS.EPSG4326;

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

    let layerVincoli = await this.loadLayer('vincoli.geojson', optionsVincoli);
    let layerPaesistici = await this.loadLayer('paesistici.geojson', optionsPaesistici);
    let notConserData = await fetch(`${API_URL}/not_conserved`)
        .then(data => data.json());

    // Add scale and ruler controls
    L.control.scale({imperial: false}).addTo(map);
    L.control.graphicScale({fill: 'hollow', position: 'bottomright'}).addTo(map);

    let sitesMarkers = [];
    let siteIcon = L.icon(
        {
            iconUrl: 'img/icons/siti.png',
            iconSize: [24, 36],
            iconAnchor: [12, 32],
            tooltipAnchor: [0, -26],
        }
    );
    let notConserIcon = L.icon(
        {
            iconUrl: 'img/icons/non_conserv.png',
            iconSize: [24, 36],
            iconAnchor: [12, 32],
            tooltipAnchor: [0, -26],
        }
    );

    let notConserved = [];

    for (let record of notConserData.records) {
        notConserved.push(L.marker(
                record.coordinates,
                {icon: notConserIcon}
            ).bindTooltip(record.denomination)
            .on(
                'click',
                () => UI.openNotConserModal(record, '#not-conser-data')
            )
        );
    }

    for (let id in MARKER_NAMES.sites) {
        let layer = sitesGroup.customGetLayer(id);
        let coords = layer.getBounds().getCenter();
        const fromStorage = localStorage.getItem(id);
        let data = {};

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

        const marker = L.marker(coords, { icon: siteIcon })
            .bindTooltip(MARKER_NAMES.sites[id])
            .openTooltip();
        
        marker.on('click', () => UI.openSiteModal(data, '#site-data'));

        sitesMarkers.push(marker);
    }

    let markersGroup = L.layerGroup(sitesMarkers);
    let notConservedGroup = L.layerGroup(notConserved);

    const archeo = {
        'Beni archeologici (punti)' : markersGroup,
        'Beni archeologici (strutture)' : sitesGroup,
        'Beni non conservati' : notConservedGroup,
        'Vincoli archeologici' : layerVincoli,
        'Vincoli paesistici' : layerPaesistici,
    };

    markersGroup.addTo(map);
    sitesGroup.addTo(map);
    notConservedGroup.addTo(map);

    L.control.layers(
        baseMap,
        archeo,
    ).addTo(map);

    // TODO Horrible?
    return {map: map, sites: sitesGroup};
}
/*
GIS._prepareLayers = async function(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);
    }

    // TODO: terrible!
    if (!layer.id.includes('area')) {
        const marker = L.marker(coords)
            .addTo(map)
            .bindTooltip(
                Object.keys(archeo).find(k => archeo[k] === layer)
                    .replace(/\s\(.*$/, '')
            )
            .openTooltip();
        
        if (typeof data === 'object') {
            marker.on('click', () => UI.openModal(data, '#site-data'));
        }
    }
}
*/
/**
 * 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 layerMaterArea = await this.loadLayer('matermania_area.geojson', optionsSiti, false);
    let layerArsenale = await this.loadLayer('arsenale.geojson', optionsSiti, false);
    let layerArsenaleArea = await this.loadLayer('arsenale_area.geojson', optionsSiti, false);
    let layerGradola = await this.loadLayer('gradola.geojson', optionsSiti, false);
    let layerGradolaArea = await this.loadLayer('gradola_area.geojson', optionsSiti, false);
    let layerMura = await this.loadLayer('mura.geojson', optionsSiti, false);
    let layerSanMichele = await this.loadLayer('san_michele.geojson', optionsSiti, false);
    let layerDamecuta = await this.loadLayer('damecuta.geojson', optionsSiti, false);
    let layerTiberio = await this.loadLayer('tiberio.geojson', optionsSiti, false);
    let layerScala = await this.loadLayer('scala_fenicia.geojson', optionsSiti, false);
    let layerGrotta = await this.loadLayer('grotta_azzurra.geojson', optionsGrotta, false);

    layerMater.id = 'matermania';
    layerMaterArea.id = 'matermania_area';
    layerGradola.id = 'gradola';
    layerGradolaArea.id = 'gradola_area';
    layerArsenale.id = 'arsenale';
    layerArsenaleArea.id = 'arsenale_area';
    layerMura.id = 'mura';
    layerSanMichele.id = 'san_michele';
    layerDamecuta.id = 'damecuta';
    layerTiberio.id = 'tiberio';
    layerScala.id = 'scala_fenicia';
    layerGrotta.id = 'grotta_azzurra';

    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'
    });
    let mapbox = new L.tileLayer(`https://api.mapbox.com/v4/{id}/{z}/{x}/{y}@2x.jpg90?access_token=${MAPBOX_TOKEN}`, {
        id : 'mapbox.satellite',
        tileSize : 512,
        zoomOffset : -1,
        maxNativeZoom : 22,
        maxZoom: 22,
        attribution: '&copy; Mapbox'
    });
    let boundaries = await this.loadLayer('confini.geojson', {}, false);
    let buildings = await this.loadLayer('fabbricati.geojson', optionsFabbricati, false);
    let baseCatasto = new L.LayerGroup([buildings, boundaries]);

    const sitesGroup = new L.LayerGroup([
        layerMater,
        layerMaterArea,
        layerGradola,
        layerGradolaArea,
        layerArsenale,
        layerArsenaleArea,
        layerMura,
        layerSanMichele,
        layerDamecuta,
        layerTiberio,
        layerScala,
        layerGrotta,
    ]);
    const baseGroup = new L.LayerGroup([osmap]);
    baseGroup.addTo(map);
    const baseMap = {
        "OpenStreetMap" : osmap,
        "Satellite" : mapbox,
        "Cartografia catastale" : baseCatasto,
    };

    return {baseMap, 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 () {
            return options;
        },
        onEachFeature: function (feature, layer) {
            if (popup) {
                layer.bindPopup(GIS.featurePopup(geoJSON, feature));
            }
            else {
                layer.on("click", async () => {
                    let data = GIS.layerData(layerId);
                    
                    if (typeof data === 'object') {
                        UI.openSiteModal(data);
                    }
                });
            }
        }
    });

    return layer;
}
/**
 * Retrieves data for a given layer
 * @param {string} layerId 
 * @returns {object} Data for this layer from DB or cache
 */
GIS.layerData = async function (layerId) {
    const fromStorage = localStorage.getItem(layerId);
    let data = {};

    if (fromStorage !== 'undefined') {
        try {
            data = JSON.parse(fromStorage);
        } catch (error) {
            console.log(error);
        }
    } else {
        data = await GIS._fetchData('site/' + layerId);
    }

    return data;
}
/**
 * Cache data from DB in local storage
 * for a given layer
 * @param {string} layerId
 */
GIS.cacheDBData = async function (layerId) {
    const data = await this._fetchData('site/' + 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;