import { MathUtils } from '../math/MathUtils';

import { Object3D } from './Object3D';

import { Utils } from '../common/Utils';

import { SpaceRelationComponent } from '../components/SpaceRelationComponent';

import { SpaceHelperComponent } from '../components/SpaceHelperComponent';



const __ = {

	private: Symbol('private'),

}



const relation = {

	contains: 'contains',

	intersects: 'intersects',

	disjoint: 'disjoint'

}



const registerComponentParam = { isResident: true };



/**
 * CellSpace - 空间
 *
 * 提供处理对象的空间关系能力,可进行包含、相交和相离等空间计算。
 * @class CellSpace
 * @memberof THING
 * @extends THING.Object3D
 * @public
 * @example
 const space = new THING.CellSpace({
   size: [5, 3, 5],
   position: [0, 1.5, 0],
 });
 */
class CellSpace extends Object3D {



	static defaultTagArray = ['CellSpace'];



	/**

	 提供处理对象的空间关系的能力。空间是具有一定体积的三维对象,可进行空间计算。

	 </br>空间可以包含子空间和实体,实体也可以包含空间。空间关系可以分为包含、相交和相离。

	 </br>空间计算时是以物体自身包围盒进行计算
	 * @param {object} param The initial parameters. 除Object3D构造方法中的参数之外,还可以传size
	 * @param {number[]} param.size 三个方向的尺寸
	 * @constructor
	 * @public
	 */

	constructor(param = {}) {
		super(param);



		this[__.private] = {};

		let _private = this[__.private];



		_private.size = param['size'];



		// init local bounding box

		if (_private.size) {
			this.initialLocalBoundingBox = {

				center: [0, 0, 0],

				halfSize: MathUtils.divideVector(_private.size, 2)

			};
		}
	}



	clearInitialLocalBoundingBox() { }



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



		this.addComponent(SpaceRelationComponent, 'spaceCalculation', registerComponentParam);

		this.addComponent(SpaceHelperComponent, 'spaceHelper', registerComponentParam);
	}



	/**
	 
	 判读包含关系
	 * @param {object} obj 当前物体是否包含该参数传入的物体
	 * @param {boolean} recursive 空间计算是否包含obj的子对象
	 * @returns {boolean}
	 * @example
	 
	 * 	let result = this.contains(obj, false);
	 
	 *  console.log(result);
	 * @public
	 */

	contains(obj, recursive = true) {
		return this._calcRelationByCondition(obj, recursive, relation.contains);
	}



	/**
	 
	 判读相交关系
	 * @param {object} obj 当前物体是否与该参数传入的物体相交
	 * @param {boolean} recursive 空间计算是否包含obj的子对象
	 * @returns {boolean}
	 * @example
	 
	 * 	let result = this.intersects(obj, false);
	 
	 *  console.log(result);
	 * @public
	 */

	intersects(obj, recursive = true) {
		return this._calcRelationByCondition(obj, recursive, relation.intersects);
	}



	/**
	 
	 判读相离关系 相离代表两个物体之间没有交集
	 * @param {object} obj 当前物体是否与该参数传入的物体不相交
	 * @param {boolean} recursive 空间计算是否包含obj的子对象
	 * @returns {boolean}
	 * @example
	 
	 * 	let result = this.disjoint(obj, false);
	 
	 *  console.log(result);
	 * @public
	 */

	disjoint(obj, recursive = true) {
		return this._calcRelationByCondition(obj, recursive, relation.disjoint);
	}



	/**
	 
	 显示包围盒
	 * @param {boolean} value 是否显示
	 * @example
	 
	 * 	space.showBounding();
	 * @public
	 */

	showBounding(value = true) {
		let _private = this[__.private];



		if (!_private.size && !this.children.length) {
			Utils.warn('CellSpace no size!');

			return;
		}



		this.traverse((object) => {
			if (object.isCellSpace && object.size) {
				object.spaceHelper.showBounding(value);
			}
		});
	}



	// calc relation by condition

	_calcRelationByCondition(obj, cascade, condition) {
		let spaces = [];

		this.traverse((object) => {
			if (object.isCellSpace && object.size) {
				spaces.push(object);
			}
		});



		switch (condition) {
			case relation.contains:

			case relation.intersects:

				return _calcRelation(true);

			case relation.disjoint:

				return _calcRelation(false);
		}



		function _calcRelation(bool) {
			for (let i = 0; i < spaces.length; i++) {
				const space = spaces[i];



				if (space.spaceCalculation[condition](obj, cascade) === bool) {
					return bool;
				}
			}



			return !bool;
		}
	}



	/**
	 
	 获取对象三个方向的尺寸
	 * @type {Array<number>}
	 */

	get size() {
		let _private = this[__.private];



		return _private.size;
	}



	/**
	 
	 Check whether it's CellSpace type or inherit from it.
	 * @type {boolean}
	 */

	get isCellSpace() {
		return true;
	}



	get isSpace3D() {
		console.warn('Plase use isCellSpace instead of isSpace3D');

		return true;
	}



}



export { CellSpace }