import { Utils } from '../common/Utils'
import { MathUtils } from '../math/MathUtils';
import { PlanePointsHelperComponent } from '../components/PlanePointsHelperComponent';
import { BaseDynamicPoints } from './BaseDynamicPoints';

const registerComponentParam = { isResident: true };

/**
 * @class BasePlanePoints
 * The base plane points object.
 * @memberof THING
 * @extends THING.BaseDynamicPoints
 * @public
 */
class BasePlanePoints extends BaseDynamicPoints {

	/**
	 * 基础平面点对象类,负责管理平面上的点数据和区域属性
	 * @param {object} param 初始化参数
	 */
	constructor(param = {}) {
		super(param);
	}

	// #region Private Functions

	/**
	 * Refresh holes.
	 * @private
	 */
	_refreshHoles() {
		let selfPlaneHoles = this.selfPlaneHoles;

		this.bodyNode.setHoles(selfPlaneHoles);

		if (this.hasComponent('helper')) {
			this.helper.refresh();
		}
	}

	/**
	 * Get the outline points in self space in plane.
	 * @returns {Array<Array<number>>}
	 * @private
	 */
	_getOutlinePoints() {
		let planePoints = this.onGetSelfPoints(this.selfPoints);

		return MathUtils.getOutlinePoints(planePoints);
	}

	// #endregion

	// #region Overrides

	onSetupResource(param) {
		// 读取点数据,写到参数里传给父类处理
		const selfPlanePoints = param['selfPlanePoints'];
		if (selfPlanePoints) {
			param['selfPoints'] = convertPlanePoints(selfPlanePoints);
		}

		// 读取洞数据
		const selfPlaneHoles = param['selfPlaneHoles'];
		if (selfPlaneHoles) {
			this._selfHoles = convertPlaneHolesPoints(selfPlaneHoles);
		}
		else if (param['selfHoles']) {
			this._selfHoles = param['selfHoles'];
		}
		else if (param['holes']) {
			this._selfHoles = param['holes'].map(hole => {
				return hole.map(_hole => {
					return this.worldToSelf(_hole);
				});
			});
		}
		else {
			this._selfHoles = [];
		}

		super.onSetupResource(param);
	}

	onSetupBodyNode(node) {
		super.onSetupBodyNode(node);
		const holes = this.selfPlaneHoles;
		if (holes) {
			node.setHoles(holes);
		}
	}

	onExportExternalData() {
		let external = Object.assign({}, super.onExportExternalData());
		Utils.setAttributeIfExist(external, 'selfPlanePoints', this);
		Utils.setAttributeIfExist(external, 'selfPlaneHoles', this);
		Utils.setAttributeIfExist(external, 'selfHoles', this);
		return external;
	}

	onSetupComponent(param) {
		super.onSetupComponent(param);

		this.addComponent(PlanePointsHelperComponent, 'helper', registerComponentParam);
	}

	onSetupTransform(param) {
		// Auto calculate the position when it do not provide any positions
		let position = param['position'] || param['localPosition'];
		if (!position) {
			let points = param['points'];
			if (points && points.length) {
				// Get the height
				let height = points.reduce((value, point) => {
					return value + point[1];
				}, 0) / points.length;

				// Make label position as default position
				param['position'] = MathUtils.getLabelPosition(points, param['holes'], height);
			}
		}

		super.onSetupTransform(param);
	}

	onGetSelfPoints(selfPoints) {
		return selfPoints.map(point => {
			return [point[0], point[2]];
		});
	}

	onBeforeSetBodyPoints(selfPoints) {
	}

	onRefresh() {
		this._refreshHoles();

		super.onRefresh();
	}

	// #endregion

	// #region Accessor

