/**
 * Created by rockyl on 2019-11-20.
 */

export default class Wave extends engine.ScriptBase {
	static id = 'wave';

	duration: number = 1000;
	@engine.dirtyFieldTrigger
	waveMethod: string = 'rotate';
	waveParams: string;
	loop: number = -1;
	autoPlay: boolean = true;
	ratio: string;
	revert: boolean = false;

	private _playing;
	private _waveAlgorithm;
	private _startTime;
	private _oldProps: any = {};
	private _loopCounting;

	mounted() {
		this._startTime = 0;

		engine.copyProp(this._oldProps, this.host, oldPropFields);

		if (this.autoPlay) {
			this.play();
		}
	}

	sleep(): void {
		if (this.revert) {
			this.revertProps();
		}
	}

	update(t) {
		if (this._playing) {
			if (!this._startTime) {
				this._startTime = t;
			}

			const {duration, waveParams, _waveAlgorithm, host, _oldProps} = this;

			let pass = (t - this._startTime) % duration;
			let r = pass / duration * PI2;

			let loopCounting = Math.floor((t - this._startTime) / duration);
			if (loopCounting != this._loopCounting) {
				this._loopCounting = loopCounting;
				if (this.onLoopEnd()) {
					r = PI2;
				}
			}

			let t2;
			if (this.ratio) {
				let ratio = this.ratio.split(',').map(item => parseFloat(item));
				if (ratio.length === 2) {
					const [begin, end] = ratio;
					if (end >= begin) {
						let tr = r / PI2;
						if (tr >= begin && tr < end) {
							t2 = r / (end - begin);
						} else {
							t2 = 0;
						}
					} else {
						t2 = 0;
					}
				} else {
					t2 = 0;
				}
			} else {
				t2 = r;
			}
			let params = waveParams ? (waveParams.split(',').map(item => parseFloat(item))) : [];
			let props = _waveAlgorithm(...params, t2);

			if (props.hasOwnProperty('x')) {
				host.x = (props.x || 0) + _oldProps.x;
			}
			if (props.hasOwnProperty('y')) {
				host.y = (props.y || 0) + _oldProps.y;
			}
			if (props.hasOwnProperty('sx')) {
				host.scaleX = props.sx;
			}
			if (props.hasOwnProperty('sy')) {
				host.scaleY = props.sy;
			}
			if (props.hasOwnProperty('r')) {
				host.rotation = props.r;
			}
			if (props.hasOwnProperty('alpha')) {
				host.alpha = props.alpha;
			}
		}
	}

	private onLoopEnd() {
		if (this.loop < 0) {
			//this.onLoopComplete.invoke();
		} else if (this._loopCounting < this.loop) {
			//this.onLoopComplete.invoke();
		} else {
			this._playing = false;
			//this.onComplete.invoke();
			return true;
		}
	}

	play() {
		this._loopCounting = 0;
		this._playing = true;
		this._startTime = 0;
	}

	/**
	 * 停止动画
	 * @param revert 是否恢复初始状态
	 */
	stop(revert = false) {
		this._playing = false;
		if (revert) {
			this.revertProps();
		}
	}

	revertProps() {
		for (let key in this._oldProps) {
			let prop = this._oldProps[key];
			if (typeof prop === 'object') {
				engine.injectProp(this.host[key], prop);
			} else {
				this.host[key] = prop;
			}
		}
	}

	protected onModify(value, key, oldValue) {
		switch (key) {
			case 'waveMethod':
				this._waveAlgorithm = waveLibs[this.waveMethod];
				break;
		}
	}
}

const PI2 = Math.PI * 2;

const oldPropFields = {
	x: 'x',
	y: 'y',
	scaleX: 'scaleX',
	scaleY: 'scaleY',
	alpha: 'alpha',
	rotation: 'rotation',
};

const {cos, sin, PI} = Math;

const waveLibs = {
	round: function (radius: number, t: number): any {
		return {x: cos(t) * radius, y: sin(t) * radius};
	},

	cosWave: function (h: number, t: number): any {
		return {x: cos(t) * h, y: 0};
	},

	sinWave: function (h: number, t: number): any {
		h = h || 1;
		return {x: 0, y: sin(t) * h};
	},

	rotate: function (t: number): any {
		return {r: 360 * t / PI / 2};
	},

	shake: function (angle: number, count: number, t: number): any {
		return {r: sin(t * count) * angle};
	},

	breath: function (scale: number = 0.1, t: number): any {
		return {sx: sin(t) * scale + 1, sy: -sin(t + PI / 4) * scale + 1};
	},

	zoom: function (scale: number = 0.1, t: number): any {
		return {sx: sin(t) * scale + 1, sy: sin(t) * scale + 1};
	},

	fade: function (base = 0, scale = 1, t: number): any {
		return {alpha: (sin(t) + 1) * 0.5 * scale + base};
	},
};
