import Stack from './Stack.js';
import arrayEach from './arrayEach.js';
import assignValue from './assignValue.js';
import cloneBuffer from './cloneBuffer.js';
import copyArray from './copyArray.js';
import copyObject from './copyObject.js';
import cloneArrayBuffer from './cloneArrayBuffer.js';
import cloneDataView from './cloneDataView.js';
import cloneRegExp from './cloneRegExp.js';
import cloneSymbol from './cloneSymbol.js';
import cloneTypedArray from './cloneTypedArray.js';
import copySymbols from './copySymbols.js';
import copySymbolsIn from './copySymbolsIn.js';
import getAllKeys from './getAllKeys.js';
import getAllKeysIn from './getAllKeysIn.js';
import getTag from './getTag.js';
import initCloneObject from './initCloneObject.js';
import isBuffer from './isBuffer.js';
import isObject from './isObject.js';
import isTypedArray from './isTypedArray.js';
import keys from './keys.js';
import keysIn from './keysIn.js';

/** Used to compose bitmasks for cloning. */
/** 用于为克隆合成位掩码。 */
const CLONE_DEEP_FLAG = 1;
const CLONE_FLAT_FLAG = 2;
const CLONE_SYMBOLS_FLAG = 4;

/** `Object#toString` result references. */
const argsTag = '[object Arguments]';
const arrayTag = '[object Array]';
const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const mapTag = '[object Map]';
const numberTag = '[object Number]';
const objectTag = '[object Object]';
const regexpTag = '[object RegExp]';
const setTag = '[object Set]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const weakMapTag = '[object WeakMap]';

const arrayBufferTag = '[object ArrayBuffer]';
const dataViewTag = '[object DataView]';
const float32Tag = '[object Float32Array]';
const float64Tag = '[object Float64Array]';
const int8Tag = '[object Int8Array]';
const int16Tag = '[object Int16Array]';
const int32Tag = '[object Int32Array]';
const uint8Tag = '[object Uint8Array]';
const uint8ClampedTag = '[object Uint8ClampedArray]';
const uint16Tag = '[object Uint16Array]';
const uint32Tag = '[object Uint32Array]';

/** Used to identify `toStringTag` values supported by `clone`. */
/** 用于标识“clone”支持的“toStringTag”值。 */
const cloneableTags = {};
cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[
	dataViewTag
] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[
	float64Tag
] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[
	mapTag
] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[
	setTag
] = cloneableTags[stringTag] = cloneableTags[symbolTag] = cloneableTags[uint8Tag] = cloneableTags[
	uint8ClampedTag
] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
cloneableTags[errorTag] = cloneableTags[weakMapTag] = false;

/** Used to check objects for own properties. */
/** 用于检查对象的自身属性。 */
const hasOwnProperty = Object.prototype.hasOwnProperty;

/**
 * Initializes an object clone based on its `toStringTag`.
 * 根据对象的“toStringTag”初始化对象克隆。
 * **Note:** This function only supports cloning values with tags of
 * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.
 *  *注意:**此函数仅支持使用的标签克隆值
 * “布尔”,“日期”,“错误”,“地图”,“数量”,“正则表达式”,“设置”,或“字符串”。
 * @private
 * @param {Object} object 要克隆的对象。
 * @param {string} tag 要克隆的对象的“toStringTag”。
 * @param {boolean} [isDeep] 指定深度克隆。
 * @returns {Object} 返回初始化的克隆。
 */
function initCloneByTag(object, tag, isDeep) {
	const Ctor = object.constructor;
	switch (tag) {
		case arrayBufferTag:
			return cloneArrayBuffer(object);

		case boolTag:
		case dateTag:
			return new Ctor(+object);

		case dataViewTag:
			return cloneDataView(object, isDeep);

		case float32Tag:
		case float64Tag:
		case int8Tag:
		case int16Tag:
		case int32Tag:
		case uint8Tag:
		case uint8ClampedTag:
		case uint16Tag:
		case uint32Tag:
			return cloneTypedArray(object, isDeep);

		case mapTag:
			return new Ctor();

		case numberTag:
		case stringTag:
			return new Ctor(object);

		case regexpTag:
			return cloneRegExp(object);

		case setTag:
			return new Ctor();

		case symbolTag:
			return cloneSymbol(object);
	}
}

