'use strict';

var d3 = require('@plotly/d3');

var Lib = require('../../lib');
var getTopojsonFeatures = require('../../lib/topojson_utils').getTopojsonFeatures;
var geoJsonUtils = require('../../lib/geojson_utils');
var geoUtils = require('../../lib/geo_location_utils');
var findExtremes = require('../../plots/cartesian/autorange').findExtremes;
var BADNUM = require('../../constants/numerical').BADNUM;

var calcMarkerSize = require('../scatter/calc').calcMarkerSize;
var subTypes = require('../scatter/subtypes');
var style = require('./style');

function plot(gd, geo, calcData) {
    var scatterLayer = geo.layers.frontplot.select('.scatterlayer');
    var gTraces = Lib.makeTraceGroups(scatterLayer, calcData, 'trace scattergeo');

    function removeBADNUM(d, node) {
        if(d.lonlat[0] === BADNUM) {
            d3.select(node).remove();
        }
    }

    // TODO find a way to order the inner nodes on update
    gTraces.selectAll('*').remove();

    gTraces.each(function(calcTrace) {
        var s = d3.select(this);
        var trace = calcTrace[0].trace;

        if(subTypes.hasLines(trace) || trace.fill !== 'none') {
            var lineCoords = geoJsonUtils.calcTraceToLineCoords(calcTrace);

            var lineData = (trace.fill !== 'none') ?
                geoJsonUtils.makePolygon(lineCoords) :
                geoJsonUtils.makeLine(lineCoords);

            s.selectAll('path.js-line')
                .data([{geojson: lineData, trace: trace}])
              .enter().append('path')
                .classed('js-line', true)
                .style('stroke-miterlimit', 2);
        }

        if(subTypes.hasMarkers(trace)) {
            s.selectAll('path.point')
                .data(Lib.identity)
             .enter().append('path')
                .classed('point', true)
                .each(function(calcPt) { removeBADNUM(calcPt, this); });
        }

        if(subTypes.hasText(trace)) {
            s.selectAll('g')
                .data(Lib.identity)
              .enter().append('g')
                .append('text')
                .each(function(calcPt) { removeBADNUM(calcPt, this); });
        }

        // call style here within topojson request callback
        style(gd, calcTrace);
    });
}

function calcGeoJSON(calcTrace, fullLayout) {
    var trace = calcTrace[0].trace;
    var geoLayout = fullLayout[trace.geo];
    var geo = geoLayout._subplot;
    var len = trace._length;
    var i, calcPt;

    if(Lib.isArrayOrTypedArray(trace.locations)) {
        var locationmode = trace.locationmode;
        var features = locationmode === 'geojson-id' ?
            geoUtils.extractTraceFeature(calcTrace) :
            getTopojsonFeatures(trace, geo.topojson);

        for(i = 0; i < len; i++) {
            calcPt = calcTrace[i];

            var feature = locationmode === 'geojson-id' ?
                calcPt.fOut :
                geoUtils.locationToFeature(locationmode, calcPt.loc, features);

            calcPt.lonlat = feature ? feature.properties.ct : [BADNUM, BADNUM];
        }
    }

    var opts = {padded: true};
    var lonArray;
    var latArray;

    if(geoLayout.fitbounds === 'geojson' && trace.locationmode === 'geojson-id') {
        var bboxGeojson = geoUtils.computeBbox(geoUtils.getTraceGeojson(trace));
        lonArray = [bboxGeojson[0], bboxGeojson[2]];
        latArray = [bboxGeojson[1], bboxGeojson[3]];
    } else {
        lonArray = new Array(len);
        latArray = new Array(len);
        for(i = 0; i < len; i++) {
            calcPt = calcTrace[i];
            lonArray[i] = calcPt.lonlat[0];
            latArray[i] = calcPt.lonlat[1];
        }

        opts.ppad = calcMarkerSize(trace, len);
    }

    trace._extremes.lon = findExtremes(geoLayout.lonaxis._ax, lonArray, opts);
    trace._extremes.lat = findExtremes(geoLayout.lataxis._ax, latArray, opts);
}

module.exports = {
    calcGeoJSON: calcGeoJSON,
    plot: plot
};
