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

let _vec3 = MathUtils.createVec3();

/**
 * @class Box3
 * 表示具有[min, max]的盒子。
 * @memberof THING
 * @public
 */
class Box3 {
	/**
	 * 盒子类构造函数。
	 * @param {object} param 构造参数。
	 * @param {Array} param.center 盒子中心坐标。
	 * @param {Array} param.halfSize 盒子半尺寸。
	 */
	constructor(center, halfSize) {
		this._min = [+Infinity, +Infinity, +Infinity];
		this._max = [-Infinity, -Infinity, -Infinity];

		this.set(center, halfSize);
	}

	// #region Private

	_getPoints(min, max, result) {
		result = result || [];
		// TOP
		result[0] = [min[0], min[1], min[2]];
		result[1] = [max[0], min[1], min[2]];
		result[2] = [max[0], min[1], max[2]];
		result[3] = [min[0], min[1], max[2]];
		// BOTTOM
		result[4] = [min[0], max[1], min[2]];
		result[5] = [max[0], max[1], min[2]];
		result[6] = [max[0], max[1], max[2]];
		result[7] = [min[0], max[1], max[2]];
		return result;
	}

	// Get the layout position from box
	_getLayoutPosition(types, min, max) {
		if (Utils.getArrayLength(types) !== 3) {
			return null;
		}

		let points = this._getPoints(min, max);
		let lines = [
			[points[0], points[1]], // X
			[points[0], points[4]], // Y
			[points[0], points[2]], // Z
		];

		let progress = [
			Utils.getProgressByLayout(types[0]),
			Utils.getProgressByLayout(types[1]),
			Utils.getProgressByLayout(types[2]),
		];

		let x = MathUtils.lerpVector(lines[0][0], lines[0][1], progress[0])[0];
		let y = MathUtils.lerpVector(lines[1][0], lines[1][1], progress[1])[1];
		let z = MathUtils.lerpVector(lines[2][0], lines[2][1], progress[2])[2];

		return [x, y, z];
	}

	// #endregion

	isEmpty() {
		return (this._max[0] < this._min[0]) || (this._max[1] < this._min[1]) || (this._max[2] < this._min[2]);
	}

	makeEmpty() {
		this._min = [+Infinity, +Infinity, +Infinity];
		this._max = [-Infinity, -Infinity, -Infinity];

		return this;
	}

	set(center, halfSize) {
		if (center && halfSize) {
			MathUtils.vec3.sub(this._min, center, halfSize);
			MathUtils.vec3.add(this._max, center, halfSize);
		}

		return this;
	}

	expandByPoint(point) {
		MathUtils.vec3.min(this._min, this._min, point);
		MathUtils.vec3.max(this._max, this._max, point);

		return this;
	}

	getBoundingSphere(boundingSphere = new BoundingSphere()) {
		boundingSphere.set(this.center, this.radius);
		return boundingSphere;
	}

	getPoints() {
		return this._getPoints(this.min, this.max);
	}

	getLayoutPosition(types) {
		return this._getLayoutPosition(types, this.min, this.max);
	}

	/**
	 * 获取最小位置。
	 * @type {Array<number>}
	 * @example
	 * // 打印盒子的最小位置。
	 * console.log(box.min);
	 * @public
	 */
	get min() {
		return this._min.slice(0);
	}

	/**
	 * 获取最大位置。
	 * @type {Array<number>}
	 * @example
	 * // 打印盒子的最大位置。
	 * console.log(box.max);
	 * @public
	 */
	get max() {
		return this._max.slice(0);
	}

	/**
	 * 获取中心位置。
	 * @type {Array<number>}
	 * @example
	 * 	let box = new THING.Box3([10, 20, 40], [2, 3, 4]);
	 * 	let center = box.boundingBox.center;
	 * 	// @expect(center[0] == 10);
	 * 	// @expect(center[1] == 20);
	 * 	// @expect(center[2] == 40);
	 * @public
	 */
	get center() {
		return this.isEmpty() ? [0, 0, 0] : MathUtils.vec3.scale([], MathUtils.vec3.add(_vec3, this._min, this._max), 0.5);
	}

	/**
	 * 获取尺寸的一半
	 * @type {Array<number>}
	 * @example
	 * 	let box = new THING.Box3([10, 20, 40], [2, 3, 4]);
	 * 	let halfSize = box.halfSize;
	 * 	// @expect(halfSize[0] == 2);
	 * 	// @expect(halfSize[1] == 3);
	 * 	// @expect(halfSize[2] == 4);
	 * @public
	 */
	get halfSize() {
		let size = this.size;

		return MathUtils.vec3.scale(size, size, 0.5);
	}

	/**
	 * 获取尺寸
	 * @type {Array<number>}
	 * @example
	 * 	let box = new THING.Box3([10, 20, 40], [2, 3, 4]);
	 * 	let size = box.size;
	 * 	// @expect(size[0] == 4);
	 * 	// @expect(size[1] == 6);
	 * 	// @expect(size[2] == 8);
	 * @public
	 */
	get size() {
		return this.isEmpty() ? [0, 0, 0] : MathUtils.vec3.sub([], this._max, this._min);
	}

	/**
	 * 获取半径
	 * @type {number}
	 * @example
	 * // 创建一个盒子并获取其半径
	 * let box = new THING.Box3([10, 20, 30], [5, 5, 5]);
	 * let radius = box.radius;
	 * // 半径应该是盒子对角线长度的一半
	 * console.log(radius); // 约为 8.66
	 * @public
	 */
	get radius() {
		let size = this.size;

		return Math.sqrt((Math.pow(size[0], 2) + Math.pow(size[1], 2) + Math.pow(size[2], 2))) / 2;
	}

	get isBox3() {
		return true;
	}

}

export { Box3 }