import { select as d3_select } from 'd3-selection';
import { geoProjection as d3_geoProjection } from 'd3-geo';

describe('iD.svgLines', function () {
    var context, surface;
    var all = function() { return true; };
    var none = function() { return false; };
    var projection = d3_geoProjection(function(x, y) { return [x, -y]; })
        .translate([0, 0])
        .scale(iD.geoZoomToScale(17))
        .clipExtent([[0, 0], [Infinity, Infinity]]);


    beforeEach(function () {
        context = iD.coreContext().assetPath('../dist/').init();
        d3_select(document.createElement('div'))
            .attr('class', 'main-map')
            .call(context.map().centerZoom([0, 0], 17));
        surface = context.surface();
    });


    it('adds way and line classes', function () {
        var a = new iD.osmNode({loc: [0, 0]});
        var b = new iD.osmNode({loc: [1, 1]});
        var line = new iD.osmWay({nodes: [a.id, b.id]});
        var graph = new iD.coreGraph([a, b, line]);

        surface.call(iD.svgLines(projection, context), graph, [line], all);

        expect(surface.select('path.way').classed('way')).toBe(true);
        expect(surface.select('path.line').classed('line')).toBe(true);
    });

    it('adds relation and area classes for untagged line member of multipolygon', function () {
        var a = new iD.osmNode({loc: [0, 0]});
        var b = new iD.osmNode({loc: [1, 1]});
        var line = new iD.osmWay({nodes: [a.id, b.id]});
        var relation = new iD.osmRelation({members: [{id: line.id}], tags: {type: 'multipolygon', natural: 'wood'}});
        var graph = new iD.coreGraph([a, b, line, relation]);

        surface.call(iD.svgLines(projection, context), graph, [line], all);

        expect(surface.select('.stroke').classed('relation')).toBe(true);
        expect(surface.select('.stroke').classed('area')).toBe(true);
    });

    it('adds tag classes', function () {
        var a = new iD.osmNode({loc: [0, 0]});
        var b = new iD.osmNode({loc: [1, 1]});
        var line = new iD.osmWay({nodes: [a.id, b.id], tags: {highway: 'residential'}});
        var graph = new iD.coreGraph([a, b, line]);

        surface.call(iD.svgLines(projection, context), graph, [line], all);

        expect(surface.select('.line').classed('tag-highway')).toBe(true);
        expect(surface.select('.line').classed('tag-highway-residential')).toBe(true);
    });

    it('adds stroke classes for the tags of the parent relation of multipolygon members', function() {
        var a = new iD.osmNode({loc: [0, 0]});
        var b = new iD.osmNode({loc: [1, 1]});
        var line = new iD.osmWay({nodes: [a.id, b.id]});
        var relation = new iD.osmRelation({members: [{id: line.id}], tags: {type: 'multipolygon', natural: 'wood'}});
        var graph = new iD.coreGraph([a, b, line, relation]);

        surface.call(iD.svgLines(projection, context), graph, [line], all);

        expect(surface.select('.stroke').classed('tag-natural-wood')).toBe(true);
    });

    describe('z-indexing', function() {
        var graph = new iD.coreGraph([
            new iD.osmNode({id: 'a', loc: [0, 0]}),
            new iD.osmNode({id: 'b', loc: [1, 1]}),
            new iD.osmNode({id: 'c', loc: [0, 0]}),
            new iD.osmNode({id: 'd', loc: [1, 1]}),
            new iD.osmWay({id: 'lo', tags: {highway: 'residential', layer: '0'}, nodes: ['a', 'b']}),
            new iD.osmWay({id: 'hi', tags: {highway: 'residential', layer: '1'}, nodes: ['c', 'd']})
        ]);

        it('stacks higher lines above lower ones in a single render', function () {
            surface.call(iD.svgLines(projection, context), graph, [graph.entity('lo'), graph.entity('hi')], none);

            var selection = surface.selectAll('g.line-stroke > path.line');
            expect(selection.nodes()[0].__data__.id).toEqual('lo');
            expect(selection.nodes()[1].__data__.id).toEqual('hi');
        });

        it('stacks higher lines above lower ones in a single render (reverse)', function () {
            surface.call(iD.svgLines(projection, context), graph, [graph.entity('hi'), graph.entity('lo')], none);

            var selection = surface.selectAll('g.line-stroke > path.line');
            expect(selection.nodes()[0].__data__.id).toEqual('lo');
            expect(selection.nodes()[1].__data__.id).toEqual('hi');
        });

        it('stacks higher lines above lower ones in separate renders', function () {
            surface.call(iD.svgLines(projection, context), graph, [graph.entity('lo')], none);
            surface.call(iD.svgLines(projection, context), graph, [graph.entity('hi')], none);

            var selection = surface.selectAll('g.line-stroke > path.line');
            expect(selection.nodes()[0].__data__.id).toEqual('lo');
            expect(selection.nodes()[1].__data__.id).toEqual('hi');
        });

        it('stacks higher lines above lower in separate renders (reverse)', function () {
            surface.call(iD.svgLines(projection, context), graph, [graph.entity('hi')], none);
            surface.call(iD.svgLines(projection, context), graph, [graph.entity('lo')], none);

            var selection = surface.selectAll('g.line-stroke > path.line');
            expect(selection.nodes()[0].__data__.id).toEqual('lo');
            expect(selection.nodes()[1].__data__.id).toEqual('hi');
        });
    });

    it('rounds layers down to the nearest whole number for rendering', () => {
        const graph = new iD.coreGraph([
            new iD.osmNode({id: 'a', loc: [0, 0]}),
            new iD.osmNode({id: 'b', loc: [1, 1]}),
            new iD.osmWay({id: 'w1', tags: {highway: 'residential', layer: '-2.5'}, nodes: ['a', 'b']}),
        ]);
        surface.call(iD.svgLines(projection, context), graph, [graph.entity('w1')], none);

        const layerGroup = surface.select('path.w1').nodes()[0].parentNode.parentNode;

        // the feature with layer=-2.5 was rendered in layer -2
        expect(layerGroup.className.baseVal).toEqual('layergroup layer-2');
    });

    describe('oneway-markers', function() {
        it('has marker layer for oneway ways', function() {
            // use 1e-2 to make sure segments are long enough to get
            // markers, but not so long that they get split and have
            // multiple marker segments.
            var a = new iD.osmNode({id: 'a', loc: [0, 0]});
            var b = new iD.osmNode({id: 'b', loc: [1e-2, 0]});
            var c = new iD.osmNode({id: 'c', loc: [0, 1e-2]});

            var i_o = new iD.osmWay({id: 'implied-oneway', tags: {waterway: 'stream'}, nodes: [a.id, b.id]});
            var e_o = new iD.osmWay({id: 'explicit-oneway', tags: {highway: 'residential', oneway: 'yes'}, nodes: [a.id, c.id]});
            var e_b = new iD.osmWay({id: 'explicit-backwards', tags: {highway: 'residential', oneway: '-1'}, nodes: [b.id, c.id]});

            var graph = new iD.coreGraph([a, b, c, i_o, e_o, e_b]);

            surface.call(iD.svgLines(projection, context), graph, [i_o, e_o, e_b], all);

            var selection = surface.selectAll('g.onewaygroup > path');

            expect(selection.size()).toEqual(3);
            expect(selection.nodes()[0].attributes['marker-mid'].nodeValue).toEqual('url(#ideditor-oneway-marker-black)');
            expect(selection.nodes()[1].attributes['marker-mid'].nodeValue).toEqual('url(#ideditor-oneway-marker-black)');
            expect(selection.nodes()[2].attributes['marker-mid'].nodeValue).toEqual('url(#ideditor-oneway-marker-black)');
        });

        it('has two marker layers for alternating oneway ways', function() {
            var a = new iD.osmNode({id: 'a', loc: [0, 0]});
            var b = new iD.osmNode({id: 'b', loc: [1e-2, 0]});

            var e_a = new iD.osmWay({id: 'explicit-alternating', tags: {highway: 'residential', oneway: 'alternating'}, nodes: [a.id, b.id]});

            var graph = new iD.coreGraph([a, b, e_a]);

            surface.call(iD.svgLines(projection, context), graph, [e_a], all);

            var selection = surface.selectAll('g.onewaygroup > path');
            expect(selection.size()).toEqual(2);
            expect(selection.nodes()[0].attributes['marker-mid'].nodeValue).toEqual('url(#ideditor-oneway-marker-black)');
            expect(selection.nodes()[1].attributes['marker-mid'].nodeValue).toEqual('url(#ideditor-oneway-marker-black)');
        });

        it('has no marker layer for oneway=no ways', function() {
            var a = new iD.osmNode({id: 'a', loc: [0, 0]});
            var b = new iD.osmNode({id: 'b', loc: [1e-2, 0]});
            var c = new iD.osmNode({id: 'c', loc: [0, 1e-2]});

            var e_no = new iD.osmWay({id: 'explicit-no-oneway', tags: {highway: 'residential', oneway: 'no'}, nodes: [a.id, b.id]});
            var i_no = new iD.osmWay({id: 'implied-no-oneway', tags: {highway: 'residential' }, nodes: [a.id, c.id]});

            var graph = new iD.coreGraph([a, b, c, e_no, i_no]);

            surface.call(iD.svgLines(projection, context), graph, [i_no, e_no], all);
            var selection = surface.selectAll('g.onewaygroup > path');
            expect(selection.empty()).toBe(true);
        });
    });

    describe('sided-markers', function() {
        it('has marker layer for sided way', function() {
            var a = new iD.osmNode({id: 'a', loc: [0, 0]});
            var b = new iD.osmNode({id: 'b', loc: [1e-2, 0]});
            var c = new iD.osmNode({id: 'c', loc: [0, 1e-2]});
            var d = new iD.osmNode({id: 'd', loc: [1e-2, 1e-2]});

            var i_n = new iD.osmWay({id: 'implied-natural', tags: {natural: 'cliff'}, nodes: [a.id, b.id]});
            var i_nc = new iD.osmWay({id: 'implied-coastline', tags: {natural: 'coastline'}, nodes: [a.id, c.id]});
            var i_b = new iD.osmWay({id: 'implied-barrier', tags: {barrier: 'city_wall'}, nodes: [a.id, d.id]});
            var i_mm = new iD.osmWay({id: 'implied-man_made', tags: {man_made: 'quay'}, nodes: [b.id, c.id]});

            var graph = new iD.coreGraph([a, b, c, d, i_n, i_nc, i_b, i_mm]);

            surface.call(iD.svgLines(projection, context), graph, [i_n, i_nc, i_b, i_mm], all);
            var selection = surface.selectAll('g.sidedgroup > path');
            expect(selection.size()).toEqual(4);
            expect(selection.nodes()[0].attributes['marker-mid'].nodeValue).toEqual('url(#ideditor-sided-marker-natural)');
            expect(selection.nodes()[1].attributes['marker-mid'].nodeValue).toEqual('url(#ideditor-sided-marker-coastline)');
            expect(selection.nodes()[2].attributes['marker-mid'].nodeValue).toEqual('url(#ideditor-sided-marker-barrier)');
            expect(selection.nodes()[3].attributes['marker-mid'].nodeValue).toEqual('url(#ideditor-sided-marker-man_made)');
        });

        it('has no marker layer for two_sided way', function() {
            var a = new iD.osmNode({id: 'a', loc: [0, 0]});
            var b = new iD.osmNode({id: 'b', loc: [1e-2, 0]});

            var e_ts = new iD.osmWay({id: 'explicit-two-sided', tags: {barrier: 'city_wall', two_sided: 'yes'}, nodes: [a.id, b.id]});

            var graph = new iD.coreGraph([a, b, e_ts]);

            surface.call(iD.svgLines(projection, context), graph, [e_ts], all);
            var selection = surface.selectAll('g.sidedgroup > path');
            expect(selection.empty()).toBe(true);
        });
    });
});
