/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/


/**
 * AUTO-GENERATED FILE. DO NOT MODIFY.
 */

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { SERIES_LAYOUT_BY_COLUMN, SOURCE_FORMAT_OBJECT_ROWS, SOURCE_FORMAT_ARRAY_ROWS } from '../../util/types.js';
import { normalizeToArray } from '../../util/model.js';
import { createHashMap, bind, each, hasOwn, map, clone, isObject, extend, isNumber } from 'zrender/lib/core/util.js';
import { getRawSourceItemGetter, getRawSourceDataCounter, getRawSourceValueGetter } from './dataProvider.js';
import { parseDataValue } from './dataValueHelper.js';
import { log, makePrintable, throwError } from '../../util/log.js';
import { createSource, detectSourceFormat } from '../Source.js';
/**
 * TODO: disable writable.
 * This structure will be exposed to users.
 */

var ExternalSource =
/** @class */
function () {
  function ExternalSource() {}

  ExternalSource.prototype.getRawData = function () {
    // Only built-in transform available.
    throw new Error('not supported');
  };

  ExternalSource.prototype.getRawDataItem = function (dataIndex) {
    // Only built-in transform available.
    throw new Error('not supported');
  };

  ExternalSource.prototype.cloneRawData = function () {
    return;
  };
  /**
   * @return If dimension not found, return null/undefined.
   */


  ExternalSource.prototype.getDimensionInfo = function (dim) {
    return;
  };
  /**
   * dimensions defined if and only if either:
   * (a) dataset.dimensions are declared.
   * (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`).
   * If dimensions are defined, `dimensionInfoAll` is corresponding to
   * the defined dimensions.
   * Otherwise, `dimensionInfoAll` is determined by data columns.
   * @return Always return an array (even empty array).
   */


  ExternalSource.prototype.cloneAllDimensionInfo = function () {
    return;
  };

  ExternalSource.prototype.count = function () {
    return;
  };
  /**
   * Only support by dimension index.
   * No need to support by dimension name in transform function,
   * because transform function is not case-specific, no need to use name literally.
   */


  ExternalSource.prototype.retrieveValue = function (dataIndex, dimIndex) {
    return;
  };

  ExternalSource.prototype.retrieveValueFromItem = function (dataItem, dimIndex) {
    return;
  };

  ExternalSource.prototype.convertValue = function (rawVal, dimInfo) {
    return parseDataValue(rawVal, dimInfo);
  };

  return ExternalSource;
}();

export { ExternalSource };

function createExternalSource(internalSource, externalTransform) {
  var extSource = new ExternalSource();
  var data = internalSource.data;
  var sourceFormat = extSource.sourceFormat = internalSource.sourceFormat;
  var sourceHeaderCount = internalSource.startIndex;
  var errMsg = '';

  if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) {
    // For the logic simplicity in transformer, only 'culumn' is
    // supported in data transform. Otherwise, the `dimensionsDefine`
    // might be detected by 'row', which probably confuses users.
    if (process.env.NODE_ENV !== 'production') {
      errMsg = '`seriesLayoutBy` of upstream dataset can only be "column" in data transform.';
    }

    throwError(errMsg);
  } // [MEMO]
  // Create a new dimensions structure for exposing.
  // Do not expose all dimension info to users directly.
  // Because the dimension is probably auto detected from data and not might reliable.
  // Should not lead the transformers to think that is reliable and return it.
  // See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.


  var dimensions = [];
  var dimsByName = {};
  var dimsDef = internalSource.dimensionsDefine;

  if (dimsDef) {
    each(dimsDef, function (dimDef, idx) {
      var name = dimDef.name;
      var dimDefExt = {
        index: idx,
        name: name,
        displayName: dimDef.displayName
      };
      dimensions.push(dimDefExt); // Users probably do not specify dimension name. For simplicity, data transform
      // does not generate dimension name.

      if (name != null) {
        // Dimension name should not be duplicated.
        // For simplicity, data transform forbids name duplication, do not generate
        // new name like module `completeDimensions.ts` did, but just tell users.
        var errMsg_1 = '';

        if (hasOwn(dimsByName, name)) {
          if (process.env.NODE_ENV !== 'production') {
            errMsg_1 = 'dimension name "' + name + '" duplicated.';
          }

          throwError(errMsg_1);
        }

        dimsByName[name] = dimDefExt;
      }
    });
  } // If dimension definitions are not defined and can not be detected.
  // e.g., pure data `[[11, 22], ...]`.
  else {
      for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) {
        // Do not generete name or anything others. The consequence process in
        // `transform` or `series` probably have there own name generation strategry.
        dimensions.push({
          index: i
        });
      }
    } // Implement public methods:


  var rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);

  if (externalTransform.__isBuiltIn) {
    extSource.getRawDataItem = function (dataIndex) {
      return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);
    };

    extSource.getRawData = bind(getRawData, null, internalSource);
  }

  extSource.cloneRawData = bind(cloneRawData, null, internalSource);
  var rawCounter = getRawSourceDataCounter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);
  extSource.count = bind(rawCounter, null, data, sourceHeaderCount, dimensions);
  var rawValueGetter = getRawSourceValueGetter(sourceFormat);

  extSource.retrieveValue = function (dataIndex, dimIndex) {
    var rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex);
    return retrieveValueFromItem(rawItem, dimIndex);
  };

  var retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) {
    if (dataItem == null) {
      return;
    }

    var dimDef = dimensions[dimIndex]; // When `dimIndex` is `null`, `rawValueGetter` return the whole item.

    if (dimDef) {
      return rawValueGetter(dataItem, dimIndex, dimDef.name);
    }
  };

  extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName);
  extSource.cloneAllDimensionInfo = bind(cloneAllDimensionInfo, null, dimensions);
  return extSource;
}

