import { ObjectProxy } from '@uino/base-thing';
import { Utils } from '../common/Utils'


const __ = {
	private: Symbol('private'),
}


/**
 * @class ParticleEmitter
 * 粒子发射器。
 * @memberof THING
 * @public
 * @example
 * const emitter = new THING.ParticleEmitter();
 * emitter.particleCount = 100; // 粒子数量
 * emitter.distribution = 1; // 分布方式
 * emitter.duration = 2; // 持续时间
 * emitter.isLookAtCamera = true; // 是否朝向摄像机
 * emitter.isLookAtCameraY = true; // 是否锁定Y轴朝向摄像机
 * emitter.wiggle = {
 * 	spread:0, // 抖动范围
 * 	value:0 // 抖动值
 * };
 * emitter.rotation = {
 * 	axisSpread:[0, 0, 0], // 旋转轴的变化范围
 * 	randomise:false, // 是否随机旋转
 * 	angleSpread:0, // 旋转角度的变化范围
 * 	center:[0, 0, 0], // 旋转中心点
 * 	angle:0, // 旋转角度
 * 	static:false, // 是否静态旋转
 * 	axis:[0, 1, 0] // 旋转轴
 * };
 * emitter.velocity = {
 * 	randomise:false, // 是否随机速度
 * 	value:[0, 25, 0], // 速度值
 * 	distribution:1, // 分布方式
 * 	spread:[10, 7.5, 10] // 速度的变化范围
 * };
 * emitter.size = {
 * 	randomise:[false, false], // 是否随机大小
 * 	spread:[0, 0], // 大小的变化范围
 * 	value:[2, 0] // 大小值
 * };
 * emitter.angle = {
 * 	randomise:[false, false], // 是否随机角度
 * 	spread:[0, 0], // 角度的变化范围
 * 	value:[1, 0] // 角度值
 * };
 * emitter.opacity = {
 * 	randomise:[false, false], // 是否随机透明度
 * 	spread:[0, 0], // 透明度的变化范围
 * 	value:[1, 0.1] // 透明度值
 * };
 * emitter.color = {
 * 	randomise:[false, false], // 是否随机颜色
 * 	spread:[[0, 0, 0], [0, 0, 0]], // 颜色的变化范围
 * 	value:[[0, 0, 0], [1, 1, 0]] // 颜色值
 * };
 * emitter.acceleration = {
 * 	randomise:false, // 是否随机加速度
 * 	value:[0, -10, 0], // 加速度值
 * 	distribution:1, // 分布方式
 * 	spread:[0, 0, 0] // 加速度的变化范围
 * };
 * emitter.maxAge = {
 * 	spread:0, // 最大寿命的变化范围
 * 	value:2 // 最大寿命值
 * };
 * emitter.position = {
 *  randomise:false, // 是否随机位置
 *  radius:10, // 半径
 *  spreadClamp:[0, 0, 0], // 位置的变化范围
 *  value:[0, 0, -50], // 位置值
 *  radiusScale:[1, 1, 1], // 半径的缩放比例
 *  distribution:1, // 分布方式
 *  spread:[0, 0, 0] // 位置的变化范围
 * };
 * emitter.drag = {
 *  randomise:false, // 是否随机阻力
 *  spread:0, // 阻力的变化范围
 *  value:0 // 阻力值
 * };
 */
class ParticleEmitter {

	constructor(param = {}) {
		this.node = param['node'] || Utils.createObject('ParticleEmitter');

		this[__.private] = {};
		let _private = this[__.private];

		_private.maxAge = this.node.getAttribute('MaxAge');

		_private.position = this.node.getAttribute('Position');
		_private.rotation = this.node.getAttribute('Rotation');
		_private.velocity = this.node.getAttribute('Velocity');

		_private.listColor = this.node.getAttribute('ListColor');

		_private.listOpacity = this.node.getAttribute('ListOpacity');
		_private.listSize = this.node.getAttribute('ListSize');
		_private.listAngle = this.node.getAttribute('ListAngle');

		_private.acceleration = this.node.getAttribute('Acceleration');

		_private.drag = this.node.getAttribute('Drag');

		_private.onChange = null;

		const that = this;
		_private.objectProxy = new ObjectProxy({
			data: {
				MaxAge: _private.maxAge,
				Position: _private.position,
				Rotation: _private.rotation,
				Velocity: _private.velocity,
				ListColor: _private.listColor,
				ListOpacity: _private.listOpacity,
				ListSize: _private.listSize,
				ListAngle: _private.listAngle,
				Acceleration: _private.acceleration,
				Drag: _private.drag,
			},
			onChange: function (ev) {
				that.setAttribute(ev.propName, ev.data[ev.propName]);

				if (_private.onChange) {
					_private.onChange(ev);
				}
			}
		});
	}

	/**
	 * 获取属性值。
	 * @param {THING.ADAPTER.ParticleEmitterAttributeType} key 属性键
	 * @returns {any} 返回属性值。
	 * @public
	 */
	getAttribute(key) {
		return this.node.getAttribute(key);
	}

