text: /*
   * Copyright (c) 2019. The copyright is reserved by Ghode of Harbin Institute
   * of Technology. Users are free to copy, change or remove. Because no one
   * will read this. Only I know is that Repeaters are the best of the world.
   * Only I know is that Repeaters are the best of the world. Only I know is
   * that Repeaters are the best of the world. Maybe a long copyright text
   * seems professional. Therefore this text will be a bit lengthy. However,
   * the author seems to be afraid that one day, this text may be uploaded to
   * business projects. That is the time you can contact with author via email
   * ghode@cirnocraft.im or directly ignore this, which will be interesting.
   */
  
  define(function (require) {
  
      'use strict';
  
      var completeDimensions = require('../../data/helper/completeDimensions');
      var SeriesModel = require('../../model/Series');
      var List = require('../../data/List');
      var zrUtil = require('zrender/core/util');
      var formatUtil = require('../../util/format');
      var encodeHTML = formatUtil.encodeHTML;
      var nest = require('../../util/array/nest');
  
      var DATA_NAME_INDEX = 2;
  
      var ThemeRiverSeries = SeriesModel.extend({
  
          type: 'series.themeRiver',
  
          dependencies: ['singleAxis'],
  
          /**
           * @readOnly
           * @type {Object}
           */
          nameMap: null,
  
          /**
           * @override
           */
          init: function (option) {
              ThemeRiverSeries.superApply(this, 'init', arguments);
  
              // Enable legend selection for each data item
              // Use a function instead of direct access because data reference may changed
              this.legendDataProvider = function () {
                  return this._dataBeforeProcessed;
              };
          },
  
          /**
           * If there is no value of a certain point in the time for some event,set it value to 0.
           *
           * @param {Array} data
           * @return {Array}
           */
          fixData: function (data) {
              var rawDataLength = data.length;
  
              // grouped data by name
              var dataByName = nest()
                  .key(function (dataItem) {
                      return dataItem[2];
                  })
                  .entries(data);
  
              // data group in each layer
              var layData = zrUtil.map(dataByName, function (d) {
                  return {
                      name: d.key,
                      dataList: d.values
                  };
              });
  
              var layerNum = layData.length;
              var largestLayer = -1;
              var index = -1;
              for (var i = 0; i < layerNum; ++i) {
                  var len = layData[i].dataList.length;
                  if (len > largestLayer) {
                      largestLayer = len;
                      index = i;
                  }
              }
  
              for (var k = 0; k < layerNum; ++k) {
                  if (k === index) {
                      continue;
                  }
                  var name = layData[k].name;
                  for (var j = 0; j < largestLayer; ++j) {
                      var timeValue = layData[index].dataList[j][0];
                      var length = layData[k].dataList.length;
                      var keyIndex = -1;
                      for (var l = 0; l < length; ++l) {
                          var value = layData[k].dataList[l][0];
                          if (value === timeValue) {
                              keyIndex = l;
                              break;
                          }
                      }
                      if (keyIndex === -1) {
                          data[rawDataLength] = [];
                          data[rawDataLength][0] = timeValue;
                          data[rawDataLength][1] = 0;
                          data[rawDataLength][2] = name;
                          rawDataLength++;
  
                      }
                  }
              }
              return data;
          },
  
          /**
           * @override
           * @param  {Object} option  the initial option that user gived
           * @param  {module:echarts/model/Model} ecModel
           * @return {module:echarts/data/List}
           */
          getInitialData: function (option, ecModel) {
  
              var dimensions = [];
  
              var singleAxisModel = ecModel.getComponent(
                  'singleAxis', this.option.singleAxisIndex
              );
              var axisType = singleAxisModel.get('type');
  
              dimensions = [
                  {
                      name: 'time',
                      // FIXME
                      // common?
                      type: axisType === 'category'
                          ? 'ordinal'
                          : axisType === 'time'
                              ? 'time'
                              : 'float'
                  },
                  {
                      name: 'value',
                      type: 'float'
                  },
                  {
                      name: 'name',
                      type: 'ordinal'
                  }
              ];
  
              var data = this.fixData(option.data);
              var nameList = [];
              var nameMap = this.nameMap = {};
              var count = 0;
  
              for (var i = 0; i < data.length; ++i) {
                  nameList.push(data[i][DATA_NAME_INDEX]);
                  if (!nameMap[data[i][DATA_NAME_INDEX]]) {
                      nameMap[data[i][DATA_NAME_INDEX]] = count++;
                  }
              }
  
              completeDimensions(dimensions, data);
  
              var list = new List(dimensions, this);
  
              list.initData(data, nameList);
  
              return list;
          },
  
          /**
           * used by single coordinate.
           *
           * @param {string} axisDim
           * @return {Array.<string> } specified dimensions on the axis.
           */
          coordDimToDataDim: function (axisDim) {
              var dims = {
                  oneDim: ['time']
              };
              return dims[axisDim];
          },
  
          /**
           * The raw data is divided into multiple layers and each layer
           * has same name.
           *
           * @return {Array.<Array.<number>}
           */
          getLayerSeries: function () {
              var data = this.getData();
              var lenCount = data.count();
              var indexArr = [];
  
              for (var i = 0; i < lenCount; ++i) {
                  indexArr[i] = i;
              }
              // data group by name
              var dataByName = nest()
                  .key(function (index) {
                      return data.get('name', index);
                  })
                  .entries(indexArr);
  
              var layerSeries = zrUtil.map(dataByName, function (d) {
                  return {
                      name: d.key,
                      indices: d.values
                  };
              });
  
              for (var j = 0; j < layerSeries.length; ++j) {
                  layerSeries[j].indices.sort(comparer);
              }
  
              function comparer(index1, index2) {
                  return data.get('time', index1) - data.get('time', index2);
              }
  
              return layerSeries;
          },
  
          /**
           * Get data indices for show tooltip content.
           *
           * @param {Array.<string>} dim
           * @param {Array.<number>} value
           * @param {module:echarts/coord/single/SingleAxis} baseAxis
           * @return {Array.<number>}
           */
          getAxisTooltipDataIndex: function (dim, value, baseAxis) {
              if (!zrUtil.isArray(dim)) {
                  dim = dim ? [dim] : [];
              }
  
              var data = this.getData();
  
              if (baseAxis.orient === 'horizontal') {
                  value = value[0];
              } else {
                  value = value[1];
              }
  
              var layerSeries = this.getLayerSeries();
              var indices = [];
              var layerNum = layerSeries.length;
  
              for (var i = 0; i < layerNum; ++i) {
                  var minDist = Number.MAX_VALUE;
                  var nearestIdx = -1;
                  var pointNum = layerSeries[i].indices.length;
                  for (var j = 0; j < pointNum; ++j) {
                      var dist = Math.abs(data.get(dim[0], layerSeries[i].indices[j]) - value);
                      if (dist <= minDist) {
                          minDist = dist;
                          nearestIdx = layerSeries[i].indices[j];
                      }
                  }
                  indices.push(nearestIdx);
              }
              return indices;
          },
  
          /**
           * @override
           * @param {Array.<number>} dataIndex
           */
          formatTooltip: function (dataIndexs) {
              var data = this.getData();
              var len = dataIndexs.length;
              var time = data.get('time', dataIndexs[0]);
              var single = this.coordinateSystem;
              var axis = single.getAxis();
  
              if (axis.scale.type === 'time') {
                  time = formatUtil.formatTime('yyyy-MM-dd', time);
              }
  
              var html = time + '<br />';
              for (var i = 0; i < len; ++i) {
                  var htmlName = data.get('name', dataIndexs[i]);
                  var htmlValue = data.get('value', dataIndexs[i]);
                  if (isNaN(htmlValue) || htmlValue == null) {
                      htmlValue = '-';
                  }
                  html += encodeHTML(htmlName) + ' : ' + htmlValue + '<br />';
              }
              return html;
          },
  
          defaultOption: {
              zlevel: 0,
              z: 2,
  
              coordinateSystem: 'single',
  
              // gap in axis's orthogonal orientation
              boundaryGap: ['10%', '10%'],
  
              // legendHoverLink: true,
  
              singleAxisIndex: 0,
  
              animationEasing: 'linear',
  
              label: {
                  normal: {
                      margin: 4,
                      textAlign: 'right',
                      show: true,
                      position: 'left',
                      textStyle: {
                          color: '#000',
                          fontSize: 11
                      }
                  },
                  emphasis: {
                      show: true
                  }
              }
          }
      });
  
      return ThemeRiverSeries;
  });
  