/**
 * Initializes an array clone.
 * 初始化数组克隆。
 * @private
 * @param {Array} array 要克隆的数组。
 * @returns {Array} 返回初始化的克隆。
 */
function initCloneArray(array) {
	const { length } = array;
	const result = new array.constructor(length);

	// Add properties assigned by `RegExp#exec`.
	if (length && typeof array[0] === 'string' && hasOwnProperty.call(array, 'index')) {
		result.index = array.index;
		result.input = array.input;
	}
	return result;
}

/**
 * The base implementation of `clone` and `cloneDeep` which tracks
 * traversed objects.
 *  ' clone '和' cloneDeep '跟踪的基本实现
 * 遍历对象。
 * @private
 * @param {*} value 要克隆的值。
 * @param {number} bitmask 位掩码的旗帜。
 *  1 - Deep clone 深克隆
 *  2 - Flatten inherited properties 继承属性
 *  4 - Clone symbols 克隆符号
 * @param {Function} [customizer] 函数自定义克隆。
 * @param {string} [key] The key of `value`.
 * @param {Object} [object] The parent object of `value`.
 * @param {Object} [stack] 跟踪遍历的对象及其克隆副本。
 * @returns {*} 返回克隆的值。
 */
function baseClone(value, bitmask, customizer, key, object, stack) {
	let result;
	const isDeep = bitmask & CLONE_DEEP_FLAG;
	const isFlat = bitmask & CLONE_FLAT_FLAG;
	const isFull = bitmask & CLONE_SYMBOLS_FLAG;

	if (customizer) {
		result = object ? customizer(value, key, object, stack) : customizer(value);
	}
	if (result !== undefined) {
		return result;
	}
	if (!isObject(value)) {
		return value;
	}
	const isArr = Array.isArray(value);
	const tag = getTag(value);
	if (isArr) {
		result = initCloneArray(value);
		if (!isDeep) {
			return copyArray(value, result);
		}
	} else {
		const isFunc = typeof value === 'function';

		if (isBuffer(value)) {
			return cloneBuffer(value, isDeep);
		}
		if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
			result = isFlat || isFunc ? {} : initCloneObject(value);
			if (!isDeep) {
				return isFlat
					? copySymbolsIn(value, copyObject(value, keysIn(value), result))
					: copySymbols(value, Object.assign(result, value));
			}
		} else {
			if (isFunc || !cloneableTags[tag]) {
				return object ? value : {};
			}
			result = initCloneByTag(value, tag, isDeep);
		}
	}
	// Check for circular references and return its corresponding clone.
	// 检查循环引用并返回其对应的克隆。
	stack || (stack = new Stack());
	const stacked = stack.get(value);
	if (stacked) {
		return stacked;
	}
	stack.set(value, result);

	if (tag == mapTag) {
		value.forEach((subValue, key) => {
			result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
		});
		return result;
	}

	if (tag == setTag) {
		value.forEach((subValue) => {
			result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
		});
		return result;
	}

	if (isTypedArray(value)) {
		return result;
	}

	const keysFunc = isFull ? (isFlat ? getAllKeysIn : getAllKeys) : isFlat ? keysIn : keys;

	const props = isArr ? undefined : keysFunc(value);
	arrayEach(props || value, (subValue, key) => {
		if (props) {
			key = subValue;
			subValue = value[key];
		}
		// Recursively populate clone (susceptible to call stack limits).
		// 递归地填充克隆(容易受到调用堆栈限制)。
		assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
	});
	return result;
}

export default baseClone;