	/**
	 * 设置属性值。
	 * @param {THING.ADAPTER.ParticleEmitterAttributeType} key 属性键
	 * @param {any} value 属性值
	 * @public
	 */
	setAttribute(key, value) {
		this.node.setAttribute(key, value);
	}

	// #region  Accessors

	/**
	 * 获取/设置粒子数量。渲染时的粒子数量。
	 * @type {number}
	 * @public
	 */
	get particleCount() {
		return this.getAttribute('ParticleCount');
	}
	set particleCount(value) {
		this.setAttribute('ParticleCount', value);
	}

	/**
	 * 获取/设置方向。粒子的方向。如果值为 `1`,粒子将从粒子生命周期的开始位置开始。如果值为 `-1`,粒子将从粒子生命周期的结束位置开始,并逆向运动。
	 * @type {number}
	 * @public
	 */
	get direction() {
		return this.getAttribute('Direction');
	}
	set direction(value) {
		this.setAttribute('Direction', value);
	}

	/**
	 * 获取/设置是否静态。如果为 true,则表示这些粒子不会被模拟。
	 * @type {boolean}
	 * @public
	 */
	get isStatic() {
		return this.getAttribute('IsStatic');
	}
	set isStatic(value) {
		this.setAttribute('IsStatic', value);
	}

	/**
	 * 获取/设置活跃倍数。介于 0 和 1 之间的值,描述应该发射的粒子数量的百分比,其中 0 表示 0%,1 表示 100%。
	 * @type {number}
	 * @public
	 */
	get activeMultiplier() {
		return this.getAttribute('ActiveMultiplier');
	}
	set activeMultiplier(value) {
		this.setAttribute('ActiveMultiplier', value);
	}

	/**
	 * 获取/设置分布方式。用于控制粒子的生成位置和力的行为。选项有 1(盒子)、2(球体)、3(圆盘)、4(线段)。
	 * @type {number}
	 * @public
	 */
	get distribution() {
		return this.getAttribute('Distribution');
	}
	set distribution(value) {
		this.setAttribute('Distribution', value);
	}

	/**
	 * 获取/设置持续时间。以秒为单位,表示此发射器应持续存在的时间。如果未指定,则发射器将无限期地发射粒子。
	 * @type {number|null}
	 * @public
	 */
	get duration() {
		return this.getAttribute('Duration');
	}
	set duration(value) {
		this.setAttribute('Duration', value);
	}


	/**
	 * 获取/设置是否始终朝向相机。当 UseMesh 为 true 时生效。如果为 true,则表示其粒子将始终朝向相机。
	 * @type {boolean}
	 * @public
	 */
	get isLookAtCamera() {
		return this.getAttribute('IsLookAtCamera');
	}
	set isLookAtCamera(value) {
		this.setAttribute('IsLookAtCamera', value);
	}

	/**
	 * 获取/设置是否始终朝向相机并锁定 Y 轴。当 UseMesh 为 true 时生效。如果为 true,则表示其粒子将始终朝向相机并锁定 Y 轴。
	 * @type {boolean}
	 * @public
	 */
	get isLookAtCameraY() {
		return this.getAttribute('IsLookAtCameraY');
	}
	set isLookAtCameraY(value) {
		this.setAttribute('IsLookAtCameraY', value);
	}

	/**
	 * The particle's maximum age attribute.
	 * @typedef {object} MaxAge
	 * @property {number} value A number describing the amount of maxAge to apply to all particles.
	 * @property {number} spread A number describing the maxAge variance on a per-particle basis.
	 */

