import * as d3 from 'd3';
import _ from 'lodash';
import {
    svgOneWaySegments,
    svgPath,
    svgRelationMemberTags,
    svgTagClasses
} from './index';

import { osmEntity, osmSimpleMultipolygonOuterMember } from '../osm';
import { utilDetect } from '../util/detect';


export function svgLines(projection, context) {
    var detected = utilDetect();

    var highway_stack = {
        motorway: 0,
        motorway_link: 1,
        trunk: 2,
        trunk_link: 3,
        primary: 4,
        primary_link: 5,
        secondary: 6,
        tertiary: 7,
        unclassified: 8,
        residential: 9,
        service: 10,
        footway: 11
    };


    function drawLines(selection, graph, entities, filter) {


        function waystack(a, b) {
            var selected = context.selectedIDs(),
                scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0,
                scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;

            if (a.tags.highway) { scoreA -= highway_stack[a.tags.highway]; }
            if (b.tags.highway) { scoreB -= highway_stack[b.tags.highway]; }
            return scoreA - scoreB;
        }


        function drawLineGroup(selection, klass, isSelected) {
            var lines = selection
                .selectAll('path')
                .filter(filter)
                .data(getPathData(isSelected), osmEntity.key);

            lines.exit()
                .remove();

            // Optimization: call simple TagClasses only on enter selection. This
            // works because osmEntity.key is defined to include the entity v attribute.
            lines.enter()
                .append('path')
                .attr('class', function(d) {
                    return 'way line ' + klass + ' ' + d.id + (isSelected ? ' selected' : '') +
                        (oldMultiPolygonOuters[d.id] ? ' old-multipolygon' : '');
                })
                .call(svgTagClasses())
                .merge(lines)
                .sort(waystack)
                .attr('d', getPath)
                .call(svgTagClasses().tags(svgRelationMemberTags(graph)));

            return selection;
        }


        function getPathData(isSelected) {
            return function() {
                var layer = this.parentNode.__data__;
                var data = pathdata[layer] || [];
                return data.filter(function(d) {
                    if (isSelected)
                        return context.selectedIDs().indexOf(d.id) !== -1;
                    else
                        return context.selectedIDs().indexOf(d.id) === -1;
                });
            };
        }


        var getPath = svgPath(projection, graph),
            ways = [],
            pathdata = {},
            onewaydata = {},
            oldMultiPolygonOuters = {};

        for (var i = 0; i < entities.length; i++) {
            var entity = entities[i],
                outer = osmSimpleMultipolygonOuterMember(entity, graph);
            if (outer) {
                ways.push(entity.mergeTags(outer.tags));
                oldMultiPolygonOuters[outer.id] = true;
            } else if (entity.geometry(graph) === 'line') {
                ways.push(entity);
            }
        }

        ways = ways.filter(getPath);
        pathdata = _.groupBy(ways, function(way) { return way.layer(); });

        _.forOwn(pathdata, function(v, k) {
            onewaydata[k] = _(v)
                .filter(function(d) { return d.isOneWay(); })
                .map(svgOneWaySegments(projection, graph, 35))
                .flatten()
                .valueOf();
        });


        var layer = selection.selectAll('.layer-lines');

        var layergroup = layer
            .selectAll('g.layergroup')
            .data(d3.range(-10, 11));

        layergroup = layergroup.enter()
            .append('g')
            .attr('class', function(d) { return 'layergroup layer' + String(d); })
            .merge(layergroup);

        layergroup
            .selectAll('g.linegroup')
            .data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted'])
            .enter()
            .append('g')
            .attr('class', function(d) { return 'linegroup line-' + d; });


        layergroup.selectAll('g.line-shadow')
            .call(drawLineGroup, 'shadow', false);
        layergroup.selectAll('g.line-casing')
            .call(drawLineGroup, 'casing', false);
        layergroup.selectAll('g.line-stroke')
            .call(drawLineGroup, 'stroke', false);

        layergroup.selectAll('g.line-shadow-highlighted')
            .call(drawLineGroup, 'shadow', true);
        layergroup.selectAll('g.line-casing-highlighted')
            .call(drawLineGroup, 'casing', true);
        layergroup.selectAll('g.line-stroke-highlighted')
            .call(drawLineGroup, 'stroke', true);


        var onewaygroup = layergroup
            .selectAll('g.onewaygroup')
            .data(['oneway']);

        onewaygroup = onewaygroup.enter()
            .append('g')
            .attr('class', 'onewaygroup')
            .merge(onewaygroup);

        var oneways = onewaygroup
            .selectAll('path')
            .filter(filter)
            .data(
                function() { return onewaydata[this.parentNode.__data__] || []; },
                function(d) { return [d.id, d.index]; }
            );

        oneways.exit()
            .remove();

        oneways = oneways.enter()
            .append('path')
            .attr('class', 'oneway')
            .attr('marker-mid', 'url(#oneway-marker)')
            .merge(oneways)
            .attr('d', function(d) { return d.d; });

        if (detected.ie) {
            oneways.each(function() { this.parentNode.insertBefore(this, this); });
        }
    }


    return drawLines;
}