	/**
	 * 设置/获取 自身平面坐标系下(2D)区域空洞坐标
	 * @type {Array<Array<Array<number>>>}
	 * @public
	 */
	get selfPlaneHoles() {
		return this._selfHoles.map(hole => {
			return hole.map(_hole => {
				return [_hole[0], _hole[2]];
			});
		});
	}
	set selfPlaneHoles(value) {
		this._selfHoles = value.map(hole => {
			return hole.map(_hole => {
				return [_hole[0], 0, _hole[1]];
			});
		});

		this._refreshHoles();
	}

	/**
	 * 设置/获取 自身坐标系下(3D)区域空洞坐标
	 * @type {Array<Array<Array<number>>>}
	 * @public
	 */
	get selfHoles() {
		return this._selfHoles;
	}
	set selfHoles(value = []) {
		this._selfHoles = value.slice(0);

		this._refreshHoles();
	}

	/**
	 * 设置/获取 世界坐标系下(3D)区域空洞坐标
	 * @type {Array<Array<Array<number>>>}
	 * @public
	 */
	get holes() {
		return this._selfHoles.map(hole => {
			return hole.map(_hole => {
				return this.selfToWorld(_hole);
			});
		});
	}
	set holes(value = []) {
		this._selfHoles = value.map(hole => {
			return hole.map(_hole => {
				return this.worldToSelf(_hole);
			});
		});

		this._refreshHoles();
	}

	/**
	 * 设置/获取 自身平面坐标系下(2D)区域外轮廓坐标
	 * @type {Array<Array<Array<number>>>}
	 * @public
	 */
	get selfPlanePoints() {
		return this.points.map(point => {
			let selfPoint = this.worldToSelf(point);

			return [selfPoint[0], selfPoint[2]];
		});
	}
	set selfPlanePoints(value = []) {
		if (!MathUtils.isClockWise(value)) {
			const reversedArr = value.slice().reverse();
			this.points = (reversedArr).map(point => {
				return this.selfToWorld([point[0], 0, point[1]]);
			});
		}
		else {
			this.points = (value).map(point => {
				return this.selfToWorld([point[0], 0, point[1]]);
			});
		}
	}

	/**
	 * 获取区域面积(计算中会减去洞的面积)
	 * @type {number}
	 * @public
	 */
	get area() {
		let outlinePoints = this._getOutlinePoints();

		let area = Math.abs(MathUtils.getArea(outlinePoints));

		// Sub holes area
		this.selfPlaneHoles.forEach(holes => {
			let holeArea = MathUtils.getArea(holes);
			area -= holeArea;
		});

		return area;
	}

	/**
	 * 获取区域周长
	 * @type {number}
	 * @public
	 */
	get perimeter() {
		let outlinePoints = this.outlinePoints;
		return MathUtils.getDistanceFromPoints(outlinePoints, true);
	}

	/**
	 * 获取世界坐标系下区域的标注位置(一般是在区域中心)
	 * @type {Array<number>}
	 * @public
	 */
	get labelPosition() {
		let outlinePoints = this._getOutlinePoints();

		let labelPos = MathUtils.getLabelPosition(outlinePoints);
		return this.selfToWorld(labelPos);
	}

	/**
	 * 获取自身平面空间(2D)下区域外轮廓点位
	 * @type {Array<Array<number>>}
	 * @public
	 */
	get outlinePoints() {
		let planePoints = this._getOutlinePoints();

		return planePoints.map(point => {
			return this.selfToWorld([point[0], 0, point[1]]);
		});
	}

	// #endregion

}

export { BasePlanePoints }

function convertPlanePoints(value) {
	if (!MathUtils.isClockWise(value)) {
		const reversedArr = value.slice().reverse();
		return (reversedArr).map(point => {
			return [point[0], 0, point[1]];
		});
	}
	else {
		return (value).map(point => {
			return [point[0], 0, point[1]];
		});
	}
}

function convertPlaneHolesPoints(value) {
	return value.map(hole => {
		return hole.map(_hole => {
			return [_hole[0], 0, _hole[1]];
		});
	});
}