import { Utils } from '../common/Utils';
import { MathUtils } from '../math/MathUtils';
import { Object3D } from './Object3D';

/**
 * @class BasePoints
 * 基础点对象。
 * @memberof THING
 * @extends THING.Object3D
 * @public
 */
class BasePoints extends Object3D {


	/**
	 * 基础点对象类,负责管理点的集合和相关操作
	 * @param {object} param 初始化参数。
	 * 参数与 {@link THING.Object3D} 的构造函数相同。
	 * 请在 {@link THING.Object3D} 的文档中查看构造函数部分以获取详细参数列表。
	 */
	constructor(param = {}) {
		super(param);
	}

	// #region 私有方法

	_adjustPosition(points) {
		let center = MathUtils.getCenterFromPoints(points);

		this.position = center;
	}

	// #endregion

	// #region 覆盖方法

	onDestroy() {
		// 清空点集引用,避免在对象销毁后还持有大量点数据导致内存泄漏
		this._selfPoints = null;

		super.onDestroy();
	}

	hasResource() {
		return true;
	}

	onSetupResource(param) {
		this._autoAdjustPosition = Utils.parseBoolean(param['autoAdjustPosition'], false);

		this.onSetupPoints(param);

		// Setup components
		const components = param['components'];
		const toolkit = param['toolkit'];
		if (components && toolkit) {
			this.onImportComponents(components, toolkit, param);
		}

		if (this.isDynamicLoad) {
			return;
		}

		this.onLoadRenderableResource(param, () => {
			this.onLoadComplete();
		}, (ev) => {
			if (this._loadPromise) {
				this._loadPromise.reject(ev);
			}

			// Notify error
			const onError = param['onError'] || param['error'];
			onError && onError(ev);
		})
	}

	onSetupPoints(param) {
		// Setup self points
		if (param['selfPoints']) {
			this._selfPoints = Utils.parseValue(param['selfPoints'], []);
		}
		else if (param['points']) {
			this._selfPoints = param['points'].map(point => {
				return this.worldToSelf(point);
			});
		}
		else {
			this._selfPoints = []
		}
	}

	onLoadRenderableResource(options, resolve, reject) {
		if (this.hasResource()) {
			const node = options['renderableNode'] || this.onCreateBodyNode(options);
			this.onBeginSetupBodyNode(node);
			this.onSetupBodyNode(node);
			this.onEndSetupBodyNode(node);
			this.body.setNode(node);
		}
		resolve && resolve();
	}

	onCreateBodyNode(param) {

	}

	// 开始设置body node 的参数
	onBeginSetupBodyNode(node) {
		if (!node) {
			return;
		}
		node.begin();
	}

	onSetupBodyNode(node) {
		const points = this.onGetSelfPoints(this._selfPoints);
		node.setPoints(points);
	}

	// 结束设置body node 的参数
	onEndSetupBodyNode(node) {
		if (!node) {
			return;
		}
		node.end();
	}

	onExportExternalData() {
		let external = Object.assign({}, super.onExportExternalData());
		Utils.setAttributeIfExist(external, 'autoAdjustPosition', this);
		Utils.setAttributeIfExist(external, 'selfPoints', this);
		return external;
	}

	onSetupPosition(param) {
		if (param['localPosition'] || param['position']) {
			super.onSetupPosition(param);
		}
		// 自动设置点集中心为线的位置
		else if (param['points']) {
			let center = MathUtils.getCenterFromPoints(param['points']);
			this.position = center;
		}
		else {
			super.onSetupPosition(param);
		}
	}

	/**
	 * 获取自身点集回调函数。
	 * @param {Array<Array<number>>} selfPoints 自身点集。
	 * @returns {Array<Array<number>>}
	 * @private
	 */
	onGetSelfPoints(selfPoints) {
		return selfPoints;
	}

	/**
	 * 设置体自身点集之前。
	 * @param {Array<Array<number>>} selfPoints 自身点集。
	 * @private
	 */
	onBeforeSetBodyPoints(selfPoints) {
	}

	/**
	 * 设置体自身点集之后。
	 * @private
	 */
	onAfterSetBodyPoints() {

	}

	// onRefresh() {
	// this.onRefreshPoints();
	// this.onRefreshAttributes();
	// }

	/**
	 * 刷新点集时。
	 * @returns {Array<Array<number>>}
	 * @private
	 */
	onRefreshPoints() {
		this.onBeforeSetBodyPoints(this._selfPoints);

		let selfPoints = this.onGetSelfPoints(this._selfPoints);
		this.bodyNode.setPoints(selfPoints);

		this.onAfterSetBodyPoints();

		return selfPoints;
	}

	/**
	 * 刷新属性时。
	 * @private
	 */
	onRefreshAttributes() {

	}

	// #endregion

	/**
	 * 清除所有点。
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.points.length == 0;
	 * // @expect(ret == true);
	 * point.addPoint([1,1,1]);
	 * ret = point.points.length == 1;
	 * // @expect(ret == true);
	 * point.clearPoints();
	 * ret = point.points.length == 0;
	 * // @expect(ret == true);
	 */
	clearPoints() {
		this._selfPoints = [];

		this.bodyNode.setPoints([]);
	}

	/**
	 * 在世界坐标系中按索引插入点。
	 * @param {number} index 索引。
	 * @param {Array<number>} position 世界坐标位置。
	 * @example
	 * let point = new THING.Points();
	 * point.addPoint([1,1,1]);
	 * point.insertPoint(0,[10,10,10]);
	 * ret = point.points[0][0] == 10;
	 * // @expect(ret == true);
	 */
	insertPoint(index, position) {
		let selfPoints = this._selfPoints;

		selfPoints._insert(index, this.worldToSelf(position));

		this.onRefreshPoints();
	}

