let assign = function (target, ...varArgs) {
	if (target == null) {
		throw new TypeError('Cannot convert undefined or null to object');
	}
	if (!varArgs || varArgs.length <= 0) {
		return target;
	}
	// 深度合并对象
	function deepAssign(obj1, obj2) {
		for (let key in obj2) {
			obj1[key] = obj1[key] && obj1[key].toString() === "[object Object]" ?
				deepAssign(obj1[key], obj2[key]) : obj1[key] = obj2[key];
		}
		return obj1;
	}

	varArgs.forEach(val => {
		target = deepAssign(target, val);
	});
	return target;
};

function Event() {
	this.events = {};
}
Event.prototype.addEventListener = function (type, listener) {
	this.events[type] = this.events[type] || [];
	this.events[type].push(listener);
};
Event.prototype.trigger = function () {
	for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
		args[_key] = arguments[_key];
	}

	var type = args[0];
	var params = args.slice(1);
	if (!!this.events[type]) {
		// console.log("type:",type);
		this.events[type].forEach(function (listener) {
			try {
				listener.apply(null, params);
			} catch (e) {
				console.error(e);
			}
		});
	}
};

var EquipmentAction = function EquipmentAction() {
	let opts = {
		//合力计算
		oldResultantDifference: 0,
		resultantDifference: 0,
		bCalResultant: false,

		//最大的差值
		resultantMaxDifference: 0,
		//最大的加速度
		resultantMaxX: 0,
		resultantMaxZ: 0,
		resultantMaxY: 0,

		resultantMaxAddCount: 0,
		resultAddDifferenceCount: 0,

		xA: 0,
		oldxA: 0,
		zA: 0,
		oldzA: 0,

		yA: 0,
		oldyA: 0,

		//初始化
		initX: false,
		initZ: false,

		initY: false,

		mass: 5,

		//是否限制回弹,true 是限制回弹,false 是快速打击
		bLimitRebound: true
	}
	this.opts = opts;

	let runOpts = {
		ValueNum: 4,
		tempValue: [], //用于存放计算阈值的波峰波谷差值
		tempCount: 0,
		//是否上升的标志位
		isDirectionUp: false,
		//持续上升次数
		continueUpCount: 0,
		//上一点的持续上升的次数，为了记录波峰的上升次数
		continueUpFormerCount: 0,
		//上一点的状态，上升还是下降
		lastStatus: false,
		//波峰值
		peakOfWave: 0,
		//波谷值
		valleyOfWave: 0,
		//此次波峰的时间
		timeOfThisPeak: 0,
		//上次波峰的时间
		timeOfLastPeak: 0,
		//当前的时间
		timeOfNow: 0,
		//当前传感器的值
		gravityNew: 0,
		//上次传感器的值
		gravityOld: 0,
		//上传传感器的陀螺仪
		gyroOld: 0,
		//动态阈值需要动态的数据，这个值用于这些动态数据的阈值
		InitialValue: 1.3,
		//初始阈值
		ThreadValue: 2.0,
		//波峰波谷时间差
		TimeInterval: 250,

		//跳高
		jumpStepCount: 0,
		jumpTimeOfLastStep: 0,
		jumpTimeOfThisStep: 0,
		jumpAverageTimeOfEveryStep: 0,
		//跑步
		stepCount: 0,
		timeOfLastStep: 0,
		timeOfThisStep: 0,
		averageTimeOfEveryStep: 0,

		currentState: "idle"
	}
	this.runOpts = runOpts;


	let gyroOpts = {
		ValueNum: 4,
		tempValue: [], //用于存放计算阈值的波峰波谷差值
		tempCount: 0,
		//是否上升的标志位
		isDirectionUp: false,
		//持续上升次数
		continueUpCount: 0,
		//上一点的持续上升的次数，为了记录波峰的上升次数
		continueUpFormerCount: 0,
		//上一点的状态，上升还是下降
		lastStatus: false,
		//波峰值
		peakOfWave: 0,
		//波谷值
		valleyOfWave: 0,
		//此次波峰的时间
		timeOfThisPeak: 0,
		//上次波峰的时间
		timeOfLastPeak: 0,
		//当前的时间
		timeOfNow: 0,
		//当前传感器的值
		gravityNew: 0,
		//上次传感器的值
		gravityOld: 0,
		//动态阈值需要动态的数据，这个值用于这些动态数据的阈值
		InitialValue: 1.3,
		//初始阈值
		ThreadValue: 200.0,
		//波峰波谷时间差
		TimeInterval: 250,

		bJump: false,
	}
	this.gyroOpts = gyroOpts;

	this.event = new Event();

	// console.log("hit instanl:",this.opts);
}
EquipmentAction.prototype.addEventListener = function (type, listener) {
	this.event.addEventListener(type, listener);
};

