/**
 * shape基类
 * @module zrender/shape/Base
 * @author  Kener (@Kener-林峰, kener.linfeng@gmail.com)
 *          errorrik (errorrik@gmail.com)
 */

/**
 * @typedef {Object} IBaseShapeStyle
 * @property {string} [brushType='fill']
 * @property {string} [color='#000000'] 填充颜色
 * @property {string} [strokeColor='#000000'] 描边颜色
 * @property {string} [lineCape='butt'] 线帽样式，可以是 butt, round, square
 * @property {number} [lineWidth=1] 描边宽度
 * @property {number} [opacity=1] 绘制透明度
 * @property {number} [shadowBlur=0] 阴影模糊度，大于0有效
 * @property {string} [shadowColor='#000000'] 阴影颜色
 * @property {number} [shadowOffsetX=0] 阴影横向偏移
 * @property {number} [shadowOffsetY=0] 阴影纵向偏移
 * @property {string} [text] 图形中的附加文本
 * @property {string} [textColor='#000000'] 文本颜色
 * @property {string} [textFont] 附加文本样式，eg:'bold 18px verdana'
 * @property {string} [textPosition='end'] 附加文本位置, 可以是 inside, left, right, top, bottom
 * @property {string} [textAlign] 默认根据textPosition自动设置，附加文本水平对齐。
 *                                可以是start, end, left, right, center
 * @property {string} [textBaseline] 默认根据textPosition自动设置，附加文本垂直对齐。
 *                                可以是top, bottom, middle, alphabetic, hanging, ideographic
 */

/**
 * @typedef {Object} module:zrender/shape/Base~IBoundingRect
 * @property {number} x 左上角顶点x轴坐标 
 * @property {number} y 左上角顶点y轴坐标
 * @property {number} width 包围盒矩形宽度
 * @property {number} height 包围盒矩形高度
 */