function getRawData(upstream) {
  var sourceFormat = upstream.sourceFormat;

  if (!isSupportedSourceFormat(sourceFormat)) {
    var errMsg = '';

    if (process.env.NODE_ENV !== 'production') {
      errMsg = '`getRawData` is not supported in source format ' + sourceFormat;
    }

    throwError(errMsg);
  }

  return upstream.data;
}

function cloneRawData(upstream) {
  var sourceFormat = upstream.sourceFormat;
  var data = upstream.data;

  if (!isSupportedSourceFormat(sourceFormat)) {
    var errMsg = '';

    if (process.env.NODE_ENV !== 'production') {
      errMsg = '`cloneRawData` is not supported in source format ' + sourceFormat;
    }

    throwError(errMsg);
  }

  if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
    var result = [];

    for (var i = 0, len = data.length; i < len; i++) {
      // Not strictly clone for performance
      result.push(data[i].slice());
    }

    return result;
  } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
    var result = [];

    for (var i = 0, len = data.length; i < len; i++) {
      // Not strictly clone for performance
      result.push(extend({}, data[i]));
    }

    return result;
  }
}

function getDimensionInfo(dimensions, dimsByName, dim) {
  if (dim == null) {
    return;
  } // Keep the same logic as `List::getDimension` did.


  if (isNumber(dim) // If being a number-like string but not being defined a dimension name.
  || !isNaN(dim) && !hasOwn(dimsByName, dim)) {
    return dimensions[dim];
  } else if (hasOwn(dimsByName, dim)) {
    return dimsByName[dim];
  }
}

function cloneAllDimensionInfo(dimensions) {
  return clone(dimensions);
}

var externalTransformMap = createHashMap();
export function registerExternalTransform(externalTransform) {
  externalTransform = clone(externalTransform);
  var type = externalTransform.type;
  var errMsg = '';

  if (!type) {
    if (process.env.NODE_ENV !== 'production') {
      errMsg = 'Must have a `type` when `registerTransform`.';
    }

    throwError(errMsg);
  }

  var typeParsed = type.split(':');

  if (typeParsed.length !== 2) {
    if (process.env.NODE_ENV !== 'production') {
      errMsg = 'Name must include namespace like "ns:regression".';
    }

    throwError(errMsg);
  } // Namespace 'echarts:xxx' is official namespace, where the transforms should
  // be called directly via 'xxx' rather than 'echarts:xxx'.


  var isBuiltIn = false;

  if (typeParsed[0] === 'echarts') {
    type = typeParsed[1];
    isBuiltIn = true;
  }

  externalTransform.__isBuiltIn = isBuiltIn;
  externalTransformMap.set(type, externalTransform);
}
export function applyDataTransform(rawTransOption, sourceList, infoForPrint) {
  var pipedTransOption = normalizeToArray(rawTransOption);
  var pipeLen = pipedTransOption.length;
  var errMsg = '';

  if (!pipeLen) {
    if (process.env.NODE_ENV !== 'production') {
      errMsg = 'If `transform` declared, it should at least contain one transform.';
    }

    throwError(errMsg);
  }

  for (var i = 0, len = pipeLen; i < len; i++) {
    var transOption = pipedTransOption[i];
    sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i); // piped transform only support single input, except the fist one.
    // piped transform only support single output, except the last one.

    if (i !== len - 1) {
      sourceList.length = Math.max(sourceList.length, 1);
    }
  }

  return sourceList;
}

