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

/**
 * 跟随组件类,用于以指定角度和距离平滑地跟随目标对象。
 * @class FollowerComponent
 * @memberof THING
 * @extends THING.BaseComponent
 * @public
 */
class FollowerComponent extends BaseComponent {

	/**
	 * 用于以指定角度和距离平滑地跟随目标对象。
	 * @example
	 * 	camera.addComponent(THING.FollowerComponent, 'follower');
	 * 	camera.follower.start(THING.App.current.query('.car01')[0]);
	 */
	constructor() {
		super();

		this._tracingObject = null;
		this._options = {};

		this._init();
	}

	// #region Private Functions

	// Update.
	_update() {
		var tracingObject = this._tracingObject;

		// If the target had been destroyed then stop to follow
		if (tracingObject.destroyed) {
			this.stop();
			return;
		}

		// Get the options
		var options = this._options;
		var direction = options['direction'];
		var factor = options['factor'];
		var speed = options['speed'];

		// Get the center position to look at
		var orientedBox = tracingObject.orientedBox;
		var center = orientedBox.center;
		var radius = orientedBox.radius;
		var distance = radius * factor;

		// Get the follower position
		var position = MathUtils.addVector(center, MathUtils.scaleVector(direction, distance));

		// Get the current camera position and target
		var object = this.object;
		var cameraPosition = MathUtils.lerpVector(object.position, position, speed);
		var cameraTarget = MathUtils.lerpVector(object.target, center, speed);
		object.position = cameraPosition;
		object.target = cameraTarget;

		// Check whether reach target
		const epsilon = 0.0001;
		const positionDistance = MathUtils.getDistance(cameraPosition, position);
		const targetDistance = MathUtils.getDistance(cameraTarget, center);
		if (targetDistance < epsilon && positionDistance < epsilon) {
			// Check whether to stop when reach target
			var once = options['once'];
			if (once) {
				this.stop();
			}
		}
	}

	// Initialize options.
	_initOptions() {
		this._options = {
			horzAngle: 45,
			vertAngle: 45,
			direction: null,
			factor: 2.5,
			speed: 0.1,
		};
	}

	// Update direction.
	_updateDirection() {
		var options = this._options;

		options['direction'] = MathUtils.getDirectionFromAngles(options['horzAngle'], options['vertAngle']);
	}

	// Initialize.
	_init() {
		this._initOptions();
	}

	// #endregion

	// #region BaseComponent Interface

	onUpdate(deltaTime) {
		if (this._tracingObject) {
			this._update();
		}
	}

	// #endregion

	/**
	 * 停止跟随对象。
	 * @public
	 */
	stop() {
		this._tracingObject = null;

		this._initOptions();
	}

	/**
	 * 开始跟随对象。
	 * @param {THING.BaseObject} object 要跟随的对象。
	 * @param {object} options 选项。
	 * @public
	 */
	start(object, options = {}) {
		if (!object) {
			return;
		}

		// Stop current following action
		this.stop();

		// Parse options
		this._options['horzAngle'] = Utils.parseValue(options['horzAngle'], this._options['horzAngle']);
		this._options['vertAngle'] = Utils.parseValue(options['vertAngle'], this._options['vertAngle']);
		this._options['factor'] = Utils.parseValue(options['factor'], this._options['factor']);
		this._options['speed'] = Utils.parseValue(options['speed'], this._options['speed']);
		this._options['once'] = Utils.parseValue(options['once'], false);

		// Start to follow object
		this._tracingObject = object;

		// Update direction
		this._updateDirection();
	}

	/**
	 * 获取正在跟随的对象。
	 * @type {THING.BaseObject}
	 * @public
	 */
	get tracingObject() {
		return this._tracingObject;
	}

	/**
	 * 获取/设置水平角度。
	 * @type {number}
	 * @public
	 */
	get horzAngle() {
		return this._options['horzAngle'];
	}
	set horzAngle(value) {
		this._options['horzAngle'] = value;

		this._updateDirection();
	}

	/**
	 * 获取/设置垂直角度。
	 * @type {number}
	 * @public
	 */
	get vertAngle() {
		return this._options['vertAngle'];
	}
	set vertAngle(value) {
		this._options['vertAngle'] = value;

		this._updateDirection();
	}

	/**
	 * 获取/设置距离因子。
	 * @type {number}
	 * @public
	 */
	get factor() {
		return this._options['factor'];
	}
	set factor(value) {
		this._options['factor'] = value;
	}

	/**
	 * 获取/设置速度。
	 * @type {number}
	 * @public
	 */
	get speed() {
		return this._options['speed'];
	}
	set speed(value) {
		this._options['speed'] = value;
	}

}

export { FollowerComponent }