	/**
	 * 获取/设置最大寿命属性。
	 * @type {MaxAge}
	 * @public
	 */
	get maxAge() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.MaxAge;
	}
	set maxAge(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.maxAge[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's position attribute.
	 * @typedef {object} Position
	 * @property {Array<number>} value An array describing this emitter's base position.
	 * @property {Array<number>} spread An array describing this emitter's position variance on a per-particle basis.
	 * @property {Array<number>} spreadClamp An array describing the numeric multiples the particle's should be spread out over.
	 * @property {number} radius This emitter's base radius.
	 * @property {Array<number>} radiusScale An array describing the radius's scale in all three axes.
	 * @property {number} distribution A specific distribution to use when radiusing particles. Overrides the `ParticleEmitterAttributeType.Distribution` option.
	 * @property {boolean} randomise When a particle is re-spawned, whether it's position should be re-randomised or not.
	 */

	/**
	 * 获取位置。
	 * @type {Position}
	 * @public
	 */
	get position() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.Position;
	}
	set position(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.position[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's rotation attribute.
	 * @typedef {object} Rotation
	 * @property {Array<number>} axis An array describing this emitter's axis of rotation.
	 * @property {Array<number>} axisSpread An array describing the amount of variance to apply to the axis of rotation on a per-particle basis.
	 * @property {number} angle The angle of rotation, given in radians. If `Rotation.static` is true, the emitter will start off rotated at this angle, and stay as such.
	 *                          Otherwise, the particles will rotate from 0radians to this value over their lifetimes.
	 * @property {number} angleSpread The amount of variance in each particle's rotation angle.
	 * @property {boolean} static Whether the rotation should be static or not.
	 * @property {Array<number>} center An array describing the center point of rotation.
	 * @property {boolean} randomise When a particle is re-spawned, whether it's rotation should be re-randomised or not.
	 */

	/**
	 * 获取旋转属性。
	 * @type {Rotation}
	 * @public
	 */
	get rotation() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.Rotation;
	}
	set rotation(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.rotation[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's velocity attribute.
	 * @typedef {object} Velocity
	 * @property {Array<number>} value An array describing this emitter's base velocity.
	 * @property {Array<number>} spread An array describing this emitter's velocity variance on a per-particle basis.
	 * @property {number} distribution A specific distribution to use when calculating a particle's velocity. Overrides the `ParticleEmitterAttributeType.Distribution` option.
	 * @property {boolean} randomise When a particle is re-spawned, whether it's velocity should be re-randomised or not.
	 */

	/**
	 * 获取速度。
	 * @type {Velocity}
	 * @public
	 */
	get velocity() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.Velocity;
	}
	set velocity(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.velocity[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's color attribute.
	 * @typedef {object} ListColor
	 * @property {Array<number>} value Either an array, or multiple arrays to describe the color of a particle over it's lifetime.
	 * @property {Array<number>} spread Either an array, or multiple arrays to describe the color variance of a particle over it's lifetime.
	 * @property {boolean} randomise When a particle is re-spawned, whether it's color should be re-randomised or not.
	 */

	/**
	 * 获取颜色。
	 * @type {ListColor}
	 * @public
	 */
	get color() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.ListColor;
	}
	set color(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.color[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's opacity attribute.
	 * @typedef {object} ListOpacity
	 * @property {number} value Either a number, or an array of numbers to describe the opacity of a particle over it's lifetime.
	 * @property {number} spread Either a number, or an array of numbers to describe the opacity variance of a particle over it's lifetime.
	 * @property {boolean} randomise When a particle is re-spawned, whether it's opacity should be re-randomised or not.
	 */

	/**
	 * 获取不透明度。
	 * @type {ListOpacity}
	 * @public
	 */
	get opacity() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.ListOpacity;
	}
	set opacity(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.opacity[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's size attribute.
	 * @typedef {object} ListSize
	 * @property {number} value Either a number, or an array of numbers to describe the size of a particle over it's lifetime.
	 * @property {number} spread Either a number, or an array of numbers to describe the size variance of a particle over it's lifetime.
	 * @property {boolean} randomise When a particle is re-spawned, whether it's size should be re-randomised or not.
	 */

	/**
	 * 获取尺寸。
	 * @type {ListSize}
	 * @public
	 */
	get size() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.ListSize;
	}
	set size(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.size[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's angle attribute.
	 * @typedef {object} ListAngle
	 * @property {number} value Either a number, or an array of numbers to describe the angle of a particle over it's lifetime.
	 * @property {number} spread Either a number, or an array of numbers to describe the angle variance of a particle over it's lifetime.
	 * @property {boolean} randomise When a particle is re-spawned, whether it's angle should be re-randomised or not.
	 */

	/**
	 * 获取角度。
	 * @type {ListAngle}
	 * @public
	 */
	get angle() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.ListAngle;
	}
	set angle(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.angle[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's acceleration attribute.
	 * @typedef {object} Acceleration
	 * @property {Array<number>} value An array describing this emitter's base acceleration.
	 * @property {Array<number>} spread An array describing this emitter's acceleration variance on a per-particle basis.
	 * @property {number} distribution A specific distribution to use when calculating a particle's acceleration. Overrides the `ParticleEmitterAttributeType.Distribution` option.
	 * @property {boolean} randomise When a particle is re-spawned, whether it's acceleration should be re-randomised or not.
	 */

	/**
	 * 获取加速度。
	 * @type {Acceleration}
	 * @public
	 */
	get acceleration() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.Acceleration;
	}
	set acceleration(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.acceleration[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's drag attribute.
	 * @typedef {object} Drag
	 * @property {number} value A number between 0 and 1 describing the amount of drag to apply to all particles.
	 * @property {number} spread A number describing the drag variance on a per-particle basis.
	 * @property {boolean} randomise When a particle is re-spawned, whether it's drag should be re-randomised or not.
	 */

	/**
	 * 获取拖拽属性。
	 * @type {Drag}
	 * @public
	 */
	get drag() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.Drag;
	}
	set drag(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.drag[i] = value[i];
					break;
			}
		}
	}

	/**
	 * Get/Set when change callback function.
	 * @type {Function}
	 * @private
	 */
	get onChange() {
		let _private = this[__.private];

		return _private.onChange;
	}
	set onChange(value) {
		let _private = this[__.private];

		_private.onChange = value;
	}

	// #endregion

}


export { ParticleEmitter }