var LeafletLabel = L.Class.extend({

    includes: L.Mixin.Events,

    options: {
        className: '',
        clickable: false,
        direction: 'right',
        noHide: false,
        offset: [12, -15], // 6 (width of the label triangle) + 6 (padding)
        opacity: 1,
        zoomAnimation: true
    },

    initialize: function (options, source) {
        L.setOptions(this, options);

        this._source = source;
        this._animated = L.Browser.any3d && this.options.zoomAnimation;
        this._isOpen = false;
    },

    onAdd: function (map) {
        this._map = map;

        this._pane = this.options.pane ? map._panes[this.options.pane] :
            this._source instanceof L.Marker ? map._panes.markerPane : map._panes.popupPane;

        if (!this._container) {
            this._initLayout();
        }

        this._pane.appendChild(this._container);

        this._initInteraction();

        this._update();

        this.setOpacity(this.options.opacity);

        map
            .on('moveend', this._onMoveEnd, this)
            .on('viewreset', this._onViewReset, this);

        if (this._animated) {
            map.on('zoomanim', this._zoomAnimation, this);
        }

        if (L.Browser.touch && !this.options.noHide) {
            L.DomEvent.on(this._container, 'click', this.close, this);
            map.on('click', this.close, this);
        }
    },

    onRemove: function (map) {
        this._pane.removeChild(this._container);

        map.off({
            zoomanim: this._zoomAnimation,
            moveend: this._onMoveEnd,
            viewreset: this._onViewReset
        }, this);

        this._removeInteraction();

        this._map = null;
    },

    setLatLng: function (latlng) {
        this._latlng = L.latLng(latlng);
        if (this._map) {
            this._updatePosition();
        }
        return this;
    },

    setContent: function (content) {
        // Backup previous content and store new content
        this._previousContent = this._content;
        this._content = content;

        this._updateContent();

        return this;
    },

    close: function () {
        var map = this._map;

        if (map) {
            if (L.Browser.touch && !this.options.noHide) {
                L.DomEvent.off(this._container, 'click', this.close);
                map.off('click', this.close, this);
            }

            map.removeLayer(this);
        }
    },

    updateZIndex: function (zIndex) {
        this._zIndex = zIndex;

        if (this._container && this._zIndex) {
            this._container.style.zIndex = zIndex;
        }
    },

    setOpacity: function (opacity) {
        this.options.opacity = opacity;

        if (this._container) {
            L.DomUtil.setOpacity(this._container, opacity);
        }
    },

    _initLayout: function () {
        this._container = L.DomUtil.create('div', 'leaflet-label ' + this.options.className + ' leaflet-zoom-animated');
        this.updateZIndex(this._zIndex);
    },

    _update: function () {
        if (!this._map) { return; }

        this._container.style.visibility = 'hidden';

        this._updateContent();
        this._updatePosition();

        this._container.style.visibility = '';
    },

    _updateContent: function () {
        if (!this._content || !this._map || this._prevContent === this._content) {
            return;
        }

        if (typeof this._content === 'string') {
            this._container.innerHTML = this._content;

            this._prevContent = this._content;

            this._labelWidth = this._container.offsetWidth;
        }
    },

    _updatePosition: function () {
        var pos = this._map.latLngToLayerPoint(this._latlng);

        this._setPosition(pos);
    },

    _setPosition: function (pos) {
        var map = this._map,
            container = this._container,
            centerPoint = map.latLngToContainerPoint(map.getCenter()),
            labelPoint = map.layerPointToContainerPoint(pos),
            direction = this.options.direction,
            labelWidth = this._labelWidth,
            offset = L.point(this.options.offset);

        // position to the right (right or auto & needs to)
        if (direction === 'right' || direction === 'auto' && labelPoint.x < centerPoint.x) {
            L.DomUtil.addClass(container, 'leaflet-label-right');
            L.DomUtil.removeClass(container, 'leaflet-label-left');

            pos = pos.add(offset);
        } else { // position to the left
            L.DomUtil.addClass(container, 'leaflet-label-left');
            L.DomUtil.removeClass(container, 'leaflet-label-right');

            pos = pos.add(L.point(-offset.x - labelWidth, offset.y));
        }

        L.DomUtil.setPosition(container, pos);
    },

    _zoomAnimation: function (opt) {
        var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();

        this._setPosition(pos);
    },

    _onMoveEnd: function () {
        if (!this._animated || this.options.direction === 'auto') {
            this._updatePosition();
        }
    },

    _onViewReset: function (e) {
        /* if map resets hard, we must update the label */
        if (e && e.hard) {
            this._update();
        }
    },

    _initInteraction: function () {
        if (!this.options.clickable) { return; }

        var container = this._container,
            events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];

        L.DomUtil.addClass(container, 'leaflet-clickable');
        L.DomEvent.on(container, 'click', this._onMouseClick, this);

        for (var i = 0; i < events.length; i++) {
            L.DomEvent.on(container, events[i], this._fireMouseEvent, this);
        }
    },

    _removeInteraction: function () {
        if (!this.options.clickable) { return; }

        var container = this._container,
            events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];

        L.DomUtil.removeClass(container, 'leaflet-clickable');
        L.DomEvent.off(container, 'click', this._onMouseClick, this);

        for (var i = 0; i < events.length; i++) {
            L.DomEvent.off(container, events[i], this._fireMouseEvent, this);
        }
    },

    _onMouseClick: function (e) {
        if (this.hasEventListeners(e.type)) {
            L.DomEvent.stopPropagation(e);
        }

        this.fire(e.type, {
            originalEvent: e
        });
    },

    _fireMouseEvent: function (e) {
        this.fire(e.type, {
            originalEvent: e
        });

        // TODO proper custom event propagation
        // this line will always be called if marker is in a FeatureGroup
        if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) {
            L.DomEvent.preventDefault(e);
        }
        if (e.type !== 'mousedown') {
            L.DomEvent.stopPropagation(e);
        } else {
            L.DomEvent.preventDefault(e);
        }
    }
});
export default LeafletLabel;