/**
 * 更新两轴数据
 */
EquipmentAction.prototype.updateAcc = function () {
	let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
	// this.opts = assign({}, this.opts, data);
	let bLimitRebound = data.bLimitRebound && this.opts.bLimitRebound;
	//使用两个轴的数据，其实就是不计算重力轴的加速度
	this.opts.xA = data.xA;
	this.opts.zA = data.zA;

	// console.log(bLimitRebound,data.bLimitRebound,this.opts.bLimitRebound);

	//这里防止第一次触发调用
	if (!this.opts.initX && this.opts.oldxA === 0) {
		this.opts.oldxA = data.xA;
		this.opts.initX = true;
		console.log("初始化oldxa");
	}
	if (!this.opts.initZ && this.opts.oldzA === 0) {
		this.opts.oldzA = data.zA;
		this.opts.initZ = true;
		console.log("初始化oldza");
	}

	let _differenceX = this.opts.xA - this.opts.oldxA;
	let _differenceZ = this.opts.zA - this.opts.oldzA;
	this.opts.resultantDifference = Math.abs(_differenceX) + Math.abs(_differenceZ);

	if (this.opts.oldResultantDifference != this.opts.resultantDifference && 0 !== this.opts.resultantDifference) {
		//5  默认值是12
		let _limitresultDifference = 12;
		//如果是回弹限制时候，判断值等于5
		if (bLimitRebound)
			_limitresultDifference = 5;

		if (this.opts.resultantDifference > _limitresultDifference) {
			if (!this.opts.bCalResultant) {
				//假如进入了一个计算周期
				this.opts.bCalResultant = true;
			}
		}

		if (this.opts.bCalResultant) {
			if (this.opts.resultantMaxX <= this.opts.xA)
				this.opts.resultantMaxX = this.opts.xA;
			if (this.opts.resultantMaxZ <= this.opts.zA)
				this.opts.resultantMaxZ = this.opts.zA;

			if (this.opts.resultantMaxDifference <= this.opts.resultantDifference) {
				this.opts.resultantMaxDifference = this.opts.resultantDifference;
				this.opts.resultantMaxAddCount = 0;
			} else {
				this.opts.resultantMaxAddCount++;
				//小于最大值的个数判断,超过限制，说明波动下滑
				// > 4
				if (this.opts.resultantMaxAddCount >= 4) {
					//这里区分两个版本，bLimitRebound:true一个是限制回弹版，bLimitRebound:false一个是快速打击版本
					if (bLimitRebound) {
						if (this.opts.resultantDifference <= 3) {
							this.opts.resultAddDifferenceCount++;
							// 当波动值小于3 次数到达5次，判断达到一个平稳状态
							if (this.opts.resultAddDifferenceCount >= 5) {
								let _resultantMaxAcc = Math.abs(this.opts.resultantMaxX) + Math.abs(this.opts.resultantMaxZ);

								this.opts.bCalResultant = false;
								this.opts.resultantMaxAddCount = 0;
								this.opts.resultAddDifferenceCount = 0;
								//成功判断打击直拳 todo return
								this.event.trigger('resultantHit', {
									type: "hit",
									acc: _resultantMaxAcc,
									power: Math.ceil(_resultantMaxAcc * this.opts.mass),
									mass: this.opts.mass
								});


								this.opts.oldResultantDifference = 0;
								this.opts.resultantDifference = 0;
								this.opts.resultantMaxDifference = 0;
								this.opts.resultantMaxX = 0;
								this.opts.resultantMaxZ = 0;
								// this.opts.oldxA = 0;
								// this.opts.oldzA = 0;

							}
						} else {
							this.opts.resultAddDifferenceCount = 0;
						}
					} else {
						let _resultantMaxAcc = Math.abs(this.opts.resultantMaxX) + Math.abs(this.opts.resultantMaxZ);

						this.opts.bCalResultant = false;
						this.opts.resultantMaxAddCount = 0;
						this.opts.resultAddDifferenceCount = 0;
						//成功判断打击直拳 todo return
						this.event.trigger('resultantHit', {
							type: "hit",
							acc: _resultantMaxAcc,
							power: Math.ceil(_resultantMaxAcc * this.opts.mass),
							mass: this.opts.mass
						});


						this.opts.oldResultantDifference = 0;
						this.opts.resultantDifference = 0;
						this.opts.resultantMaxDifference = 0;
						this.opts.resultantMaxX = 0;
						this.opts.resultantMaxZ = 0;
						// this.opts.oldxA = 0;
						// this.opts.oldzA = 0;
					}

				}
			}
		}

		this.opts.oldResultantDifference = this.opts.resultantDifference;

	}
	this.opts.oldxA = this.opts.xA;
	this.opts.oldzA = this.opts.zA;
};

