/**
 * @fileOverview The tooltip handler
 * @author sima.zhang
 */
var Util = require('../../util');

var _require = require('../../global'),
    defaultColor = _require.defaultColor;

var FIELD_ORIGIN = '_origin';

function getScaleName(scale) {
  return scale.alias || scale.field;
}

var TooltipMixin = {
  _getIntervalSize: function _getIntervalSize(obj) {
    var size = null;
    var type = this.get('type');
    var coord = this.get('coord');

    if (coord.isRect && (type === 'interval' || type === 'schema')) {
      size = this.getSize(obj[FIELD_ORIGIN]); // 濡傛灉瀛楁鍙戠敓浜嗘槧灏勶紝瀹藉害璁$畻灏变細鎶ラ敊

      var dim = coord.isTransposed ? 'y' : 'x';

      if (Util.isArray(obj[dim])) {
        var width = Math.abs(obj[dim][1] - obj[dim][0]);
        size = size < width ? null : size; // 鐩存柟鍥捐绠楅敊璇�
      }
    }

    return size;
  },
  _snapEqual: function _snapEqual(v1, v2, scale) {
    var equals;
    v1 = scale.translate(v1);
    v2 = scale.translate(v2);

    if (scale.isCategory) {
      equals = v1 === v2;
    } else {
      equals = Util.snapEqual(v1, v2);
    }

    return equals;
  },
  _getScaleValueByPoint: function _getScaleValueByPoint(point) {
    var result = 0;
    var coord = this.get('coord');
    var xScale = this.getXScale();
    var invertPoint = coord.invert(point);
    var xValue = invertPoint.x;

    if (this.isInCircle() && xValue > (1 + xScale.rangeMax()) / 2) {
      xValue = xScale.rangeMin(); // 鏋佸潗鏍囦笅锛宻cale 鐨� range 琚仛杩囩壒娈婂鐞� see view.js#L88
    }

    result = xScale.invert(xValue);

    if (xScale.isCategory) {
      result = xScale.translate(result); // 闃叉鍒嗙被绫诲瀷
    }

    return result;
  },
  _getOriginByPoint: function _getOriginByPoint(point) {
    var xScale = this.getXScale();
    var yScale = this.getYScale();
    var xField = xScale.field;
    var yField = yScale.field;
    var coord = this.get('coord');
    var invertPoint = coord.invert(point);
    var xValue = xScale.invert(invertPoint.x);
    var yValue = yScale.invert(invertPoint.y);
    var result = {};
    result[xField] = xValue;
    result[yField] = yValue;
    return result;
  },
  _getScale: function _getScale(field) {
    var self = this;
    var scales = self.get('scales');
    var rst = null;
    Util.each(scales, function (scale) {
      if (scale.field === field) {
        rst = scale;
        return false;
      }
    });
    return rst;
  },
  // 鑾峰彇鍊煎搴旂殑搴﹂噺
  _getTipValueScale: function _getTipValueScale() {
    var attrs = this.getAttrsForLegend();
    var scale;
    Util.each(attrs, function (attr) {
      var tmpScale = attr.getScale(attr.type);

      if (tmpScale.isLinear) {
        // 濡傛灉鎸囧畾瀛楁鏄潪position鐨勶紝鍚屾椂鏄繛缁殑
        scale = tmpScale;
        return false;
      }
    });
    var xScale = this.getXScale();
    var yScale = this.getYScale();

    if (!scale && yScale && yScale.field === '..y') {
      return xScale;
    }

    return scale || yScale || xScale;
  },
  _getTipTitleScale: function _getTipTitleScale(titleField) {
    var self = this;

    if (titleField) {
      return self._getScale(titleField);
    }

    var position = self.getAttr('position');
    var fields = position.getFields();
    var tmpField;
    Util.each(fields, function (field) {
      if (!field.includes('..')) {
        tmpField = field;
        return false;
      }
    });
    return self._getScale(tmpField);
  },
  _filterValue: function _filterValue(arr, point) {
    var coord = this.get('coord');
    var yScale = this.getYScale();
    var yField = yScale.field;
    var invertPoint = coord.invert(point);
    var yValue = invertPoint.y;
    yValue = yScale.invert(yValue);
    var rst = arr[arr.length - 1];
    Util.each(arr, function (obj) {
      var origin = obj[FIELD_ORIGIN];

      if (origin[yField][0] <= yValue && origin[yField][1] >= yValue) {
        rst = obj;
        return false;
      }
    });
    return rst;
  },
  getXDistance: function getXDistance() {
    var self = this;
    var distance = self.get('xDistance');

    if (!distance) {
      var xScale = self.getXScale();

      if (xScale.isCategory) {
        distance = 1;
      } else {
        var values = xScale.values; // values 鏄棤搴忕殑

        var min = xScale.translate(values[0]);
        var max = min;
        Util.each(values, function (value) {
          // 鏃堕棿绫诲瀷闇€瑕� translate
          value = xScale.translate(value);

          if (value < min) {
            min = value;
          }

          if (value > max) {
            max = value;
          }
        });
        var length = values.length; // 搴旇鏄櫎浠� length - 1

        distance = (max - min) / (length - 1);
      }

      self.set('xDistance', distance);
    }

    return distance;
  },
  findPoint: function findPoint(point, dataArray) {
    var self = this;
    var type = self.get('type');
    var xScale = self.getXScale();
    var yScale = self.getYScale();
    var xField = xScale.field;
    var yField = yScale.field;
    var rst = null;

    if (Util.indexOf(['heatmap', 'point'], type) > -1) {
      var coord = self.get('coord');
      var invertPoint = coord.invert(point);
      var xValue = xScale.invert(invertPoint.x);
      var yValue = yScale.invert(invertPoint.y);
      var min = Infinity;
      Util.each(dataArray, function (obj) {
        var distance = Math.pow(obj[FIELD_ORIGIN][xField] - xValue, 2) + Math.pow(obj[FIELD_ORIGIN][yField] - yValue, 2);

        if (distance < min) {
          min = distance;
          rst = obj;
        }
      });
      return rst;
    }

    var first = dataArray[0];
    var last = dataArray[dataArray.length - 1];

    if (!first) {
      return rst;
    }

    var value = self._getScaleValueByPoint(point); // 鏍规嵁璇ョ偣鑾峰緱瀵瑰簲搴﹂噺鍚庢暟鎹殑鍊�


    var firstXValue = first[FIELD_ORIGIN][xField];
    var firstYValue = first[FIELD_ORIGIN][yField];
    var lastXValue = last[FIELD_ORIGIN][xField];
    var isYRange = yScale.isLinear && Util.isArray(firstYValue); // 鑰冭檻 x 缁村害鐩稿悓锛寉 鏄暟缁勫尯闂寸殑鎯呭喌
    // 濡傛灉x鐨勫€兼槸鏁扮粍

    if (Util.isArray(firstXValue)) {
      Util.each(dataArray, function (record) {
        var origin = record[FIELD_ORIGIN];

        if (xScale.translate(origin[xField][0]) <= value && xScale.translate(origin[xField][1]) >= value) {
          if (isYRange) {
            if (!Util.isArray(rst)) {
              rst = [];
            }

            rst.push(record);
          } else {
            rst = record;
            return false;
          }
        }
      });

      if (Util.isArray(rst)) {
        rst = this._filterValue(rst, point);
      }
    } else {
      var next;

      if (!xScale.isLinear && xScale.type !== 'timeCat') {
        Util.each(dataArray, function (record, index) {
          var origin = record[FIELD_ORIGIN];

          if (self._snapEqual(origin[xField], value, xScale)) {
            if (isYRange) {
              if (!Util.isArray(rst)) {
                rst = [];
              }

              rst.push(record);
            } else {
              rst = record;
              return false;
            }
          } else if (xScale.translate(origin[xField]) <= value) {
            last = record;
            next = dataArray[index + 1];
          }
        });

        if (Util.isArray(rst)) {
          rst = this._filterValue(rst, point);
        }
      } else {
        if ((value > xScale.translate(lastXValue) || value < xScale.translate(firstXValue)) && (value > xScale.max || value < xScale.min)) {
          return null;
        }

        var firstIdx = 0;
        var lastIdx = dataArray.length - 1;
        var middleIdx;

        while (firstIdx <= lastIdx) {
          middleIdx = Math.floor((firstIdx + lastIdx) / 2);
          var item = dataArray[middleIdx][FIELD_ORIGIN][xField];

          if (self._snapEqual(item, value, xScale)) {
            return dataArray[middleIdx];
          }

          if (xScale.translate(item) <= xScale.translate(value)) {
            firstIdx = middleIdx + 1;
            last = dataArray[middleIdx];
            next = dataArray[middleIdx + 1];
          } else {
            if (lastIdx === 0) {
              last = dataArray[0];
            }

            lastIdx = middleIdx - 1;
          }
        }
      }

      if (last && next) {
        // 璁$畻鏈€閫艰繎鐨�
        if (Math.abs(xScale.translate(last[FIELD_ORIGIN][xField]) - value) > Math.abs(xScale.translate(next[FIELD_ORIGIN][xField]) - value)) {
          last = next;
        }
      }
    }

    var distance = self.getXDistance(); // 姣忎釜鍒嗙被闂寸殑骞冲潎闂磋窛

    if (!rst && Math.abs(xScale.translate(last[FIELD_ORIGIN][xField]) - value) <= distance / 2) {
      rst = last;
    }

    return rst;
  },

  /**
   * @protected
   * 鑾峰彇tooltip鐨勬爣棰�
   * @param  {Object} origin 鐐圭殑鍘熷淇℃伅
   * @param  {String} titleField 鏍囬鐨勫瓧娈�
   * @return {String} 鎻愮ず淇℃伅鐨勬爣棰�
   */
  getTipTitle: function getTipTitle(origin, titleField) {
    var tipTitle = '';

    var titleScale = this._getTipTitleScale(titleField);

    if (titleScale) {
      var value = origin[titleScale.field];
      tipTitle = titleScale.getText(value);
    } else if (this.get('type') === 'heatmap') {
      // 鐑姏鍥惧湪涓嶅瓨鍦� title 鐨勬椂鍊欑壒娈婂鐞�
      var xScale = this.getXScale();
      var yScale = this.getYScale();
      var xValue = xScale.getText(origin[xScale.field]);
      var yValue = yScale.getText(origin[yScale.field]);
      tipTitle = '( ' + xValue + ', ' + yValue + ' )';
    }

    return tipTitle;
  },
  getTipValue: function getTipValue(origin, valueScale) {
    var value;
    var field = valueScale.field;
    var key = origin.key;
    value = origin[field];

    if (Util.isArray(value)) {
      var tmp = [];
      Util.each(value, function (sub) {
        tmp.push(valueScale.getText(sub));
      });
      value = tmp.join('-');
    } else {
      value = valueScale.getText(value, key);
    }

    return value;
  },

  /**
   * @protected
   * 鑾峰彇tooltip鐨勫悕绉�
   * @param  {Object} origin 鐐圭殑鍘熷淇℃伅
   * @return {String} 鎻愮ず淇℃伅鐨勫悕绉�
   */
  getTipName: function getTipName(origin) {
    var name;
    var nameScale;

    var groupScales = this._getGroupScales();

    if (groupScales.length) {
      // 濡傛灉瀛樺湪鍒嗙粍绫诲瀷锛屽彇绗竴涓垎缁勭被鍨�
      Util.each(groupScales, function (scale) {
        nameScale = scale;
        return false;
      });
    }

    if (nameScale) {
      var field = nameScale.field;
      name = nameScale.getText(origin[field]);
    } else {
      var valueScale = this._getTipValueScale();

      name = getScaleName(valueScale);
    }

    return name;
  },

  /**
   * 鑾峰彇鐐瑰搴攖ooltip鐨勪俊鎭�
   * @protected
   * @param  {Object} point 鍘熷鐨勬暟鎹褰�
   * @param  {String} titleField tooltipTitle 閰嶇疆淇℃伅
   * @return {Array}  涓€鏉℃垨鑰呭鏉¤褰�
   */
  getTipItems: function getTipItems(point, titleField) {
    var self = this;
    var origin = point[FIELD_ORIGIN];
    var tipTitle = self.getTipTitle(origin, titleField);
    var tooltipCfg = self.get('tooltipCfg');
    var items = [];
    var name;
    var value;

    function addItem(itemName, itemValue, cfg) {
      if (!Util.isNil(itemValue) && itemValue !== '') {
        // 鍊间负null鐨勬椂鍊欙紝蹇借
        var item = {
          title: tipTitle,
          point: point,
          name: itemName || tipTitle,
          value: itemValue,
          color: point.color || defaultColor,
          marker: true
        };
        item.size = self._getIntervalSize(point);
        items.push(Util.mix({}, item, cfg));
      }
    }

    if (tooltipCfg) {
      var fields = tooltipCfg.fields;
      var cfg = tooltipCfg.cfg;
      var callbackParams = [];
      Util.each(fields, function (field) {
        callbackParams.push(origin[field]);
      });

      if (cfg) {
        // 瀛樺湪鍥炶皟鍑芥暟
        if (Util.isFunction(cfg)) {
          cfg = cfg.apply(null, callbackParams);
        }

        var itemCfg = Util.mix({}, {
          point: point,
          title: tipTitle,
          color: point.color || defaultColor,
          marker: true // 榛樿灞曠ず marker

        }, cfg);
        itemCfg.size = self._getIntervalSize(point);
        items.push(itemCfg);
      } else {
        Util.each(fields, function (field) {
          if (!Util.isNil(origin[field])) {
            // 瀛楁鏁版嵁涓簄ull ,undefined鏃朵笉鏄剧ず
            var scale = self._getScale(field);

            name = getScaleName(scale);
            value = scale.getText(origin[field]);
            addItem(name, value);
          }
        });
      }
    } else {
      var valueScale = self._getTipValueScale();

      if (!Util.isNil(origin[valueScale.field])) {
        // 瀛楁鏁版嵁涓簄ull ,undefined鏃朵笉鏄剧ず
        value = self.getTipValue(origin, valueScale);
        name = self.getTipName(origin);
        addItem(name, value);
      }
    }

    return items;
  },
  isShareTooltip: function isShareTooltip() {
    var shareTooltip = this.get('shareTooltip');
    var type = this.get('type');
    var view = this.get('view');
    var options;

    if (view.get('parent')) {
      options = view.get('parent').get('options');
    } else {
      options = view.get('options');
    }

    if (type === 'interval') {
      var coord = this.get('coord');
      var coordType = coord.type;

      if (coordType === 'theta' || coordType === 'polar' && coord.isTransposed) {
        shareTooltip = false;
      }
    } else if (!this.getYScale() || Util.inArray(['contour', 'point', 'polygon', 'edge'], type)) {
      shareTooltip = false;
    }

    if (options.tooltip && Util.isBoolean(options.tooltip.shared)) {
      // 浠ョ敤鎴疯缃殑涓哄噯
      shareTooltip = options.tooltip.shared;
    }

    return shareTooltip;
  }
};
module.exports = TooltipMixin;