import {
  Canvas,
  Util,
  DomUtil,
  Browser,
  DomEvent,
  Renderer,
  Point,
  Bounds
} from '../lib/leaflet/leaflet-src.esm.js';

export var LabelTextCollision = Canvas.extend({
  options: {
    /**
     * Collision detection
     */
    collisionFlg: true
  },

  initialize: function(options) {
    //options = Util.setOptions(this, options);
    Renderer.prototype.initialize.call(this, options);
  },

  _handleMouseHover: function(e, point) {
    var id, layer;

    for (id in this._drawnLayers) {
      layer = this._drawnLayers[id];
      if (layer.options.interactive && layer._containsPoint(point)) {
        DomUtil.addClass(this._containerText, 'leaflet-interactive'); // change cursor
        this._fireEvent([layer], e, 'mouseover');
        this._hoveredLayer = layer;
      }
    }

    if (this._hoveredLayer) {
      this._fireEvent([this._hoveredLayer], e);
    }
  },

  _handleMouseOut: function(e, point) {
    var layer = this._hoveredLayer;
    if (layer && (e.type === 'mouseout' || !layer._containsPoint(point))) {
      // if we're leaving the layer, fire mouseout
      DomUtil.removeClass(this._containerText, 'leaflet-interactive');
      this._fireEvent([layer], e, 'mouseout');
      this._hoveredLayer = null;
    }
  },

  _updateTransform: function(center, zoom) {
    Canvas.prototype._updateTransform.call(this, center, zoom);

    var scale = this._map.getZoomScale(zoom, this._zoom),
      position = DomUtil.getPosition(this._container),
      viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
      currentCenterPoint = this._map.project(this._center, zoom),
      destCenterPoint = this._map.project(center, zoom),
      centerOffset = destCenterPoint.subtract(currentCenterPoint),
      topLeftOffset = viewHalf
        .multiplyBy(-scale)
        .add(position)
        .add(viewHalf)
        .subtract(centerOffset);

    if (Browser.any3d) {
      DomUtil.setTransform(this._containerText, topLeftOffset, scale);
    } else {
      DomUtil.setPosition(this._containerText, topLeftOffset);
    }
  },
  _initContainer: function(options) {
    Canvas.prototype._initContainer.call(this);

    this._containerText = document.createElement('canvas');

    DomEvent.on(
      this._containerText,
      'mousemove',
      Util.throttle(this._onMouseMove, 32, this),
      this
    )
      .on(
        this._containerText,
        'click dblclick mousedown mouseup contextmenu',
        this._onClick,
        this
      )
      .on(this._containerText, 'mouseout', this._handleMouseOut, this);

    this._ctxLabel = this._containerText.getContext('2d');

    DomUtil.addClass(this._containerText, 'leaflet-zoom-animated');
    this.getPane().appendChild(this._containerText);
  },

  _update: function() {
    // textList
    this._textList = [];

    Renderer.prototype._update.call(this);
    var b = this._bounds,
      container = this._containerText,
      size = b.getSize(),
      m = Browser.retina ? 2 : 1;

    DomUtil.setPosition(container, b.min);

    // set canvas size (also clearing it); use double size on retina
    container.width = m * size.x;
    container.height = m * size.y;
    container.style.width = size.x + 'px';
    container.style.height = size.y + 'px';

    // display text on the whole surface
    container.style.zIndex = '4';
    this._container.style.zIndex = '3';

    if (Browser.retina) {
      this._ctxLabel.scale(2, 2);
    }

    // translate so we use the same path coordinates after canvas
    // element moves
    this._ctxLabel.translate(-b.min.x, -b.min.y);
    Canvas.prototype._update.call(this);
  },

  _updatePoly: function(layer, closed) {
    Canvas.prototype._updatePoly.call(this, layer, closed);
    this._text(this._ctxLabel, layer);
  },

  _updateCircle: function(layer) {
    Canvas.prototype._updateCircle.call(this, layer);
    this._text(this._ctxLabel, layer);
  },

  _text: function(ctx, layer) {
    if (layer.options.text != undefined) {
      ctx.globalAlpha = 1;

      var p = layer._point;
      var textPoint;

      if (p == undefined) {
        // polygon or polyline
        if (layer._parts.length == 0 || layer._parts[0].length == 0) {
          return;
        }
        p = this._getCenter(layer._parts[0]);
      }

      // label bounds offset
      var offsetX = 0;
      var offsetY = 0;

      /**
       * TODO setting for custom font
       */
      ctx.lineWidth = 4.0;
      ctx.font = "16px 'Meiryo'";

      // Collision detection
      var textWidth = ctx.measureText(layer.options.text).width + p.x; // + offsetX;

      var textHeight = p.y + offsetY + 20;

      var bounds = new Bounds(
        new Point(p.x + offsetX, p.y + offsetY),
        new Point(textWidth, textHeight)
      );

      if (this.options.collisionFlg) {
        for (var index in this._textList) {
          var pointBounds = this._textList[index];
          if (pointBounds.intersects(bounds)) {
            return;
          }
        }
      }

      this._textList.push(bounds);

      ctx.strokeStyle = 'white';
      ctx.strokeText(layer.options.text, p.x + offsetX, p.y + offsetY);

      if (layer.options.textColor == undefined) {
        ctx.fillStyle = 'blue';
      } else {
        ctx.fillStyle = layer.options.textColor;
      }

      ctx.fillText(layer.options.text, p.x + offsetX, p.y + offsetY);
    }
  },

  _getCenter: function(points) {
    var i,
      halfDist,
      segDist,
      dist,
      p1,
      p2,
      ratio,
      len = points.length;

    if (!len) {
      return null;
    }

    // polyline centroid algorithm; only uses the first ring if
    // there are multiple

    for (i = 0, halfDist = 0; i < len - 1; i++) {
      halfDist += points[i].distanceTo(points[i + 1]) / 2;
    }

    // The line is so small in the current view that all points are
    // on the same pixel.
    if (halfDist === 0) {
      return points[0];
    }

    for (i = 0, dist = 0; i < len - 1; i++) {
      p1 = points[i];
      p2 = points[i + 1];
      segDist = p1.distanceTo(p2);
      dist += segDist;

      if (dist > halfDist) {
        ratio = (dist - halfDist) / segDist;
        var resutl = [
          p2.x - ratio * (p2.x - p1.x),
          p2.y - ratio * (p2.y - p1.y)
        ];

        return new Point(resutl[0], resutl[1]);
      }
    }
  }
});
