(function (root, factory) {  
    if (typeof exports === 'object') {  
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {  
        define(factory);  
    } else {
        root.BMapLib = root.BMapLib || {};
        root.BMapLib.LuShu = root.BMapLib.Lushu || factory();  
    }  
})(this, function() {
    var baidu = {};
    baidu.dom = {};
    baidu.dom.g = function(id) {
        if ('string' == typeof id || id instanceof String) {
            return document.getElementById(id);
        } else if (id && id.nodeName && (id.nodeType == 1 || id.nodeType == 9)) {
            return id;
        }
        return null;
    };
    baidu.g = baidu.G = baidu.dom.g;
    baidu.lang = baidu.lang || {};
    baidu.lang.isString = function(source) {
        return '[object String]' == Object.prototype.toString.call(source);
    };
    baidu.isString = baidu.lang.isString;
    baidu.dom._g = function(id) {
        if (baidu.lang.isString(id)) {
            return document.getElementById(id);
        }
        return id;
    };
    baidu._g = baidu.dom._g;
    baidu.dom.getDocument = function(element) {
        element = baidu.dom.g(element);
        return element.nodeType == 9 ? element : element.ownerDocument || element.document;
    };
    baidu.browser = baidu.browser || {};
    baidu.browser.ie = baidu.ie = /msie (\d+\.\d+)/i.test(navigator.userAgent) ? (document.documentMode || + RegExp['\x241']) : undefined;
    baidu.dom.getComputedStyle = function(element, key) {
        element = baidu.dom._g(element);
        var doc = baidu.dom.getDocument(element),
            styles;
        if (doc.defaultView && doc.defaultView.getComputedStyle) {
            styles = doc.defaultView.getComputedStyle(element, null);
            if (styles) {
                return styles[key] || styles.getPropertyValue(key);
            }
        }
        return '';
    };
    baidu.dom._styleFixer = baidu.dom._styleFixer || {};
    baidu.dom._styleFilter = baidu.dom._styleFilter || [];
    baidu.dom._styleFilter.filter = function(key, value, method) {
        for (var i = 0, filters = baidu.dom._styleFilter, filter; filter = filters[i]; i++) {
            if (filter = filter[method]) {
                value = filter(key, value);
            }
        }
        return value;
    };
    baidu.string = baidu.string || {};
    baidu.string.toCamelCase = function(source) {

        if (source.indexOf('-') < 0 && source.indexOf('_') < 0) {
            return source;
        }
        return source.replace(/[-_][^-_]/g, function(match) {
            return match.charAt(1).toUpperCase();
        });
    };
    baidu.dom.getStyle = function(element, key) {
        var dom = baidu.dom;
        element = dom.g(element);
        key = baidu.string.toCamelCase(key);

        var value = element.style[key] ||
                    (element.currentStyle ? element.currentStyle[key] : '') ||
                    dom.getComputedStyle(element, key);

        if (!value) {
            var fixer = dom._styleFixer[key];
            if (fixer) {
                value = fixer.get ? fixer.get(element) : baidu.dom.getStyle(element, fixer);
            }
        }

        if (fixer = dom._styleFilter) {
            value = fixer.filter(key, value, 'get');
        }
        return value;
    };
    baidu.getStyle = baidu.dom.getStyle;
    baidu.dom._NAME_ATTRS = (function() {
        var result = {
            'cellpadding': 'cellPadding',
            'cellspacing': 'cellSpacing',
            'colspan': 'colSpan',
            'rowspan': 'rowSpan',
            'valign': 'vAlign',
            'usemap': 'useMap',
            'frameborder': 'frameBorder'
        };

        if (baidu.browser.ie < 8) {
            result['for'] = 'htmlFor';
            result['class'] = 'className';
        } else {
            result['htmlFor'] = 'for';
            result['className'] = 'class';
        }

        return result;
    })();
    baidu.dom.setAttr = function(element, key, value) {
        element = baidu.dom.g(element);
        if ('style' == key) {
            element.style.cssText = value;
        } else {
            key = baidu.dom._NAME_ATTRS[key] || key;
            element.setAttribute(key, value);
        }
        return element;
    };
    baidu.setAttr = baidu.dom.setAttr;
    baidu.dom.setAttrs = function(element, attributes) {
        element = baidu.dom.g(element);
        for (var key in attributes) {
            baidu.dom.setAttr(element, key, attributes[key]);
        }
        return element;
    };
    baidu.setAttrs = baidu.dom.setAttrs;
    baidu.dom.create = function(tagName, opt_attributes) {
        var el = document.createElement(tagName),
            attributes = opt_attributes || {};
        return baidu.dom.setAttrs(el, attributes);
    };
    baidu.object = baidu.object || {};
    baidu.extend =
    baidu.object.extend = function(target, source) {
        for (var p in source) {
            if (source.hasOwnProperty(p)) {
                target[p] = source[p];
            }
        }
        return target;
    };

    /**
     * @exports LuShu as BMapLib.LuShu
     */
    var LuShu = function(map, path, opts) {
        try {
            BMap;
        } catch (e) {
            throw Error('Baidu Map JS API is not ready yet!');
        }
        if (!path || path.length < 1) {
            return;
        }
        this._map = map;
        this._path = path;
        this.i = 0;
        this._setTimeoutQuene = [];
        this._projection = this._map.getMapType().getProjection();
        this._opts = {
            icon: null,
            speed: 4000,
            defaultContent: '',
            showInfoWindow: false
        };
        this._setOptions(opts);
        this._rotation = 0;

        if (!this._opts.icon instanceof BMap.Icon) {
            this._opts.icon = defaultIcon;
        }
    }
    LuShu.prototype._setOptions = function(opts) {
        if (!opts) {
            return;
        }
        for (var p in opts) {
            if (opts.hasOwnProperty(p)) {
                this._opts[p] = opts[p];
            }
        }
    }
    LuShu.prototype.start = function() {
        var me = this,
            len = me._path.length;
        this._opts.onstart && this._opts.onstart(me)
        if (me.i && me.i < len - 1) {
            if (!me._fromPause) {
                return;
            }else if(!me._fromStop){
	            me._moveNext(++me.i);
            }
        }else {
            !me._marker && me._addMarker();
            me._timeoutFlag = setTimeout(function() {
                    !me._overlay && me._addInfoWin();
                    me._moveNext(me.i);
            },400);
        }
        this._fromPause = false;
        this._fromStop = false;
    },
    LuShu.prototype.stop = function() {
        this.i = 0;
        this._fromStop = true;
        clearInterval(this._intervalFlag);
        this._clearTimeout();
        for (var i = 0, t = this._opts.landmarkPois, len = t.length; i < len; i++) {
            t[i].bShow = false;
        }
        this._opts.onstop && this._opts.onstop(this)
    };
    LuShu.prototype.pause = function() {
        clearInterval(this._intervalFlag);
        this._fromPause = true;
        this._clearTimeout();
        this._opts.onpause && this._opts.onpause(this)
    };
    LuShu.prototype.hideInfoWindow = function() {
        this._opts.showInfoWindow = false;
        this._overlay && (this._overlay._div.style.visibility = 'hidden');
    };
    LuShu.prototype.showInfoWindow = function() {
        this._opts.showInfoWindow = true;
        this._overlay && (this._overlay._div.style.visibility = 'visible');
    };
    LuShu.prototype.dispose = function () {
        clearInterval(this._intervalFlag);
        this._setTimeoutQuene && this._clearTimeout();
        if (this._map) {
            this._map.removeOverlay(this._overlay);
            this._map.removeOverlay(this._marker);
        }
    };
    baidu.object.extend(LuShu.prototype, {
        _addMarker: function(callback) {
            if (this._marker) {
                this.stop();
                this._map.removeOverlay(this._marker);
                clearTimeout(this._timeoutFlag);
            }
            this._overlay && this._map.removeOverlay(this._overlay);
            var marker = new BMap.Marker(this._path[0]);
            this._opts.icon && marker.setIcon(this._opts.icon);
            this._map.addOverlay(marker);
            marker.setAnimation(BMAP_ANIMATION_DROP);
            this._marker = marker;
        },
        _addInfoWin: function() {
            var me = this;
            !CustomOverlay.prototype.initialize && initCustomOverlay();
            var overlay = new CustomOverlay(me._marker.getPosition(), me._opts.defaultContent);
            overlay.setRelatedClass(this);
            this._overlay = overlay;
            this._map.addOverlay(overlay);
            this._opts.showInfoWindow ? this.showInfoWindow() : this.hideInfoWindow()
        },
        _getMercator: function(poi) {
            return this._map.getMapType().getProjection().lngLatToPoint(poi);
        },
        _getDistance: function(pxA, pxB) {
            return Math.sqrt(Math.pow(pxA.x - pxB.x, 2) + Math.pow(pxA.y - pxB.y, 2));
        },
        _move: function(initPos,targetPos,effect) {
            var me = this,
                currentCount = 0,
                timer = 10,
                step = this._opts.speed / (1000 / timer),
                init_pos = this._projection.lngLatToPoint(initPos),
                target_pos = this._projection.lngLatToPoint(targetPos),
                count = Math.round(me._getDistance(init_pos, target_pos) / step);
            if (count < 1) {
                me._moveNext(++me.i);
                return;
            }
            me._intervalFlag = setInterval(function() {
	            if (currentCount >= count) {
	                clearInterval(me._intervalFlag);
		        	if(me.i > me._path.length){
						return;
		        	}
	                me._moveNext(++me.i);
	            } else {
                    currentCount++;
                    var x = effect(init_pos.x, target_pos.x, currentCount, count),
                        y = effect(init_pos.y, target_pos.y, currentCount, count),
                        pos = me._projection.pointToLngLat(new BMap.Pixel(x, y));
                    if(currentCount == 1){
                        var proPos = null;
                        if(me.i - 1 >= 0){
                            proPos = me._path[me.i - 1];
                        }
                        if(me._opts.enableRotation == true){
                            me.setRotation(proPos,initPos,targetPos);
                        }
                        if(me._opts.autoView){
                            if(!me._map.getBounds().containsPoint(pos)){
                                me._map.setCenter(pos);
                            }   
                        }
                    }
                    me._marker.setPosition(pos);
                    me._setInfoWin(pos);
                }
	        },timer);
        },
        setRotation : function(prePos,curPos,targetPos){
            var me = this;
            var deg = 0;
            //start!
            curPos =  me._map.pointToPixel(curPos);
            targetPos =  me._map.pointToPixel(targetPos);   

            if(targetPos.x != curPos.x){
                var tan = (targetPos.y - curPos.y)/(targetPos.x - curPos.x),
                atan  = Math.atan(tan);
                deg = atan*360/(2*Math.PI);
                //degree  correction;
                if(targetPos.x < curPos.x){
                    deg = -deg + 90 + 90;

                } else {
                    deg = -deg;
                }

                me._marker.setRotation(-deg);   

            }else {
                var disy = targetPos.y- curPos.y ;
                var bias = 0;
                if(disy > 0)
                    bias=-1
                else
                    bias = 1
                me._marker.setRotation(-bias * 90);  
            }
            return;
        },
        linePixellength : function(from,to){ 
            return Math.sqrt(Math.abs(from.x- to.x) * Math.abs(from.x- to.x) + Math.abs(from.y- to.y) * Math.abs(from.y- to.y) );
        },
        pointToPoint : function(from,to ){
            return Math.abs(from.x- to.x) * Math.abs(from.x- to.x) + Math.abs(from.y- to.y) * Math.abs(from.y- to.y)
        },
        _moveNext: function(index) {
            var me = this;
            if (index < this._path.length - 1) {
                me._move(me._path[index], me._path[index + 1], me._tween.linear);
            } else {
                me.stop()
            }
        },
        _setInfoWin: function(pos) {
            var me = this;
            me._overlay.setPosition(pos, me._marker.getIcon().size);
            var index = me._troughPointIndex(pos);
            if (index != -1) {
                clearInterval(me._intervalFlag);
                me._overlay.setHtml(me._opts.landmarkPois[index].html);
                me._overlay.setPosition(pos, me._marker.getIcon().size);
                me._pauseForView(index);
            }else {
                me._overlay.setHtml(me._opts.defaultContent);
            }
        },
        _pauseForView: function(index) {
            var me = this;
            var t = setTimeout(function() {
                me._moveNext(++me.i);
            },me._opts.landmarkPois[index].pauseTime * 1000);
            me._setTimeoutQuene.push(t);
        },
        _clearTimeout: function() {
            for (var i in this._setTimeoutQuene) {
                clearTimeout(this._setTimeoutQuene[i]);
            }
            this._setTimeoutQuene.length = 0;
        },
        _tween: {
            linear: function(initPos, targetPos, currentCount, count) {
                var b = initPos, c = targetPos - initPos, t = currentCount,
                d = count;
                return c * t / d + b;
            }
        },
        _troughPointIndex: function(markerPoi) {
            var t = this._opts.landmarkPois, distance;
            for (var i = 0, len = t.length; i < len; i++) {
                if (!t[i].bShow) {
                    distance = this._map.getDistance(new BMap.Point(t[i].lng, t[i].lat), markerPoi);
                    if (distance < 10) {
                        t[i].bShow = true;
                        return i;
                    }
                }
            }
           return -1;
        }
    });
    function CustomOverlay(point,html) {
        this._point = point;
        this._html = html;
    }

    function initCustomOverlay() {
        CustomOverlay.prototype = new BMap.Overlay();
        CustomOverlay.prototype.initialize = function(map) {
            var div = this._div = baidu.dom.create('div', {style: 'border:solid 1px #ccc;width:auto;min-width:50px;text-align:center;position:absolute;background:#fff;color:#000;font-size:12px;border-radius: 10px;padding:5px;white-space: nowrap;'});
            div.innerHTML = this._html;
            map.getPanes().floatPane.appendChild(div);
            this._map = map;
            return div;
        }
        CustomOverlay.prototype.draw = function() {
            this.setPosition(this.lushuMain._marker.getPosition(), this.lushuMain._marker.getIcon().size);
        }
        baidu.object.extend(CustomOverlay.prototype, {
            setPosition: function(poi,markerSize) {
                var px = this._map.pointToOverlayPixel(poi),
                    styleW = baidu.dom.getStyle(this._div, 'width'),
                    styleH = baidu.dom.getStyle(this._div, 'height'),
                    overlayW = parseInt(this._div.clientWidth || styleW, 10),
                    overlayH = parseInt(this._div.clientHeight || styleH, 10);
                this._div.style.left = px.x - overlayW / 2 + 'px';
                this._div.style.bottom = -(px.y - markerSize.height) + 'px';
            },
            setHtml: function(html) {
                this._div.innerHTML = html;
            },
            setRelatedClass: function(lushuMain) {
                this.lushuMain = lushuMain;
            }
        });
    }

    return LuShu
});