	/**
	 * 在世界空间中添加一个点。
	 * @param {Array<number>} position 世界坐标位置。
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.points.length == 0;
	 * // @expect(ret == true);
	 * point.addPoint([1,1,1]);
	 * ret = point.points.length == 1;
	 * // @expect(ret == true);
	 */
	addPoint(position) {
		let selfPoints = this._selfPoints;

		selfPoints.push(this.worldToSelf(position));

		this.onRefreshPoints();
	}

	/**
	 * 在世界空间中添加多个点。
	 * @param {Array<Array<number>>} points 世界空间中的点。
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.points.length == 0;
	 * // @expect(ret == true);
	 * point.addPoints([[1,1,1],[2,2,2]]);
	 * ret = point.points.length == 2;
	 * // @expect(ret == true);
	 */
	addPoints(points) {
		let selfPoints = this._selfPoints;

		points.forEach(point => {
			selfPoints.push(this.worldToSelf(point));
		});

		this.onRefreshPoints();
	}

	/**
	 * 通过索引删除点。
	 * @param {number} index 索引。
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.points.length == 0;
	 * // @expect(ret == true);
	 * point.addPoints([[1,1,1],[2,2,2]]);
	 * ret = point.points[0][0] == 1;
	 * // @expect(ret == true);
	 * point.removePoint(0);
	 * ret = point.points[0][0] == 2;
	 * // @expect(ret == true);
	 */
	removePoint(index) {
		let selfPoints = this._selfPoints;

		selfPoints._removeAt(index);

		this.onRefreshPoints();
	}

	/**
	 * 在世界坐标系中设置指定索引的点。
	 * @param {number} index 索引。
	 * @param {Array<number>} position 世界坐标位置。
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.points.length == 0;
	 * // @expect(ret == true);
	 * point.addPoints([[1,1,1],[2,2,2]]);
	 * ret = point.points[0][0] == 1;
	 * // @expect(ret == true);
	 * point.setPoint(0,[10,10,10]);
	 * ret = point.points[0][0] == 10;
	 * // @expect(ret == true);
	 */
	setPoint(index, position) {
		let selfPoints = this._selfPoints;

		selfPoints[index] = this.worldToSelf(position);

		this.onRefreshPoints();
	}

	// #region 访问器

	/**
	 * 点的个数
	 * @type {number}
	 * @public
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.number == 0;
	 * // @expect(ret == true);
	 * point.addPoints([[1,1,1],[2,2,2]]);
	 * ret = point.number == 2;
	 * // @expect(ret == true);
	 */
	get number() {
		return this.points.length;
	}

	/**
	 * 是否自动调整点坐标。当坐标过大时,由于精度问题可能导致抖动,此时需要自动调整。
	 * @type {boolean}
	 * @public
	 * @example
	 * let point = new THING.Points();
	 * let ret = point.autoAdjustPosition == false;
	 * // @expect(ret == true);
	 * point.autoAdjustPosition = true;
	 * ret = point.autoAdjustPosition == true;
	 * // @expect(ret == true);
	 */
	get autoAdjustPosition() {
		return this._autoAdjustPosition;
	}
	set autoAdjustPosition(value) {
		this._autoAdjustPosition = value;
	}

	/**
	 * 获取/设置对象自身坐标系中的点坐标集合
	 * @type {Array<Array<number>>}
	 * @public
	 * @example
	 * let point = new THING.Points();
	 * point.selfPoints = [[1,1,1],[2,2,2]];
	 * ret = point.selfPoints[1][1] == 2;
	 * // @expect(ret == true);
	 */
	get selfPoints() {
		return this._selfPoints;
	}
	set selfPoints(value) {
		this._selfPoints = value ? value.slice(0) : [];

		this.onRefreshPoints();
	}

	/**
	 * 获取/设置世界坐标系中的点坐标集合
	 * @type {Array<Array<number>>}
	 * @public
	 * @example
	 * let point = new THING.Points();
	 * point.points = [[1,1,1],[2,2,2]];
	 * ret = point.points[1][1] == 2;
	 * // @expect(ret == true);
	 */
	get points() {
		return this._selfPoints.map(point => {
			return this.selfToWorld(point);
		});
	}
	set points(value) {
		if (value) {
			if (this._autoAdjustPosition) {
				this._adjustPosition(value);
			}

			this._selfPoints = value.map(point => {
				return this.worldToSelf(point);
			});

			this.onRefreshPoints();
		}
		else {
			this.clearPoints();
		}
	}

	/**
	 * 获取/设置局部坐标系中的点坐标集合
	 * @type {Array<Array<number>>}
	 * @public
	 * @example
	 * let point = new THING.Points();
	 * point.localPoints = [[1,1,1],[2,2,2]];
	 * ret = point.localPoints[1][1] == 2;
	 * // @expect(ret == true);
	 */
	get localPoints() {
		return this._selfPoints.map(point => {
			return this.selfToLocal(point);
		});
	}
	set localPoints(value) {
		if (value) {
			this._selfPoints = value.map(point => {
				return this.localToSelf(point);
			});

			this.onRefreshPoints();
		}
		else {
			this.clearPoints();
		}
	}

	/**
	 * 获取由点形成的路径的长度
	 * @type {number}
	 * @public
	 * @example
	 * let point = new THING.Points();
	 * point.localPoints = [[0,0,0],[3,0,0],[0,4,0]];
	 * ret = point.length == 8;
	 * // @expect(ret == true);
	 */
	get length() {
		return MathUtils.getDistanceFromPoints(this.points);
	}

	// #endregion

}

export { BasePoints }