/**
 * 更新三轴数据
 */
EquipmentAction.prototype.updateTriaxialAcc = function () {
	let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
	// this.opts = assign({}, this.opts, data);
	let bLimitRebound = data.bLimitRebound && this.opts.bLimitRebound;
	//使用三个轴的数据，计算重力轴的加速度。最后减去重力的加速度值
	this.opts.xA = data.xA;
	this.opts.zA = data.zA;
	this.opts.yA = data.yA;

	// console.log(bLimitRebound,data.bLimitRebound,this.opts.bLimitRebound);

	//这里防止第一次触发调用
	if (!this.opts.initX && this.opts.oldxA === 0) {
		this.opts.oldxA = data.xA;
		this.opts.initX = true;
		console.log("初始化oldxa");
	}
	if (!this.opts.initZ && this.opts.oldzA === 0) {
		this.opts.oldzA = data.zA;
		this.opts.initZ = true;
		console.log("初始化oldza");
	}

	if (!this.opts.initY && this.opts.oldyA === 0) {
		this.opts.oldyA = data.yA;
		this.opts.initY = true;
		console.log("初始化oldyA");
	}

	let _differenceX = this.opts.xA - this.opts.oldxA;
	let _differenceZ = this.opts.zA - this.opts.oldzA;
	let _differenceY = this.opts.yA - this.opts.oldyA;

	// let gravityNew = Math.sqrt(xA * xA + yA * yA + zA * zA);

	// this.opts.resultantDifference = Math.abs(_differenceX) + Math.abs(_differenceZ) + Math.abs(_differenceY);
	this.opts.resultantDifference = Math.sqrt(_differenceX * _differenceX + _differenceZ * _differenceZ + _differenceY *
		_differenceY);

	if (this.opts.oldResultantDifference != this.opts.resultantDifference && 0 !== this.opts.resultantDifference) {
		//5  默认值是12
		let _limitresultDifference = 12;
		//如果是回弹限制时候，判断值等于5
		if (bLimitRebound)
			_limitresultDifference = 5;

		if (this.opts.resultantDifference > _limitresultDifference) {
			if (!this.opts.bCalResultant) {
				//假如进入了一个计算周期
				this.opts.bCalResultant = true;
			}
		}

		if (this.opts.bCalResultant) {

			if (this.opts.resultantMaxDifference <= this.opts.resultantDifference) {
				this.opts.resultantMaxDifference = this.opts.resultantDifference;
				this.opts.resultantMaxAddCount = 0;
			} else {
				this.opts.resultantMaxAddCount++;
				//小于最大值的个数判断,超过限制，说明波动下滑
				// > 4
				if (this.opts.resultantMaxAddCount >= 4) {
					//这里区分两个版本，bLimitRebound:true一个是限制回弹版，bLimitRebound:false一个是快速打击版本
					if (bLimitRebound) {
						if (this.opts.resultantDifference <= 3) {
							this.opts.resultAddDifferenceCount++;
							// 当波动值小于3 次数到达5次，判断达到一个平稳状态
							if (this.opts.resultAddDifferenceCount >= 5) {
								// let _resultantMaxAcc = Math.abs(this.opts.resultantMaxX) + Math.abs(this.opts.resultantMaxZ) + Math.abs(this.opts.resultantMaxY);
								// console.log("this.opts.resultantMaxDifference:",this.opts.resultantMaxDifference);
								this.opts.bCalResultant = false;
								this.opts.resultantMaxAddCount = 0;
								this.opts.resultAddDifferenceCount = 0;
								//成功判断打击直拳 todo return
								this.event.trigger('resultantHit', {
									type: "hit",
									acc: this.opts.resultantMaxDifference,
									power: Math.ceil(this.opts.resultantMaxDifference * this.opts.mass),
									mass: this.opts.mass
								});


								this.opts.oldResultantDifference = 0;
								this.opts.resultantDifference = 0;
								this.opts.resultantMaxDifference = 0;
								this.opts.resultantMaxX = 0;
								this.opts.resultantMaxZ = 0;
								this.opts.resultantMaxY = 0;

							}
						} else {
							this.opts.resultAddDifferenceCount = 0;
						}
					} else {

						this.opts.bCalResultant = false;
						this.opts.resultantMaxAddCount = 0;
						this.opts.resultAddDifferenceCount = 0;
						//成功判断打击直拳 todo return
						this.event.trigger('resultantHit', {
							type: "hit",
							acc: this.opts.resultantMaxDifference,
							power: Math.ceil(this.opts.resultantMaxDifference * this.opts.mass),
							mass: this.opts.mass
						});


						this.opts.oldResultantDifference = 0;
						this.opts.resultantDifference = 0;
						this.opts.resultantMaxDifference = 0;
						this.opts.resultantMaxX = 0;
						this.opts.resultantMaxZ = 0;
						this.opts.resultantMaxY = 0;

					}

				}
			}
		}

		this.opts.oldResultantDifference = this.opts.resultantDifference;

	}
	this.opts.oldxA = this.opts.xA;
	this.opts.oldzA = this.opts.zA;
	this.opts.oldyA = this.opts.yA;
};