module.exports = function (require) {
  var vmlCanvasManager = window['G_vmlCanvasManager'];

  var matrix = require('../tool/matrix');

  var guid = require('../tool/guid');

  var util = require('../tool/util');

  var log = require('../tool/log');

  var Transformable = require('../mixin/Transformable');

  var Eventful = require('../mixin/Eventful');

  function _fillText(ctx, text, x, y, textFont, textAlign, textBaseline) {
    if (textFont) {
      ctx.font = textFont;
    }

    ctx.textAlign = textAlign;
    ctx.textBaseline = textBaseline;

    var rect = _getTextRect(text, x, y, textFont, textAlign, textBaseline);

    text = (text + '').split('\n');

    var lineHeight = require('../tool/area').getTextHeight('国', textFont);

    switch (textBaseline) {
      case 'top':
        y = rect.y;
        break;

      case 'bottom':
        y = rect.y + lineHeight;
        break;

      default:
        y = rect.y + lineHeight / 2;
    }

    for (var i = 0, l = text.length; i < l; i++) {
      ctx.fillText(text[i], x, y);
      y += lineHeight;
    }
  }
  /**
   * 返回矩形区域，用于局部刷新和文字定位
   * @inner
   * @param {string} text
   * @param {number} x
   * @param {number} y
   * @param {string} textFont
   * @param {string} textAlign
   * @param {string} textBaseline
   */


  function _getTextRect(text, x, y, textFont, textAlign, textBaseline) {
    var area = require('../tool/area');

    var width = area.getTextWidth(text, textFont);
    var lineHeight = area.getTextHeight('国', textFont);
    text = (text + '').split('\n');

    switch (textAlign) {
      case 'end':
      case 'right':
        x -= width;
        break;

      case 'center':
        x -= width / 2;
        break;
    }

    switch (textBaseline) {
      case 'top':
        break;

      case 'bottom':
        y -= lineHeight * text.length;
        break;

      default:
        y -= lineHeight * text.length / 2;
    }

    return {
      x: x,
      y: y,
      width: width,
      height: lineHeight * text.length
    };
  }
  /**
   * @alias module:zrender/shape/Base
   * @constructor
   * @extends module:zrender/mixin/Transformable
   * @extends module:zrender/mixin/Eventful
   * @param {Object} options 关于shape的配置项，可以是shape的自有属性，也可以是自定义的属性。
   */


  var Base = function (options) {
    options = options || {};
    /**
     * Shape id, 全局唯一
     * @type {string}
     */

    this.id = options.id || guid();

    for (var key in options) {
      this[key] = options[key];
    }
    /**
     * 基础绘制样式
     * @type {module:zrender/shape/Base~IBaseShapeStyle}
     */


    this.style = this.style || {};
    /**
     * 高亮样式
     * @type {module:zrender/shape/Base~IBaseShapeStyle}
     */

    this.highlightStyle = this.highlightStyle || null;
    /**
     * 父节点
     * @readonly
     * @type {module:zrender/Group}
     * @default null
     */

    this.parent = null;
    this.__dirty = true;
    this.__clipShapes = [];
    Transformable.call(this);
    Eventful.call(this);
  };
  /**
   * 图形是否可见，为true时不绘制图形，但是仍能触发鼠标事件
   * @name module:zrender/shape/Base#invisible
   * @type {boolean}
   * @default false
   */


  Base.prototype.invisible = false;
  /**
   * 图形是否忽略，为true时忽略图形的绘制以及事件触发
   * @name module:zrender/shape/Base#ignore
   * @type {boolean}
   * @default false
   */

  Base.prototype.ignore = false;
  /**
   * z层level，决定绘画在哪层canvas中
   * @name module:zrender/shape/Base#zlevel
   * @type {number}
   * @default 0
   */

  Base.prototype.zlevel = 0;
  /**
   * 是否可拖拽
   * @name module:zrender/shape/Base#draggable
   * @type {boolean}
   * @default false
   */

  Base.prototype.draggable = false;
  /**
   * 是否可点击
   * @name module:zrender/shape/Base#clickable
   * @type {boolean}
   * @default false
   */

  Base.prototype.clickable = false;
  /**
   * 是否可以hover
   * @name module:zrender/shape/Base#hoverable
   * @type {boolean}
   * @default true
   */

  Base.prototype.hoverable = true;
  /**
   * z值，跟zlevel一样影响shape绘制的前后顺序，z值大的shape会覆盖在z值小的上面，
   * 但是并不会创建新的canvas，所以优先级低于zlevel，而且频繁改动的开销比zlevel小很多。
   * 
   * @name module:zrender/shape/Base#z
   * @type {number}
   * @default 0
   */

  Base.prototype.z = 0;
  /**
   * 绘制图形
   * 
   * @param {CanvasRenderingContext2D} ctx
   * @param {boolean} [isHighlight=false] 是否使用高亮属性
   * @param {Function} [updateCallback]
   *        需要异步加载资源的shape可以通过这个callback(e), 
   *        让painter更新视图，base.brush没用，需要的话重载brush
   */

  Base.prototype.brush = function (ctx, isHighlight) {
    var style = this.beforeBrush(ctx, isHighlight);
    ctx.beginPath();
    this.buildPath(ctx, style);

    switch (style.brushType) {
      /* jshint ignore:start */
      case 'both':
        ctx.fill();

      case 'stroke':
        style.lineWidth > 0 && ctx.stroke();
        break;

      /* jshint ignore:end */

      default:
        ctx.fill();
    }

    this.drawText(ctx, style, this.style);
    this.afterBrush(ctx);
  };
  /**
   * 具体绘制操作前的一些公共操作
   * @param {CanvasRenderingContext2D} ctx
   * @param {boolean} [isHighlight=false] 是否使用高亮属性
   * @return {Object} 处理后的样式
   */


  Base.prototype.beforeBrush = function (ctx, isHighlight) {
    var style = this.style;

    if (this.brushTypeOnly) {
      style.brushType = this.brushTypeOnly;
    }

    if (isHighlight) {
      // 根据style扩展默认高亮样式
      style = this.getHighlightStyle(style, this.highlightStyle || {}, this.brushTypeOnly);
    }

    if (this.brushTypeOnly == 'stroke') {
      style.strokeColor = style.strokeColor || style.color;
    }

    ctx.save();
    this.doClip(ctx);
    this.setContext(ctx, style); // 设置transform

    this.setTransform(ctx);
    return style;
  };
  /**
   * 绘制后的处理
   * @param {CanvasRenderingContext2D} ctx
   */


  Base.prototype.afterBrush = function (ctx) {
    ctx.restore();
  };

  var STYLE_CTX_MAP = [['color', 'fillStyle'], ['strokeColor', 'strokeStyle'], ['opacity', 'globalAlpha'], ['lineCap', 'lineCap'], ['lineJoin', 'lineJoin'], ['miterLimit', 'miterLimit'], ['lineWidth', 'lineWidth'], ['shadowBlur', 'shadowBlur'], ['shadowColor', 'shadowColor'], ['shadowOffsetX', 'shadowOffsetX'], ['shadowOffsetY', 'shadowOffsetY']];
  /**
   * 设置 fillStyle, strokeStyle, shadow 等通用绘制样式
   * @param {CanvasRenderingContext2D} ctx
   * @param {module:zrender/shape/Base~IBaseShapeStyle} style
   */

  Base.prototype.setContext = function (ctx, style) {
    for (var i = 0, len = STYLE_CTX_MAP.length; i < len; i++) {
      var styleProp = STYLE_CTX_MAP[i][0];
      var styleValue = style[styleProp];
      var ctxProp = STYLE_CTX_MAP[i][1];

      if (typeof styleValue != 'undefined') {
        ctx[ctxProp] = styleValue;
      }
    }
  };

  var clipShapeInvTransform = matrix.create();

  Base.prototype.doClip = function (ctx) {
    if (this.__clipShapes && !vmlCanvasManager) {
      for (var i = 0; i < this.__clipShapes.length; i++) {
        var clipShape = this.__clipShapes[i];

        if (clipShape.needTransform) {
          var m = clipShape.transform;
          matrix.invert(clipShapeInvTransform, m);
          ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
        }

        ctx.beginPath();
        clipShape.buildPath(ctx, clipShape.style);
        ctx.clip(); // Transform back

        if (clipShape.needTransform) {
          var m = clipShapeInvTransform;
          ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
        }
      }
    }
  };
  /**
   * 根据默认样式扩展高亮样式
   * 
   * @param {module:zrender/shape/Base~IBaseShapeStyle} style 默认样式
   * @param {module:zrender/shape/Base~IBaseShapeStyle} highlightStyle 高亮样式
   * @param {string} brushTypeOnly
   */


  Base.prototype.getHighlightStyle = function (style, highlightStyle, brushTypeOnly) {
    var newStyle = {};

    for (var k in style) {
      newStyle[k] = style[k];
    }

    var color = require('../tool/color');

    var highlightColor = color.getHighlightColor(); // 根据highlightStyle扩展

    if (style.brushType != 'stroke') {
      // 带填充则用高亮色加粗边线
      newStyle.strokeColor = highlightColor;
      newStyle.lineWidth = (style.lineWidth || 1) + this.getHighlightZoom();
      newStyle.brushType = 'both';
    } else {
      if (brushTypeOnly != 'stroke') {
        // 描边型的则用原色加工高亮
        newStyle.strokeColor = highlightColor;
        newStyle.lineWidth = (style.lineWidth || 1) + this.getHighlightZoom();
      } else {
        // 线型的则用原色加工高亮
        newStyle.strokeColor = highlightStyle.strokeColor || color.mix(style.strokeColor, color.toRGB(highlightColor));
      }
    } // 可自定义覆盖默认值


    for (var k in highlightStyle) {
      if (typeof highlightStyle[k] != 'undefined') {
        newStyle[k] = highlightStyle[k];
      }
    }

    return newStyle;
  }; // 高亮放大效果参数
  // 当前统一设置为6，如有需要差异设置，通过this.type判断实例类型


  Base.prototype.getHighlightZoom = function () {
    return this.type != 'text' ? 6 : 2;
  };
  /**
   * 移动位置
   * @param {number} dx 横坐标变化
   * @param {number} dy 纵坐标变化
   */


  Base.prototype.drift = function (dx, dy) {
    this.position[0] += dx;
    this.position[1] += dy;
  };
  /**
   * 构建绘制的Path
   * @param {CanvasRenderingContext2D} ctx
   * @param {module:zrender/shape/Base~IBaseShapeStyle} style
   */


  Base.prototype.buildPath = function (ctx, style) {
    log('buildPath not implemented in ' + this.type);
  };
  /**
   * 计算返回包围盒矩形
   * @param {module:zrender/shape/Base~IBaseShapeStyle} style
   * @return {module:zrender/shape/Base~IBoundingRect}
   */


  Base.prototype.getRect = function (style) {
    log('getRect not implemented in ' + this.type);
  };
  /**
   * 判断鼠标位置是否在图形内
   * @param {number} x
   * @param {number} y
   * @return {boolean}
   */


  Base.prototype.isCover = function (x, y) {
    var originPos = this.transformCoordToLocal(x, y);
    x = originPos[0];
    y = originPos[1]; // 快速预判并保留判断矩形

    if (this.isCoverRect(x, y)) {
      // 矩形内
      return require('../tool/area').isInside(this, this.style, x, y);
    }

    return false;
  };

  Base.prototype.isCoverRect = function (x, y) {
    // 快速预判并保留判断矩形
    var rect = this.style.__rect;

    if (!rect) {
      rect = this.style.__rect = this.getRect(this.style);
    }

    return x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height;
  };
  /**
   * 绘制附加文本
   * @param {CanvasRenderingContext2D} ctx
   * @param {module:zrender/shape/Base~IBaseShapeStyle} style 样式
   * @param {module:zrender/shape/Base~IBaseShapeStyle} normalStyle 默认样式，用于定位文字显示
   */


  Base.prototype.drawText = function (ctx, style, normalStyle) {
    if (typeof style.text == 'undefined' || style.text === false) {
      return;
    } // 字体颜色策略


    var textColor = style.textColor || style.color || style.strokeColor;
    ctx.fillStyle = textColor; // 文本与图形间空白间隙

    var dd = 10;
    var al; // 文本水平对齐

    var bl; // 文本垂直对齐

    var tx; // 文本横坐标

    var ty; // 文本纵坐标

    var textPosition = style.textPosition // 用户定义
    || this.textPosition // shape默认
    || 'top'; // 全局默认

    switch (textPosition) {
      case 'inside':
      case 'top':
      case 'bottom':
      case 'left':
      case 'right':
        if (this.getRect) {
          var rect = (normalStyle || style).__rect || this.getRect(normalStyle || style);

          switch (textPosition) {
            case 'inside':
              tx = rect.x + rect.width / 2;
              ty = rect.y + rect.height / 2;
              al = 'center';
              bl = 'middle';

              if (style.brushType != 'stroke' && textColor == style.color) {
                ctx.fillStyle = '#fff';
              }

              break;

            case 'left':
              tx = rect.x - dd;
              ty = rect.y + rect.height / 2;
              al = 'end';
              bl = 'middle';
              break;

            case 'right':
              tx = rect.x + rect.width + dd;
              ty = rect.y + rect.height / 2;
              al = 'start';
              bl = 'middle';
              break;

            case 'top':
              tx = rect.x + rect.width / 2;
              ty = rect.y - dd;
              al = 'center';
              bl = 'bottom';
              break;

            case 'bottom':
              tx = rect.x + rect.width / 2;
              ty = rect.y + rect.height + dd;
              al = 'center';
              bl = 'top';
              break;
          }
        }

        break;

      case 'start':
      case 'end':
        var pointList = style.pointList || [[style.xStart || 0, style.yStart || 0], [style.xEnd || 0, style.yEnd || 0]];
        var length = pointList.length;

        if (length < 2) {
          // 少于2个点就不画了~
          return;
        }

        var xStart;
        var xEnd;
        var yStart;
        var yEnd;

        switch (textPosition) {
          case 'start':
            xStart = pointList[1][0];
            xEnd = pointList[0][0];
            yStart = pointList[1][1];
            yEnd = pointList[0][1];
            break;

          case 'end':
            xStart = pointList[length - 2][0];
            xEnd = pointList[length - 1][0];
            yStart = pointList[length - 2][1];
            yEnd = pointList[length - 1][1];
            break;
        }

        tx = xEnd;
        ty = yEnd;
        var angle = Math.atan((yStart - yEnd) / (xEnd - xStart)) / Math.PI * 180;

        if (xEnd - xStart < 0) {
          angle += 180;
        } else if (yStart - yEnd < 0) {
          angle += 360;
        }

        dd = 5;

        if (angle >= 30 && angle <= 150) {
          al = 'center';
          bl = 'bottom';
          ty -= dd;
        } else if (angle > 150 && angle < 210) {
          al = 'right';
          bl = 'middle';
          tx -= dd;
        } else if (angle >= 210 && angle <= 330) {
          al = 'center';
          bl = 'top';
          ty += dd;
        } else {
          al = 'left';
          bl = 'middle';
          tx += dd;
        }

        break;

      case 'specific':
        tx = style.textX || 0;
        ty = style.textY || 0;
        al = 'start';
        bl = 'middle';
        break;
    }

    if (tx != null && ty != null) {
      _fillText(ctx, style.text, tx, ty, style.textFont, style.textAlign || al, style.textBaseline || bl);
    }
  };

  Base.prototype.modSelf = function () {
    this.__dirty = true;

    if (this.style) {
      this.style.__rect = null;
    }

    if (this.highlightStyle) {
      this.highlightStyle.__rect = null;
    }
  };
  /**
   * 图形是否会触发事件
   * @return {boolean}
   */
  // TODO, 通过 bind 绑定的事件


  Base.prototype.isSilent = function () {
    return !(this.hoverable || this.draggable || this.clickable || this.onmousemove || this.onmouseover || this.onmouseout || this.onmousedown || this.onmouseup || this.onclick || this.ondragenter || this.ondragover || this.ondragleave || this.ondrop);
  };

  util.merge(Base.prototype, Transformable.prototype, true);
  util.merge(Base.prototype, Eventful.prototype, true);
  return Base;
}(require);