/* default variables */
var default_pointer = "/media/images/default-pointer.png";
var marker_cluster = "/media/images/marker-cluster.png";
var view_projection = 'EPSG:3857';
var animation_duration = 250;
var cluster_threshold_1 = 8;
var cluster_threshold_2 = 25;
var base_color_R = 111;
var base_color_V = 66;
var base_color_B = 193;
var base_color_rvb;
var map_default_center = 'SRID=4326;POINT (2.4397 46.5528)';
var map_default_zoom = '7';
var min_auto_zoom_on_cluster = 13;
/* custom control */
var track_me_msg = "Geolocalize me";
var geoloc_activated_msg = "Geolocation activated";
var geoloc_disabled_msg = "Geolocation disabled";
var geolocation = {};
var geoloc_feature = {};
var geoloc_activated = {};
var fetching_msg = "Fetching data...";
var _map_submit_search = function(query_vars, name, source){
    var modal_base_text = $('.modal-progress .modal-header').html();
    $('.modal-progress .modal-header').html(fetching_msg);
    $('.modal-progress').modal('show');
    var data = search_get_query_data(query_vars, name);
    var nb_select = jQuery("#id_" + name + "-length_map").val();
    if (!nb_select) nb_select = 10;
    var url = source + "json-map?length=" + nb_select + "&submited=1&" + data;
    var use_map_limit = false;
    if(data.indexOf("no_limit=true") == -1){
        url += "&limit=" + current_map_limit;
        use_map_limit = true;
    }
    $.getJSON(url, function(data) {
        var timestamp = Math.floor(Date.now() / 1000);
        var map_id = "map-" + timestamp;
        $('.modal-progress .modal-header').html("{% trans 'Render map...' %}");
        var html = render_map(map_id, use_map_limit);
        $("#tab-content-map-" + name).html(html);
        $("#id_" + name + "-length_map").change(map_submit_search);
        if ($('.modal-progress').length > 0){
            $('.modal-progress').modal('hide');
            $('.modal-progress .modal-header').html(modal_base_text);
        }
        register_map(map_id, data);
    });
    return false;
};
var geoloc_activated_message = function(map_id){
    setTimeout(function(){
        navigator.geolocation.watchPosition(function(position) {
            if(!geoloc_activated[map_id]){
                if (display_info) display_info(geoloc_activated_msg);
                geoloc_activated[map_id] = true;
            }
        },
        function (error) {
            if (error.code == error.PERMISSION_DENIED)
                if (display_info) display_info(geoloc_disabled_msg);
        });
    }, 200);
};
var set_geoloc_source = function(map_id){
    geolocation[map_id] = new ol.Geolocation({
        projection: view_projection
    });
    geolocation[map_id].setTracking(true);
    geoloc_feature[map_id] = new ol.Feature();
    geoloc_feature[map_id].setStyle(new ol.style.Style({
        image: new ol.style.Circle({
            radius: 6,
            fill: new ol.style.Fill({color: '#3399CC'}),
        stroke: new ol.style.Stroke({color: '#fff', width: 2})
        })
    }));
    var accuracy_feature = new ol.Feature();
    geolocation[map_id].on('change:accuracyGeometry', function() {
        accuracy_feature.setGeometry(geolocation[map_id].getAccuracyGeometry());
    });
    geolocation[map_id].on('change:position', function() {
        var coordinates = geolocation[map_id].getPosition();
        geoloc_feature[map_id].setGeometry(
            coordinates ? new ol.geom.Point(coordinates) : null);
        var v = map[map_id].getView();
        v.animate({center: coordinates, duration: animation_duration * 2});
    });
    new ol.layer.Vector({
        map: map[map_id],
        source: new ol.source.Vector({
            features: [geoloc_feature[map_id], accuracy_feature]
        })
    });
    geoloc_activated_message(map_id);
};
var TrackPositionControl = (function (Control) {
    function TrackPositionControl(opt_options) {
        var options = opt_options || {};
        this.map_id = options['map_id'];
        var button = document.createElement('button');
        button.type = "button";
        button.innerHTML = '';
        button.title = track_me_msg;
        var element = document.createElement('div');
        element.className = 'track-position ol-unselectable ol-control';
        element.appendChild(button);
        Control.call(this, {
            element: element,
            target: options.target
        });
        button.addEventListener(
            'click', this.handleTrackPosition.bind(this),
            false
        );
    }
    if ( Control ) TrackPositionControl.__proto__ = Control;
    TrackPositionControl.prototype = Object.create( Control && Control.prototype );
    TrackPositionControl.prototype.constructor = TrackPositionControl;
    TrackPositionControl.prototype.handleTrackPosition = function handleTrackPosition () {
        if (!geolocation[this.map_id]){
            set_geoloc_source(this.map_id);
        } else {
            if (!geoloc_activated[this.map_id]) return;
            if (geolocation[this.map_id].getTracking()){
                geolocation[this.map_id].setTracking(false);
                if (display_info) display_info(geoloc_disabled_msg);
            } else {
                geolocation[this.map_id].setTracking(true);
                if (display_info) display_info(geoloc_activated_msg);
            }
        }
        return false;
    };
    return TrackPositionControl;
}(ol.control.Control));
/* base layers */
var source_osm = function(options){
    return new ol.layer.Tile({
        source: new ol.source.OSM()
    });
};
var default_map_layers = {
    'osm': source_osm
};
var get_layers = function(layers){
    if (!layers){
        layers = [{'type': 'osm', 'options': null}];
    }
    var ol_layers = [];
    for (idx in layers){
        var layer_attr = layers[idx];
        ol_layers.push(
            default_map_layers[layer_attr['type']](layer_attr['options'])
        );
    }
    return ol_layers;
};
/* styles */
var get_icon_style = function(feature){
    return new ol.style.Style({
        image: new ol.style.Icon({
            anchor: [17, 50],
            anchorXUnits: 'pixels',
            anchorYUnits: 'pixels',
            size: [35, 50],
            src: static_path + default_pointer
        })
    });
};
var get_vector_style = function(feature){
    return new ol.style.Style({
        stroke: new ol.style.Stroke({
            color: 'rgba(' + base_color_rvb + ', 1)',
            width: 2
        }),
        fill: new ol.style.Fill({
            color: 'rgba(' + base_color_rvb + ', 0.2)'
        })
    });
};
var cluster_get_style = function(feature, resolution){
    feature.set('key', 'cluster');
    var cluster_features = feature.get('features');
    var size = cluster_features.length;
    feature.set('size', size);
    var style = _styleCache[size];
    if (!style && size == 1){
        style = _styleCache[size] = [get_icon_style()];
    } else if (!style && size > 1){
        var color = size > cluster_threshold_2 ? "192,0,0" : size > cluster_threshold_1 ? "255,128,0" : "0,128,0";
        var radius = Math.max(8, Math.min(size * 0.75, 20));
        var lbl = size.toString();
        style = _styleCache[size] = [
            new ol.style.Style({
                image: new ol.style.Circle({
                    radius: radius,
                    stroke: new ol.style.Stroke({
                        color:"rgba("+color+",0.5)",
                        width: 15
                    }),
                    fill: new ol.style.Fill({
                        color:"rgba("+color+",1)"
                    })
                }),
                text: new ol.style.Text({
                    text: lbl,
                    fill: new ol.style.Fill({
                        color: '#fff'
                    })
                })
            })
        ];
    }
    return style;
}
/* clustering */
var _styleCache;
var cluster_source = {};
var cluster_layer = {};
var enable_clustering = function(map_id){
    // cache for styles
    _styleCache = {};
    // cluster Source
    cluster_source[map_id] = new ol.source.Cluster({
        distance: 40,
        source: new ol.source.Vector()
    });
    // animated cluster layer
    cluster_layer[map_id] = new ol.layer.Vector({
        name: 'Cluster',
        source: cluster_source[map_id],
        // cluster style
        style: cluster_get_style
    });
    map[map_id].addLayer(cluster_layer[map_id]);
};
var reinit_clustering = function(map_id){
    if (map_id in cluster_source) {
        cluster_source[map_id].getSource().clear();
    }
    _styleCache = {};
};
/* manage clicks */
var current_feature;
var animate_in_progress = false;
var animate_end = function(){animate_in_progress = false};
var wait_animation_end = function(callback, retry){
    if (!retry) retry = 1;
    setTimeout(function(){
        retry += 1
        if (retry < 5 && animate_in_progress){
            wait_animation_end(callback)
        } else {
            callback();
        }
    }, 100);
};
var manage_click_on_map = function(map_id){
    return function(e) {
        var feature = map[map_id].forEachFeatureAtPixel(
            e.pixel,
            function(feature, layer) {
                return feature;
            }
        );
        click_on_feature(map_id, feature, e);
    };
};
var current_event;
var click_on_feature = function(map_id, feature, e){
    // console.log("click_on_feature");
    current_event = e;
    if (!$(e.target).is($(popup_item[map_id]))
        && !$.contains($(popup_item[map_id])[0], e.target) ) {
        $(popup_item[map_id]).hide();
        $('#ishtar-map-window-' + map_id).hide();
    }
    if (typeof feature == 'undefined'){
        current_feature = null;
        return;
    }
    current_feature = feature;
    if (!feature) return;
    var timeout = 10;
    setTimeout(function(){
        // zoom on aggregated
        var key = feature.get('key');
        if (feature.get('name') || feature.get('key')){
            feature = click_on_cluster(map_id, feature);
        }
    }, timeout);
};
var auto_zoom = false;
var click_on_cluster = function(map_id, feature, zoom_level, duration, nb_zoom,
                                current_nb_items){
    // console.log("click_on_cluster");
    if (!duration){
        // zoom animation must be slower
        duration = animation_duration * 2;
    }
    if (!nb_zoom) nb_zoom = 0;
    var props = feature.getProperties();
    if (!'features' in props) return feature;
    if (!auto_zoom || props['features'].length == 1){
        return display_cluster_detail(map_id, feature);
    }
    if (!current_nb_items){
        current_nb_items = props['features'].length;
    } else if(current_nb_items != props['features'].length) {
        // stop zooming there less item in the cluster
        return feature;
    }
    var v = map[map_id].getView();
    if (!zoom_level) zoom_level = v.getZoom() + 1;
    // center
    var new_center = feature.getGeometry().getCoordinates();
    // max zoom reached
    if (zoom_level >= min_auto_zoom_on_cluster){
        animate_in_progress = true;
        v.animate({center: new_center, duration: duration}, animate_end);
        return display_cluster_detail(map_id, feature);
    }
    // zoom
    animate_in_progress = true;
    v.animate({center: new_center, zoom: zoom_level, duration: duration},
              animate_end);
    nb_zoom += 1;
    // something wrong stop zoom!
    if (nb_zoom > v.getMaxZoom()) return feature;
    // wait for the animation to finish before rezoom
    return setTimeout(
        function(){
            // our cluster must be at the center (if it exists after zoom)
            var pixel = map[map_id].getPixelFromCoordinate(v.getCenter());
            var new_feature;
            map.forEachFeatureAtPixel(
                pixel, function(feat, layer){
                    if (layer == cluster_layer[map_id]){
                        new_feature = feat;
                        return true
                    }
                }
            );
            if (new_feature){
                if (zoom_level < min_auto_zoom_on_cluster){
                    return display_cluster_detail(map_id, new_feature);
                }
                return click_on_cluster(
                    map_id, new_feature, zoom_level + 1, duration, nb_zoom,
                    current_nb_items);
            }
            // no more cluster feature here or min auto zoom reach: stop zooming
            return feature;
        }, duration + 200);
};
/* display info */
var display_cluster_detail = function(map_id, cluster){
    // console.log("display_cluster_detail");
    var features = cluster.getProperties()['features']
    var offset_x = 0;
    var offset_y = -21;
    if (!features){
        features = [cluster];
        offset_y = -7;
    } else if (features.length == 1){
        offset_y = -54;
    }
    display_items(map_id, features, offset_x, offset_y);
};
var display_items = function(map_id, features, offset_x, offset_y){
    wait_animation_end(function() {_display_items(map_id, features, offset_x, offset_y)});
};
var open_map_window = function(map_id){
    return function(){
        $('#ishtar-map-window-' + map_id).show();
    };
};
var complete_list_label = "complete list...";
var _display_items = function(map_id, features, offset_x, offset_y){
    // console.log("display_items");
    var feature = features[0];
    var geom = feature.getGeometry();
    var ul_class = "map-list-" + map_id;
    var popup_content = "
";
    var window_content = "";
    var has_extra = false;
    for (idx_feat in features){
        if (idx_feat == 5){
            popup_content += "- ";
            popup_content += complete_list_label;
            popup_content += "";
        }
        var feat = features[idx_feat];
        var properties = feat.getProperties();
        var link = link_template[map_id].replace("", properties["id"]);
        var txt = "
- " + link + " " + properties['name'] + "";
        window_content += txt;
        if (idx_feat < 5){
            popup_content += txt;
        }
    }
    popup_content += "
";
    window_content += "
";
    $("#ishtar-map-window-" + map_id + " .modal-body").html(window_content);
    $(popup_item[map_id]).html(popup_content);
    $("." + ul_class + " .map-list-detail").click(open_map_window(map_id));
    if (geom.getType() == "Point"){
        popup[map_id].setPosition(geom.getCoordinates());
    } else {
        popup[map_id].setPosition(current_event.coordinate);
    }
    popup[map_id].setOffset([offset_x, offset_y]);
    $(popup_item[map_id]).css({opacity: 0});
    $(popup_item[map_id]).show(0, function(){
        setTimeout(function(){
            popup[map_id].setOffset([offset_x, offset_y]);
            $(popup_item[map_id]).css({opacity: 1});
        }, 200);
    });
};
/* hover */
var manage_hover = function(map_id){
    return function(e) {
        var pixel = map[map_id].getEventPixel(e.originalEvent);
        var feature = map[map_id].forEachFeatureAtPixel(
            e.pixel,
            function(feature, layer) {
                return feature;
            }
        );
        var hit = map[map_id].hasFeatureAtPixel(pixel);
        var target = map[map_id].getTarget();
        target = typeof target === "string" ?
            document.getElementById(target) : target;
        target.style.cursor = hit ? 'pointer' : '';
    }
};
/* popup */
var popup = {};
var popup_item = {};
var init_popup = function(map_id){
    popup_item[map_id] = document.getElementById("ishtar-map-popup-" + map_id);
    var popup_options = {
        element: popup_item[map_id],
        positioning: 'bottom-center'
    }
    popup[map_id] = new ol.Overlay(popup_options);
    map[map_id].addOverlay(popup[map_id]);
};
/* display map */
var center;
var point_features = {};
var map = {};
var map_view = {};
var map_layers = {};
var proj_options = {
    dataProjection:'EPSG:4326', featureProjection: view_projection
}
var geojson_format = new ol.format.GeoJSON(proj_options);
var wkt_format = new ol.format.WKT(proj_options);
var link_template = {};
var vector_source = {};
var vector_layer = {};
var vector_features = {};
// for test
var geo_items_features = null;
var current_test = false;
var initialize_test_map = function () {
    geo_items_features = {};
    current_test = true;
    if ($("#http-geo-items-ready").length === 0) {
        $("#display-geo-items").after('Ready!
');
    }
    $("#http-geo-items-ready").hide();
}
var initialize_base_map = function(map_id, layers){
    center = wkt_format.readGeometry(map_default_center).getCoordinates();
    map_layers[map_id] = get_layers(layers);
    map_view[map_id] = new ol.View({
        projection: view_projection,
        center: ol.proj.fromLonLat([center[0], center[1]]),
        zoom: map_default_zoom
    });
    var map_controls = ol.control.defaults().extend([
        new ol.control.OverviewMap({
            layers: map_layers[map_id]
        }),
        new ol.control.FullScreen(),
        new ol.control.ScaleLine()
    ]);
    if (location.protocol == 'https:'){
        map_controls.push(
            new TrackPositionControl({map_id: map_id})
        );
    }
    map[map_id] = new ol.Map({
        controls: map_controls,
        target: map_id,
        layers: map_layers[map_id],
        view: map_view[map_id]
    });
}
var redraw_map = function(map_id, layers){
    map[map_id].setTarget(null);
    map[map_id] = null;
    initialize_base_map(map_id, layers);
    reinit_clustering(map_id);
    current_feature = null;
};
var display_map = function(map_id, points, lines_and_polys, layers){
    base_color_rvb = base_color_R + ', ' + base_color_V + ', ' + base_color_B;
    if (points){
        link_template[map_id] = points['link_template'];
    } else {
        link_template[map_id] = lines_and_polys['link_template'];
    }
    if (map[map_id]){
        redraw_map(map_id, layers);
    } else {
        initialize_base_map(map_id, layers);
    }
    //console.log('map init')
    //console.log(map[map_id]);
    display_points(map_id, points);
    display_lines_and_polys(map_id, lines_and_polys);
    //console.log('map after points');
    //console.log(map[map_id]);
    init_popup(map_id);
    map[map_id].on('click', manage_click_on_map(map_id));
    map[map_id].on('pointermove', manage_hover(map_id));
    if (current_test) {
        geo_items_features[map_id] = []; // for test
    }
};
var display_points = function(map_id, points){
    //console.log('points');
    //console.log(points);
    if (!points) return;
    point_features[map_id] = geojson_format.readFeatures(points);
    enable_clustering(map_id);
    cluster_source[map_id].getSource().addFeatures(point_features[map_id]);
    if (points.features.length){
        map_view[map_id].fit(cluster_source[map_id].getSource().getExtent());
        if (map_view[map_id].getZoom() > 12){
            map_view[map_id].setZoom(12);
        }
    }
};
var display_lines_and_polys = function(map_id, lines_and_polys){
    if (!lines_and_polys) return;
    vector_features[map_id] = geojson_format.readFeatures(lines_and_polys);
    vector_source[map_id] = new ol.source.Vector();
    vector_source[map_id].addFeatures(vector_features[map_id]);
    vector_layer[map_id] = new ol.layer.Vector({
        source: vector_source[map_id],
        style: get_vector_style
    });
    map[map_id].addLayer(vector_layer[map_id]);
    if (lines_and_polys.features.length){
        map_view[map_id].fit(vector_source[map_id].getExtent());
    }
};
var display_geo_items = function(map_id, base_url, slug, pk,
                              disp_cr, disp_bf, get_poly) {
    var url = base_url;
    if (slug === "operation") {
        url += "?operation_pk=";
    }
    else {
        url += "?context_record_pk=";
    }
    url += pk;
    httpRequest = new XMLHttpRequest();
    if (!httpRequest) { return; }
    httpRequest.onreadystatechange = function() {
        if (httpRequest.readyState === XMLHttpRequest.DONE) {
            if (httpRequest.status === 200) {
                geo_items = to_geo_items(JSON.parse(httpRequest.responseText), slug, disp_cr, disp_bf)
                console.log('geoitems')
                console.log(geo_items)
                if (current_test) {
                    geo_items_features[map_id] = [];
                }
                let bfs = null;
                let crs = null;
                if (get_poly) {
                    bfs = display_associated_polys(map_id, geo_items['base-finds'], 'basefind');
                    crs = display_associated_polys(map_id, geo_items['context-records'], 'contextrecord');
                } else {
                    bfs = display_associated_points(map_id, geo_items['base-finds'], 'basefind');
                    crs = display_associated_points(map_id, geo_items['context-records'], 'contextrecord');
                }
                if (current_test) {
                    geo_items_features[map_id].push(bfs)
                    geo_items_features[map_id].push(crs)
                    $("#http-geo-items-ready").show();
                }
            } else { return; }
        }
    };
    httpRequest.open('GET', url, true);
    httpRequest.send();
};
var to_geo_items = function (obj, slug, disp_cr, disp_bf) {
    const objects = {'context-records': {'features': [], 'type': 'FeatureCollection'},
    'base-finds': {'features': [], 'type': 'FeatureCollection'}};
    if (slug === "operation") {
        const crs = obj['properties']['context-records'];
        for (const cr of crs['features']) {
            if (disp_bf) {console.log(cr);
                for (const bf of cr['properties']['base-finds']['features']) {
                    objects['base-finds']['features'].push(bf)
                }
            }
            delete cr['properties'];
            if (disp_cr) {
                objects['context-records']['features'].push(cr)
            }
        }
    }
    else {
        for (const bf of obj['properties']['base-finds']['features']) {
            objects['base-finds']['features'].push(bf)
        }
    }
    return objects;
}
var display_associated_polys = function (map_id, polys, slug) {
    _vector_features = geojson_format.readFeatures(polys);
    _vector_source = new ol.source.Vector();
    _vector_source.addFeatures(_vector_features);
    style = get_associated_base_find_style;
    if (slug === 'contextrecord') {
        style = get_associated_context_record_style;
    }
    _vector_layer = new ol.layer.Vector({
        source: _vector_source,
        style: style
    });
    map[map_id].addLayer(_vector_layer);
    return _vector_features
}
var get_associated_base_find_style = function (feature) {
    return new ol.style.Style({
        stroke: new ol.style.Stroke({
            color: 'rgba(0, 0, 0, 1)',
            width: 2
        }),
        fill: new ol.style.Fill({
            color: 'rgba(O, O, O, 0.2)'
        })
    });
};
var get_associated_context_record_style = function (feature) {
    return new ol.style.Style({
        stroke: new ol.style.Stroke({
            color: 'rgba(255, 255, 255, 1)',
            width: 2
        }),
        fill: new ol.style.Fill({
            color: 'rgba(255, 255, 255, 0.2)'
        }),
        radius: 10
    });
};
var display_associated_points = function (map_id, points, slug) {
    console.log('geo items points')
    console.log(points);
    _point_features = geojson_format.readFeatures(points);
    _cluster_source = new ol.source.Cluster({
        distance: 40,
        source: new ol.source.Vector()
    });
    _cluster_source.getSource().addFeatures(_point_features);
    // TODO: create own style
    style = cluster_get_style;
    if (slug === 'contextrecord') {
        style = style;
    }
    _cluster_layer = new ol.layer.Vector({
        name: 'Cluster',
        source: _cluster_source,
        style: style
    });
    map[map_id].addLayer(_cluster_layer);
    return _point_features;
}
// for test purpose
// to insert in sheet_simple_map.html when working with maps
// wait_for_map("map-{{window_id}}-{{geo_item.SLUG}}-{{geo_item.pk}}");
var wait_for_map = function(map_id) {
    map[map_id].on('rendercomplete', function(event) {
        console.log("map fully loaded");
    })
    if (map_id in cluster_layer) {
        console.log('cluster layer');
        cluster_layer[map_id].on('change', function(event) {
            if (cluster_layer.getState === "ready") {
                console.log("cluster fully loaded");
            }
        })
    }
    if (map_id in vector_layer) {
        console.log('vector layer');
        vector_layer[map_id].on('change', function(event) {
            if (vector_layer.getState === "ready") {
                console.log("vector fully loaded");
            }
        })
    }
    if (map_id in cluster_source) {
        console.log('cluster source');
        cluster_source[map_id].on('change', function(event) {
            if (cluster_source.getState === "ready") {
                console.log("cluster source fully loaded");
            }
        })
    }
    if (map_id in vector_source) {
        console.log('vector source');
        vector_source[map_id].on('change', function(event) {
            if (vector_source.getState === "ready") {
                console.log("vector source fully loaded");
            }
        })
    }
}
var get_map_by_id_test = function (id) {
// problem here: async function because map and layers are not fully loaded directly
    map[id].getLayers().getArray()[0].getSource().on(
        'change', function (evt) {
            console.log('change')
            var source = evt.target;
            if (source.getState() === "ready") {
                console.log('ready');
                console.log(map_layers[id][0].getSource().getKeys())
            }
        }
    )
    map[id].on(
        'change', function (evt) {
            console.log('change')
            var map = evt.target;
            if (map.getState() === "ready") {
                console.log('ready');
                console.log(map_layers[id][0].getSource().getKeys())
            }
        }
    )
    console.log("vector_features")
    console.log(vector_features[id])
    console.log("layers")
    console.log(map_layers[id])
    console.log("source")
    console.log(map_layers[id][0].getSource())
}
var get_features_by_id = function (id) {
    if (vector_features[id] == null) {
        var base_features = geojson_format.writeFeaturesObject(point_features[id], {decimals: 3});
    } else {
        var base_features = geojson_format.writeFeaturesObject(vector_features[id], {decimals: 3});
    }
    var geo_items_feats = [];
    if (geo_items_features && id in geo_items_features) {
        for (var features of geo_items_features[id]) {
            geo_items_feats.push(geojson_format.writeFeaturesObject(features, {decimals: 3}));
        }
    }
    return [base_features, geo_items_feats];
}
var print = function (id) {
    return "vector " + typeof vector_features[id] + ", cluster " + typeof point_features[id] ;
}