EquipmentAction.prototype.updateJumpAndRun = function () {
	let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
	//使用三个轴的数据，计算重力轴的加速度。最后减去重力的加速度值

	//********加速计********
	let { ax, ay, az } = data.acc;
	ax = ax * 9.80665;
	ay = ay * 9.80665;
	az = az * 9.80665;
	let gravityNew = Math.sqrt(ax * ax + ay * ay + az * az);

	let { gx, gy, gz } = data.gyro;
	let gyroNew = Math.sqrt(gx * gx + gy * gy + gz * gz);


	this.detectorNewStep(gravityNew, gyroNew);
};



/*
 * 检测步子，并开始计步
 * 1.传入数据
 * 2.如果检测到了波峰，并且符合时间差以及阈值的条件，则判定为1步
 * 3.符合时间差条件，波峰波谷差值大于initialValue，则将该差值纳入阈值的计算中
 * */
EquipmentAction.prototype.detectorNewStep = function (values, gyroValue) {
	if (this.gyroOpts.gravityOld == 0) {
		this.gyroOpts.gravityOld = gyroValue;
	} else {

		if (this.gyroDetectorPeak(gyroValue, this.gyroOpts.gravityOld)) {
			let step1 = this.gyroOpts.peakOfWave - this.gyroOpts.valleyOfWave;
			// if(step>10)
			// 	console.log("Gyro:", step);

			if (step1 > this.gyroOpts.ThreadValue) {
				this.gyroOpts.timeOfThisPeak = this.gyroOpts.timeOfNow;
				this.gyroOpts.bJump = true;
			}
		}
	}
	if (this.runOpts.gravityOld == 0) {
		this.runOpts.gravityOld = values;
	} else {
		if (this.detectorPeak(values, this.runOpts.gravityOld)) {
			this.runOpts.timeOfLastPeak = this.runOpts.timeOfThisPeak;
			this.runOpts.timeOfNow = new Date().getTime();
			let step2 = this.runOpts.peakOfWave - this.runOpts.valleyOfWave;

			if (this.runOpts.timeOfNow - this.runOpts.timeOfLastPeak >= this.runOpts.TimeInterval
				&& step2 > this.runOpts.ThreadValue) {
				this.runOpts.timeOfThisPeak = this.runOpts.timeOfNow;
				this.countRunStep();
			}
			// if (this.runOpts.timeOfNow - this.runOpts.timeOfLastPeak >= this.runOpts.TimeInterval
			// 	&& (this.runOpts.peakOfWave - this.runOpts.valleyOfWave >= this.runOpts.InitialValue)) {
			// 	this.runOpts.timeOfThisPeak = this.runOpts.timeOfNow;
			// 	this.runOpts.ThreadValue = this.peakValleyThread(this.runOpts.peakOfWave - this.runOpts.valleyOfWave);
			// }
		}
	}
	this.runOpts.gravityOld = values;
	this.gyroOpts.gravityOld = gyroValue;
}

