# src/layers/canvasOverlay.js

    import Overlayer from './overlay';

    /**
     * initCanvasOverlayer based on mapboxgl-canvas
     */
    export class CanvasOverlayer extends Overlayer {
        constructor(opts) {
            let _opts = opts || {};
            super(_opts);
            this.canvas = this._init();
            this.redraw = _redraw.bind(this);
            // how to deconstruct opts to this if we need defaultValue.
            this.labelOn = _opts.labelOn || false;
            this.xfield = _opts.xfield || 'lon';
            this.yfield = _opts.yfield || 'lat';
            this.shadow = _opts.shadow != undefined? _opts.shadow : false;
            this.lineColor = _opts.lineColor;
            this.blurWidth = _opts.blurWidth != undefined? _opts.blurWidth: 4;
            this.keepTrack = _opts.keepTrack != undefined? _opts.keepTrack : false;
            if (this.keepTrack) {
                // create trackLayer to render history track lines..
                this.trackLayer = this._init();
                this._initTrackCtx();
            }
            this.tracks  = [];
            this.initTrackCtx = this._initTrackCtx.bind(this);
            if (_opts && _opts.map) {
                this.setMap(_opts.map);
                // 绑定每次move 都重绘doms..
                _opts.map.on("move", () => {
                    this.redrawTrack();
                });
            }
        }

        _init(shadow=false,keepTrack=false) {
            let canvasContainer = this.map._canvasContainer,
                mapboxCanvas = this.map._canvas,
                canvasOverlay = document.createElement("canvas");
            canvasOverlay.style.position = "absolute";
            canvasOverlay.className = "overlay-canvas";
            canvasOverlay.width = parseInt(mapboxCanvas.style.width);
            canvasOverlay.height = parseInt(mapboxCanvas.style.height);
            canvasContainer.appendChild(canvasOverlay);
            return canvasOverlay;
        }

        /**
         * init track ctx for each track segment rendering..
         */
        _initTrackCtx() {
            if(this.trackLayer) {
                this.trackCtx = this.trackLayer.getContext("2d");
                this.movedTo = false;
                initCtx(this.trackCtx, this.blurWidth,"rgba(255,255,255,.4");
                this.trackCtx.lineWidth = this.lineWidth || 3;
                this.trackCtx.strokeStyle = this.lineColor || "rgba(255,255,20,.6)";
                this.trackCtx.beginPath();
            }
        }

        /**
         * set tracks coordinates of overlayer.
         * @param {*array of track points.} tracks
         */
        setTracks(tracks) {
            if (Array.isArray(tracks)) {
                this.tracks = tracks;
                return this;
            }
        }

        getTracks() {
            return this.tracks;
        }

        /**
         * render cached tracks to line when map moved..
         */
        redrawTrack() {
            if(this.trackCtx && this.tracks && this.tracks.length > 0) {
                let pix = [0, 0];
                this.trackCtx.clearRect(0,0,this.trackLayer.width, this.trackLayer.height);
                this.trackCtx.beginPath();
                pix = this.lnglat2pix(this.tracks[0][0], this.tracks[0][1]);
                this.trackCtx.moveTo(pix[0], pix[1]);
                for(let i = 1; i < this.tracks.length; i ++) {
                    pix = this.lnglat2pix(this.tracks[i][0], this.tracks[i][1]);
                    this.trackCtx.lineTo(pix[0], pix[1]);
                }
                this.trackCtx.stroke();
            }
        }
    }

    function _preSetCtx(context) {
    //默认值为source-over
        let prev = context.globalCompositeOperation;
        //只显示canvas上原图像的重叠部分 source-in, source, destination-in
        context.globalCompositeOperation = 'destination-in';
        //设置主canvas的绘制透明度
        context.globalAlpha = 0.95;
        //这一步目的是将canvas上的图像变的透明
        // context.fillStyle = "rgba(0,0,0,.95)";
        context.fillRect(0, 0, context.canvas.width, context.canvas.height);
        //在原图像上重叠新图像
        context.globalCompositeOperation = prev;
    }

    const iconSize = 32;
    /**
     * expoid this method, can be overwritten
     * for special render requirements..
     * Important ! redraw may use this.map as projector!
     * @param: keepLog, keep render Sprites location log..
     */
    function _redraw(objs) {
        if (this.canvas) {
            let ctx = this.canvas.getContext("2d");
            // ctx.clearRect(0,0,canv.width, canv.height);
            if (this.shadow) {
                _preSetCtx(ctx);
                ctx.save();
            } else {
                ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
            }
            initCtx(ctx,this.blurWidth,"rgba(255,255,255,.4");
            for(let i=0;i<objs.length;i++) {
                let x = objs[i][this.xfield], y = objs[i][this.yfield],
                    radius = objs[i]['radius'] || 3, icon = objs[i]['icon'],
                    label = objs[i]['name'], rotate = objs[i]['direction'] || 0;
                radius = Math.abs(radius);
                let pix = this.lnglat2pix(x, y);
                if (pix == null) continue;
                ctx.fillStyle = objs[i]['color'] || 'rgba(255,240,4,.9)';
                ctx.beginPath();
                if (label !== undefined && label.startsWith("Play")) radius = iconSize*0.75;
                // icon: ImageUrl/CanvasFunction..., clip part of img sometimes...
                if (icon !== undefined) {
                    ctx.save();
                    ctx.translate(pix[0], pix[1]);
                    ctx.rotate(rotate*Math.PI/180);
                    let min = icon.height > icon.width ? icon.width : icon.height;
                    try {
                        ctx.drawImage(icon,0,0,min,min, -iconSize/2, -iconSize/2, iconSize, iconSize);
                    }
                    catch (e) {
                        console.warn("ctx.drawImage.. error.");
                    }
                    ctx.restore();
                } else {
                    ctx.arc(pix[0], pix[1], radius, 0, Math.PI*2);
                    ctx.fill();
                }
                if (this.keepTrack && this.tracks.length == 0) {
                    this.initTrackCtx();
                    this.trackCtx.moveTo(pix[0],pix[1]);
                    this.tracks.push([x, y]);
                    // this.movedTo = true;
                } else if (this.trackCtx) {
                    this.trackCtx.lineTo(pix[0],pix[1]);
                    this.tracks.push([x, y]);
                    setTimeout(()=>{
                        //// closePath would auto-complete the path to polygon..
                        this.trackCtx.stroke();
                        this.trackCtx.beginPath();
                        this.trackCtx.moveTo(pix[0],pix[1]);
                    }, 0);
                }
                if (label !== undefined && this.labelOn) {
                    ctx.strokeText(label, pix[0], pix[1]);
                }
                // ctx.closePath();
            }
            if(this.shadow) {
                ctx.restore();
            }
        }
    }

    function initCtx(ctx, blurWidth, shadowColor="rgba(255,255,255,.8)") {
        if (ctx === undefined) return;
        ctx.linecap = 'round';
        ctx.shadowBlur = blurWidth;
        ctx.shadowColor = shadowColor;
        ctx.strokeStyle = "rgba(255,255,255,.9)";
        ctx.fillStyle = "rgba(255,240,91,.8)";
    }

    /**
     * draw tri on canvas by center and rotation..
     * @param rotate: degree number,
     * @param radius: number, tri radius..
     *      /\  default beta angle is 30 degree.
     *     /  \
     *    /____\
     * draw triangle
     */
    function drawTri(ctx, coord, rotate, radius=iconSize/2, beta=30) {
        // calc the head point of triangle.
        let headPoint = [undefined, undefined], tailPoint = [undefined, undefined],
            rad = rotate*Math.PI/180;
        headPoint[0] = coord[0] + Math.cos(rad)*radius;
        headPoint[1] = coord[1] + Math.sin(rad)*radius;
        tailPoint[0] = coord[0] - Math.cos(rad)*radius;
        tailPoint[1] = coord[1] - Math.sin(rad)*radius;
        let rot = (rotate - beta/2),
            rPoint = [undefined, undefined];
        rPoint[0] = Math.cos(rot*Math.PI/180);

        ctx.lineTo(headPoint);
    }