import { Utils } from '@uino/base-thing';
import { MathUtils, BaseComponent, EventType } from '@uino/thing';


const maxPitch = (85 / 180) * Math.PI;
// the camera's position will be changed by tween when it be attached,
// so we should make the dirty  state for a period of time to ensure 
// the camera reach a stable state
const attachingTime = 1;
function _initprotectedMembers(that, param) {
	that[that._protected] = {};
	const _protected = that[that._protected];

	_protected._attachingTime = 0;
	_protected._objectName = '';
	_protected._attachCamera = null;
	_protected._attaching = false;
	_protected._yaw = 0;
	_protected._yawSpeed = 1;
	_protected._pitch = 0;
	_protected._pitchSpeed = 1;
	_protected._mouseMoveX = 0.0;
	_protected._mouseMoveY = 0.0;
	_protected._mouseOppositeMode = false;
	_protected._minPitch = -maxPitch;
	_protected._maxPitch = maxPitch;
	_protected._dirty = false;

	_protected.checkAttaching = (time) => {
		if (_protected._attaching) {
			_protected._attachingTime += time;
			if (_protected._attachingTime >= attachingTime) {
				_protected._attachingTime = 0;
				_protected._attaching = false;
			}
		}
		return _protected._attaching;
	};
	_protected.resetPitchByAttachCamera = () => {
		let cameraForward = _protected._attachCamera.forward;
		cameraForward = MathUtils.normalizeVector(cameraForward);
		let cameraForwardWithZeroY = [cameraForward[0], 0, cameraForward[2]];
		cameraForwardWithZeroY = MathUtils.normalizeVector(cameraForwardWithZeroY);
		let dot = MathUtils.dotVector(cameraForward, cameraForwardWithZeroY);
		_protected._pitch = MathUtils.acos(dot);
		if (cameraForward[1] < 0) {
			_protected._pitch = -_protected._pitch;
		}
	}
	_protected.resetYawByAttachCamera = () => {
		let cameraForward = _protected._attachCamera.forward;
		cameraForward = MathUtils.normalizeVector(cameraForward);
		let cameraForwardWithZeroY = [cameraForward[0], 0, cameraForward[2]];
		cameraForwardWithZeroY = MathUtils.normalizeVector(cameraForwardWithZeroY);
		let vecNorth = [0, 0, -1];
		let dot = MathUtils.dotVector(vecNorth, cameraForwardWithZeroY);
		_protected._yaw = MathUtils.acos(dot);
		if (cameraForwardWithZeroY[0] > 0) {
			_protected._yaw = 2 * Math.PI - _protected._yaw;
		}
	}
	_protected.resetCameraTarget = () => {
		if (!!_protected._attachCamera) {
			let pos = _protected._attachCamera.position;
			let dir = _protected._attachCamera.forward;
			// let dis = _protected._attachCamera.distance;
			// 使用20替代dis,避免因为前进导致dis越来越小,从而引起如下的bug
			// bug: 长按W前进,然后按下Q转向一段时间抬起,按下D右移,抬起后方向突变
			let delta_vec = MathUtils.scaleVector(dir, 20);
			_protected._attachCamera.target = MathUtils.addVector(pos, delta_vec);
		}
	}

	_protected.resetYaw = () => {
		var preX = _protected._mouseMoveX;
		if (!!_protected._mouseOppositeMode) {
			preX = -preX;
		}
		var yawDelta = preX * _protected._yawSpeed;
		_protected._yaw = _protected._yaw + yawDelta;
		_protected._dirty = true;
	}
	_protected.resetPitch = () => {
		var preY = _protected._mouseMoveY;
		if (!!_protected._mouseOppositeMode) {
			preY = -preY;
		}
		var pitchDelta = preY * _protected._pitchSpeed;
		_protected._pitch = _protected._pitch + pitchDelta;

		if (_protected._pitch > _protected._maxPitch) {
			_protected._pitch = _protected._maxPitch;
		}
		else if (_protected._pitch <= _protected._minPitch) {
			_protected._pitch = _protected._minPitch;
		}
		_protected._dirty = true;
	}
}