EquipmentAction.prototype.countRunStep = function () {

	this.runOpts.timeOfLastStep = this.runOpts.timeOfThisStep;
	this.runOpts.timeOfThisStep = new Date().getTime();
	let diffValue = this.runOpts.timeOfThisStep - this.runOpts.timeOfLastStep;
	if (diffValue <= 2000) {

		if (this.gyroOpts.bJump) {
			let step = this.runOpts.peakOfWave - this.runOpts.valleyOfWave;
			this.event.trigger('resultant', {
				type: "jump",
				acc: step
			});
			this.resetJumpValue();
			// this.resSomeValue();
		} 
		// else 
		// {
			this.runOpts.averageTimeOfEveryStep += diffValue;
			this.runOpts.stepCount++;
			// if (this.runOpts.stepCount == 2) 
			{
				this.runOpts.averageTimeOfEveryStep = this.runOpts.averageTimeOfEveryStep / 2;
				// this.runOpts.currentState = "runing";
				let step = this.runOpts.peakOfWave - this.runOpts.valleyOfWave;
				this.resSomeValue();
				this.event.trigger('resultant', {
					type: "runing",
					acc: step
				});
			}
		// }

	} else { //超时，说明没有运动了
		this.resSomeValue();
		console.log("超时", "averageTimeOfEveryStep: " + this.runOpts.averageTimeOfEveryStep);
		if (this.gyroOpts.bJump) {
			//判断是否跳
			let step = this.runOpts.peakOfWave - this.runOpts.valleyOfWave;
			this.event.trigger('resultant', {
				type: "jump",
				acc: step
			});
			this.resetJumpValue();
		}
	}

}


EquipmentAction.prototype.resSomeValue = function () {
	this.runOpts.stepCount = 0;
	this.runOpts.averageTimeOfEveryStep = 0;


}

EquipmentAction.prototype.resetJumpValue = function () {
	// this.runOpts.jumpStepCount = 0;
	// this.runOpts.jumpAverageTimeOfEveryStep = 0;
	this.gyroOpts.bJump = false;
}


/*
 * 检测波峰
 * 以下四个条件判断为波峰：
 * 1.目前点为下降的趋势：isDirectionUp为false
 * 2.之前的点为上升的趋势：lastStatus为true
 * 3.到波峰为止，持续上升大于等于2次
 * 4.波峰值大于20
 * 记录波谷值
 * 1.观察波形图，可以发现在出现步子的地方，波谷的下一个就是波峰，有比较明显的特征以及差值
 * 2.所以要记录每次的波谷值，为了和下次的波峰做对比
 * */
