import * as d3 from './d3-custom';

export function createEnvironment() {

    let skyWidth = 100;
    let skyHeight = 100;

    let streetLength = 100;
    let streetBBox = {
        min: { x: 0, y: 0 },
        max: { x: 100, y: 100 }
    };

    let vanishingPoint = { x: 100, y: 100 };

    let x = 0;
    let y = 0;

    let bgStartColour = '#ccccd8';
    let bgStopColour = '#ccc';
    let markingColour = '#f84';

    let minVal = 0.000001;

    let opacity = 0.6;

    let streetGroup = undefined;
    let defs = undefined;

    let markings = [];
    let markingUnit = 20;   // base of markings dimensions
    let markingFactor = 2;  // marking unit * factor = height


    function environment(selection) {

        defs = selection.append('defs');

        let gradient = defs.append('linearGradient')
            .attr('id', 'sky-gradient')
            .attr('x1', '0')
            .attr('x2', '0')
            .attr('y1', '0')
            .attr('y2', '1');
        gradient.append('stop')
            .attr('offset', '0%')
            .attr('stop-color', 'skyblue')
            .attr('stop-opacity', '1');
        gradient.append('stop')
            .attr('offset', '100%')
            .attr('stop-opacity', '0.25')
            .attr('stop-color', 'skyblue');

        gradient = defs.append('linearGradient')
            .attr('id', 'fog-gradient')
            .attr('x1', '0')
            .attr('x2', '0')
            .attr('y1', '0')
            .attr('y2', '1');
        gradient.append('stop')
            .attr('offset', '0%')
            .attr('stop-opacity', '0.9')
            .attr('stop-color', '#fff');
        gradient.append('stop')
            .attr('offset', '20%')
            .attr('stop-opacity', '0.1')
            .attr('stop-color', '#fff');
        gradient.append('stop')
            .attr('offset', '100%')
            .attr('stop-opacity', '0')
            .attr('stop-color', '#fff');

        // sky
        selection.append('rect')
            .attr('class', 'sky')
            .attr('x', 0)
            .attr('y', 0)
            .attr('width', skyWidth)
            .attr('height', skyHeight)
            .attr('fill', 'url(#sky-gradient)');

        d3.selectAll('.sky')
            .attr('width', skyWidth)
            .attr('height', vanishingPoint.y);

        // street surface gradient
        gradient = defs.append('linearGradient')
            .attr('id', 'surface-gradient')
            .attr('x1', '0')
            .attr('x2', '0')
            .attr('y1', '0')
            .attr('y2', '1');
        gradient.append('stop')
            .attr('offset', '0%')
            .attr('stop-opacity', '0.5')
            .attr('stop-color', bgStartColour);
        gradient.append('stop')
            .attr('offset', '100%')
            .attr('stop-color', bgStopColour);

        // calculate scale factor for positioning street markings
        let s = markingUnit * markingFactor / (streetBBox.max.y - vanishingPoint.y) *
            (streetBBox.min.x - vanishingPoint.x);
        let t = markingUnit * markingFactor / (streetBBox.max.y - vanishingPoint.y) *
            (streetBBox.max.x - vanishingPoint.x);
        let nw = streetBBox.width - Math.abs(s) - t;
        let ns = nw / streetBBox.width;

        // create elements for whole scroll area
        while (streetLength > minVal) {
            markings.push(streetLength);
            streetLength = streetLength * Math.pow(ns, markingFactor);  // gap length equals marking length
        }

        // street
        streetGroup = selection.append('g')
            .attr('class', 'street');

        streetGroup.append('path')
            .attr('class', 'surface')
            .attr('fill', 'url(#surface-gradient)');

        // street marking clipping triangle
        streetGroup
            .append('path')
            .attr('class', 'mmarking')
            .attr('fill', markingColour)
            .attr('opacity', opacity)
            .attr('clip-path', 'url(#marking-clip)');

        // street marking elements
        defs.append('clipPath')
            .attr('id', 'marking-clip');

        // street surface triangle
        d3.selectAll('.surface').attr('d'
            , 'M' + streetBBox.min.x + ',' + streetBBox.max.y
            + 'L' + streetBBox.max.x + ',' + streetBBox.max.y
            + 'L' + vanishingPoint.x + ',' + vanishingPoint.y
            + 'Z'
        );

        let streetCentre = (streetBBox.max.x - streetBBox.min.x) / 2.0;
        let mleft = streetCentre - markingUnit / 3;
        let mright = streetCentre + markingUnit / 3;
        d3.selectAll('.mmarking').attr('d'
            , 'M' + mleft + ',' + streetBBox.max.y
            + 'L' + mright + ',' + streetBBox.max.y
            + 'L' + vanishingPoint.x + ',' + vanishingPoint.y
            + 'L' + mleft + ',' + streetBBox.max.y
        );
    }

    environment.markings = function(elements) {
        if (!arguments.length) {
            return markings;
        }

        markings = elements;
        return environment;
    };

    environment.draw = function(cScale, newData) {

        var clippath = defs.select('clipPath#marking-clip');
        let gmarkings = clippath.selectAll('rect.marking')
            .data(markings);

        // UPDATE
        if(newData) {
            clippath.selectAll('rect.marking')
                .transition()
                .duration(1000)
                .attr('transform', function(d) {
                    let my = d * cScale;
                    let mr = (my / (streetBBox.height - markingUnit));  // marking unit = entry bar height
                    let mx = (1 - mr) * vanishingPoint.x + mr * streetBBox.min.x;
                    return 'translate(' + mx + ',' +
                        (my + vanishingPoint.y) + ') scale(' + mr + ',' + mr + ')';
                });
        } else {
            clippath.selectAll('rect.marking')
                .attr('transform', function(d) {
                    let my = d * cScale;
                    let mr = (my / (streetBBox.height - markingUnit));  // marking unit = entry bar height
                    let mx = (1 - mr) * vanishingPoint.x + mr * streetBBox.min.x;
                    return 'translate(' + mx + ',' +
                        (my + vanishingPoint.y) + ') scale(' + mr + ',' + mr + ')';
                });
        }

        // ENTER
        gmarkings.enter()
            .append('rect')
            .attr('class', 'marking')
            .attr('fill', '#333')
            .attr('height', markingUnit * markingFactor)
            .attr('width', streetBBox.width)
            .attr('transform', function(d) {
                let my = d * cScale;
                let mr = (my / (streetBBox.max.y - streetBBox.min.y - markingUnit));
                let mx = (1 - mr) * vanishingPoint.x + mr * streetBBox.min.x;
                return 'translate(' + mx + ',' +
                    (my + vanishingPoint.y) + ') scale(' + mr + ',' + mr + ')';
            });
    };

    environment.streetLength = function(val) {
        if(!arguments.length) {
            return streetLength;
        }
        streetLength = val;
        return environment;
    };

    environment.streetBBox = function(val) {
        if(!arguments.length) {
            return streetBBox;
        }
        streetBBox = val;
        return environment;
    };

    environment.vanishingPoint = function(val) {
        if(!arguments.length) {
            return vanishingPoint;
        }
        vanishingPoint = val;
        return environment;
    };

    environment.markingFactor = function(val) {
        if(!arguments.length) {
            return markingFactor;
        }
        markingFactor = val;
        return environment;
    };

    environment.markingUnit = function(val) {
        if(!arguments.length) {
            return markingUnit;
        }
        markingUnit = val;
        return environment;
    };

    environment.markingColour = function(val) {
        if(!arguments.length) {
            return markingColour;
        }
        markingColour = val;
        return environment;
    };

    environment.minVal = function(val) {
        if(!arguments.length) {
            return minVal;
        }
        minVal = val;
        return environment;
    };

    environment.bgStartColour = function(val) {
        if(!arguments.length) {
            return bgStartColour;
        }
        bgStartColour = val;
        return environment;
    };

    environment.bgStopColour = function(val) {
        if(!arguments.length) {
            return bgStopColour;
        }
        bgStopColour = val;
        return environment;
    };

    environment.x = function(val) {
        if(!arguments.length) {
            return x;
        }
        x = val;
        return environment;
    };

    environment.y = function(val) {
        if(!arguments.length) {
            return y;
        }
        y = val;
        return environment;
    };

    environment.skyWidth = function(val) {
        if(!arguments.length) {
            return skyWidth;
        }
        skyWidth = val;
        return environment;
    };

    environment.skyHeight = function(val) {
        if(!arguments.length) {
            return skyHeight;
        }
        skyHeight = val;
        return environment;
    };

    return environment;
}