function applySingleDataTransform(transOption, upSourceList, infoForPrint, // If `pipeIndex` is null/undefined, no piped transform.
pipeIndex) {
  var errMsg = '';

  if (!upSourceList.length) {
    if (process.env.NODE_ENV !== 'production') {
      errMsg = 'Must have at least one upstream dataset.';
    }

    throwError(errMsg);
  }

  if (!isObject(transOption)) {
    if (process.env.NODE_ENV !== 'production') {
      errMsg = 'transform declaration must be an object rather than ' + typeof transOption + '.';
    }

    throwError(errMsg);
  }

  var transType = transOption.type;
  var externalTransform = externalTransformMap.get(transType);

  if (!externalTransform) {
    if (process.env.NODE_ENV !== 'production') {
      errMsg = 'Can not find transform on type "' + transType + '".';
    }

    throwError(errMsg);
  } // Prepare source


  var extUpSourceList = map(upSourceList, function (upSource) {
    return createExternalSource(upSource, externalTransform);
  });
  var resultList = normalizeToArray(externalTransform.transform({
    upstream: extUpSourceList[0],
    upstreamList: extUpSourceList,
    config: clone(transOption.config)
  }));

  if (process.env.NODE_ENV !== 'production') {
    if (transOption.print) {
      var printStrArr = map(resultList, function (extSource) {
        var pipeIndexStr = pipeIndex != null ? ' === pipe index: ' + pipeIndex : '';
        return ['=== dataset index: ' + infoForPrint.datasetIndex + pipeIndexStr + ' ===', '- transform result data:', makePrintable(extSource.data), '- transform result dimensions:', makePrintable(extSource.dimensions)].join('\n');
      }).join('\n');
      log(printStrArr);
    }
  }

  return map(resultList, function (result, resultIndex) {
    var errMsg = '';

    if (!isObject(result)) {
      if (process.env.NODE_ENV !== 'production') {
        errMsg = 'A transform should not return some empty results.';
      }

      throwError(errMsg);
    }

    if (!result.data) {
      if (process.env.NODE_ENV !== 'production') {
        errMsg = 'Transform result data should be not be null or undefined';
      }

      throwError(errMsg);
    }

    var sourceFormat = detectSourceFormat(result.data);

    if (!isSupportedSourceFormat(sourceFormat)) {
      if (process.env.NODE_ENV !== 'production') {
        errMsg = 'Transform result data should be array rows or object rows.';
      }

      throwError(errMsg);
    }

    var resultMetaRawOption;
    var firstUpSource = upSourceList[0];
    /**
     * Intuitively, the end users known the content of the original `dataset.source`,
     * calucating the transform result in mind.
     * Suppose the original `dataset.source` is:
     * ```js
     * [
     *     ['product', '2012', '2013', '2014', '2015'],
     *     ['AAA', 41.1, 30.4, 65.1, 53.3],
     *     ['BBB', 86.5, 92.1, 85.7, 83.1],
     *     ['CCC', 24.1, 67.2, 79.5, 86.4]
     * ]
     * ```
     * The dimension info have to be detected from the source data.
     * Some of the transformers (like filter, sort) will follow the dimension info
     * of upstream, while others use new dimensions (like aggregate).
     * Transformer can output a field `dimensions` to define the its own output dimensions.
     * We also allow transformers to ignore the output `dimensions` field, and
     * inherit the upstream dimensions definition. It can reduce the burden of handling
     * dimensions in transformers.
     *
     * See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.
     */

    if (firstUpSource && resultIndex === 0 // If transformer returns `dimensions`, it means that the transformer has different
    // dimensions definitions. We do not inherit anything from upstream.
    && !result.dimensions) {
      var startIndex = firstUpSource.startIndex; // We copy the header of upstream to the result, because:
      // (1) The returned data always does not contain header line and can not be used
      // as dimension-detection. In this case we can not use "detected dimensions" of
      // upstream directly, because it might be detected based on different `seriesLayoutBy`.
      // (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`.
      // So the original detected header should be add to the result, otherwise they can not be read.

      if (startIndex) {
        result.data = firstUpSource.data.slice(0, startIndex).concat(result.data);
      }

      resultMetaRawOption = {
        seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
        sourceHeader: startIndex,
        dimensions: firstUpSource.metaRawOption.dimensions
      };
    } else {
      resultMetaRawOption = {
        seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
        sourceHeader: 0,
        dimensions: result.dimensions
      };
    }

    return createSource(result.data, resultMetaRawOption, null);
  });
}

function isSupportedSourceFormat(sourceFormat) {
  return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS;
}