EquipmentAction.prototype.detectorPeak = function (newValue, oldValue) {
	this.runOpts.lastStatus = this.runOpts.isDirectionUp;
	if (newValue >= oldValue) {
		this.runOpts.isDirectionUp = true;
		this.runOpts.continueUpCount++;
	} else {
		this.runOpts.continueUpFormerCount = this.runOpts.continueUpCount;
		this.runOpts.continueUpCount = 0;
		this.runOpts.isDirectionUp = false;
	}
	//this.runOpts.continueUpFormerCount >= 2 || oldValue >= 20
	if (!this.runOpts.isDirectionUp && this.runOpts.lastStatus &&
		(this.runOpts.continueUpFormerCount >= 2)) {
		this.runOpts.peakOfWave = oldValue;
		return true;
	} else if (!this.runOpts.lastStatus && this.runOpts.isDirectionUp) {
		this.runOpts.valleyOfWave = oldValue;
		return false;
	} else {
		return false;
	}
}

/*
 * 检测波峰
 * 以下四个条件判断为波峰：
 * 1.目前点为下降的趋势：isDirectionUp为false
 * 2.之前的点为上升的趋势：lastStatus为true
 * 3.到波峰为止，持续上升大于等于2次
 * 4.波峰值大于20
 * 记录波谷值
 * 1.观察波形图，可以发现在出现步子的地方，波谷的下一个就是波峰，有比较明显的特征以及差值
 * 2.所以要记录每次的波谷值，为了和下次的波峰做对比
 * */
EquipmentAction.prototype.gyroDetectorPeak = function (newValue, oldValue) {
	this.gyroOpts.lastStatus = this.gyroOpts.isDirectionUp;
	if (newValue >= oldValue) {
		this.gyroOpts.isDirectionUp = true;
		this.gyroOpts.continueUpCount++;
	} else {
		this.gyroOpts.continueUpFormerCount = this.gyroOpts.continueUpCount;
		this.gyroOpts.continueUpCount = 0;
		this.gyroOpts.isDirectionUp = false;
	}
	//this.runOpts.continueUpFormerCount >= 2 || oldValue >= 20
	if (!this.gyroOpts.isDirectionUp && this.gyroOpts.lastStatus &&
		(this.gyroOpts.continueUpFormerCount >= 2)) {
		this.gyroOpts.peakOfWave = oldValue;
		return true;
	} else if (!this.gyroOpts.lastStatus && this.gyroOpts.isDirectionUp) {
		this.gyroOpts.valleyOfWave = oldValue;
		return false;
	} else {
		return false;
	}
}

/*
 * 阈值的计算
 * 1.通过波峰波谷的差值计算阈值
 * 2.记录4个值，存入tempValue[]数组中
 * 3.在将数组传入函数averageValue中计算阈值
 * */
EquipmentAction.prototype.peakValleyThread = function (value) {
	let tempThread = this.runOpts.ThreadValue;
	if (this.runOpts.tempCount < this.runOpts.ValueNum) {
		this.runOpts.tempValue[this.runOpts.tempCount] = value;
		this.runOpts.tempCount++;
	} else {
		tempThread = this.averageValue(this.runOpts.tempValue, this.runOpts.ValueNum);
		for (let i = 1; i < this.runOpts.ValueNum; i++) {
			this.runOpts.tempValue[i - 1] = this.runOpts.tempValue[i];
		}
		this.runOpts.tempValue[this.runOpts.ValueNum - 1] = value;
	}
	return tempThread;
}

/*
 * 梯度化阈值
 * 1.计算数组的均值
 * 2.通过均值将阈值梯度化在一个范围里
 * */
EquipmentAction.prototype.averageValue = function (value, n) {
	let ave = 0;
	for (let i = 0; i < n; i++) {
		ave += value[i];
	}
	ave = ave / this.runOpts.ValueNum;
	// console.log("ave:", ave);
	if (ave >= 8)
		ave = 4.3;
	else if (ave >= 7 && ave < 8)
		ave = 3.3;
	else if (ave >= 4 && ave < 7)
		ave = 2.3;
	else if (ave >= 3 && ave < 4)
		ave = 2.0;
	else {
		ave = 1.3;
	}
	return ave;
}



if (typeof module === "object" && typeof module.exports === "object") {
	module.exports = EquipmentAction;
	//export default EquipmentAction;//建议使用nodejs的module导出方式，如报错请使用export方式导出
}