/**
 * @class CameraAttachedComponent
 * @summary 相机附加组件,用于将摄像机绑定到某个对象上并跟随其变换。
 * 提供摄像机偏航(Yaw)和俯仰(Pitch)的角度控制接口,支持鼠标反向模式,
 * 是 FlyControlComponent 和第三人称摄像机控制的底层基础组件。
 * 组件会将目标对象的变换应用到绑定的摄像机,使摄像机的朝向和位置与对象保持关联。
 * @memberof THING
 * @extends THING.BaseComponent
 * @public
 */
class CameraAttachedComponent extends BaseComponent {

	static beAttached = false;
	/**
	 * 相机附加组件类构造函数,用于管理相机附加组件。
	 * @param {object} param 参数列表
	 */
	constructor(param = {}) {
		super(param);

		this._protected = Symbol('protected');
		_initprotectedMembers(this, param);
	}

	/**
	 * 设置鼠标反向模式。
	 * 当反向为true时,鼠标向右转动时,玩家将向左转动。
	 * 俯仰操作也会相应反向。
	 * @param {bool} opposite 是否启用鼠标反向模式
	 */
	setMouseOppositeMode(opposite) {
		var _protected = this[this._protected];
		_protected._mouseOppositeMode = opposite;
	}

	/**
	 * 设置偏航速度
	 * @param {number} speed 相机水平旋转速度
	 */
	setYawSpeed(speed) {
		var _protected = this[this._protected];
		_protected._yawSpeed = speed;
	}
	/**
	 * 设置俯仰速度
	 * @param {number} speed 相机垂直旋转速度
	 */
	setPitchSpeed(speed) {
		var _protected = this[this._protected];
		_protected._pitchSpeed = speed;
	}
	/**
	 * 设置附加相机
	 * @param {Thing.Camera} attachCamera 要附加的相机
	 */
	setAttachCamera(attachCamera) {
		if (!!CameraAttachedComponent.beAttached) {
			Utils.error("it already has camera be attached!!");
			return;
		}
		CameraAttachedComponent.beAttached = true;

		var _protected = this[this._protected];
		_protected._attachCamera = attachCamera;
		_protected._oriCameraEnable = _protected._attachCamera.enable;
		_protected._attachCamera.enable = false;
		_protected.resetYawByAttachCamera();
		_protected.resetPitchByAttachCamera();
		_protected.resetCameraTarget();
		_protected._attaching = true;
		_protected._dirty = true;

		// this.addEventListener('change', (ev) => {
		// 	var _position = [];
		// 	var _target = [];
		// 	this.object.getWorldPosition(_position);
		// 	this.object.control.getTarget(_target);
		// 	this.app.trigger(EventType.CameraChange, {
		// 		position: _position,
		// 		target: _target
		// 	});
		// });
		// this.addEventListener('wheel', (ev) => {
		// 	var _position = [];
		// 	var _target = [];
		// 	this.object.getWorldPosition(_position);
		// 	this.object.control.getTarget(_target);
		// 	this.app.trigger(EventType.CameraChange, {
		// 		position: _position,
		// 		target: _target
		// 	});
		// });
	}

	onAdd(object) {
		super.onAdd(object);
	}

	onRemove() {
		var _protected = this[this._protected];
		if (!!_protected._attachCamera) {
			_protected._attachCamera.enable = _protected._oriCameraEnable;
		}
		_protected.resetCameraTarget();
		CameraAttachedComponent.beAttached = false;

		super.onRemove();
	}

	onUpdate(deltaTime) {
		var _protected = this[this._protected];
		if (!_protected._attachCamera) {
			return;
		}
		_protected.resetCameraTarget();
		_protected._attachCamera.onUpdate(0);
		_protected._dirty = _protected.checkAttaching(deltaTime);
	}

}
export { CameraAttachedComponent }