import { Object3D, BaseComponent, MathUtils, Utils } from '@uino/thing';

/**
 * @class DragControlComponent
 * @summary 对象拖拽控制组件
 * @memberof THING
 * @extends THING.BaseComponent
 * @public
 */
class DragControlComponent extends BaseComponent {

	/**
	 * 拖拽控制组件
	 * @param {object} param
	 * @param {Array.<Number>} param.position - 工作平面的位置坐标
	 * @param {Array.<Number>} param.quaternion - 工作平面的四元数
	 * @param {number} param.precision - 工作平面的缩放值
	 */
	constructor(param) {
		super(param);

		this._workPlane = { position: [0, 0, 0], quaternion: [0, 0, 0, 1], precision: 0 };
		this._orientedBox = {};
		if (param) {
			if (param.workPlane) {
				this._workPlane = param.workPlane;
			}
		}
		this._mouseOffsetV3 = null;
		this._mousePos = null;
		this._objQuaterStart = null;
		this._anchorPosV3 = null;
		this._oldForward = null;
		this._oldScale = null;
		this._oldPosition = null;
		this._edgeXPosV3 = null;
		this._edgeZPosV3 = null;
		this._angleBtnDir = null;
		this._mode = null;
		this._btnOffset = [];
		this._downCenter = null;
		this._cancelShiftScale = false;
		this._oldSize = null;

		this._objMap = {};
		this._emptyObjects = [];
		this._snapRotationStep = 45;
	}

	onRemove() {
		this._orientedBox = null;
		super.onRemove();
	}

	onUpdate() {
		let result = {};
		if (this._emptyObjects.length) {
			this._calculation_orientedBox();
			result = this._calc();
		}
		this.object.trigger('TRANSFORM_CONTROL_UPDATE', { data: result });
	}

	_initOrientedBoxData(object) {
		const orientedBox = object.orientedBox;
		this._orientedBox = {
			'center': orientedBox.center,
			'size': orientedBox.size
		};
		this.__oldSize = MathUtils.divideVector(this._orientedBox.size, object.scale);
		this.__oldOffsetPos = MathUtils.divideVector(
			MathUtils.vec3ApplyQuat(
				MathUtils.subVector(this._orientedBox.center, object.position), [
				-1 * object.quaternion[0],
				-1 * object.quaternion[1],
				-1 * object.quaternion[2],
				object.quaternion[3],
			]), object.scale);
		const pY = MathUtils.round(object.position[1] * 1000) / 1000;
		const cY = MathUtils.round(this._orientedBox.center[1] * 1000) / 1000;
		this._centerPos = pY === cY ? 1 : 0;
	}

	_calc() {
		let result = [];
		const camera = this.object.app.camera;
		const obbSizeX = MathUtils.abs(this.__oldSize[0] / 2);
		const obbSizeY = MathUtils.abs(this.__oldSize[1] / 2);
		const obbSizeZ = MathUtils.abs(this.__oldSize[2] / 2);

		let lu = this._selfToWorld([-obbSizeX, -obbSizeY, -obbSizeZ]);
		let ru = this._selfToWorld([obbSizeX, -obbSizeY, -obbSizeZ]);
		let rd = this._selfToWorld([obbSizeX, -obbSizeY, obbSizeZ]);
		let ld = this._selfToWorld([-obbSizeX, -obbSizeY, obbSizeZ]);
		let front = this._selfToWorld([0, -obbSizeY, obbSizeZ]);
		let after = this._selfToWorld([0, -obbSizeY, -obbSizeZ]);
		let left = this._selfToWorld([-obbSizeX, -obbSizeY, 0]);
		let right = this._selfToWorld([obbSizeX, -obbSizeY, 0]);
		const top = this._selfToWorld([0, obbSizeY, 0]);
		const objPosition = this.object.position;
		const objCenterPos = camera.worldToScreen([objPosition[0], this._orientedBox.center[1], objPosition[2]]);
		let rotateYPos;
		if (!this._angleBtnDir) {
			rotateYPos = [objCenterPos[0], objCenterPos[1]];
		}
		else {
			const _objPosV2 = [objCenterPos[0] - this._btnOffset[0], objCenterPos[1] - this._btnOffset[1], 0];
			const temp = [
				this._angleBtnDir[0] * 30,
				this._angleBtnDir[1] * 30,
				this._angleBtnDir[2] * 30
			];
			rotateYPos = MathUtils.addVector(_objPosV2, temp);
		}

		result.push({ mode: 'scale', axis: 'z', anchor: [0, -1], pos: camera.worldToScreen(front), visible: true });
		result.push({ mode: 'scale', axis: 'z', anchor: [0, 1], pos: camera.worldToScreen(after), visible: true });
		result.push({ mode: 'scale', axis: 'x', anchor: [1, 0], pos: camera.worldToScreen(left), visible: true });
		result.push({ mode: 'scale', axis: 'x', anchor: [-1, 0], pos: camera.worldToScreen(right), visible: true });

		result.push({ mode: 'scale', axis: 'xz', anchor: [1, 1], pos: camera.worldToScreen(lu), visible: true });
		result.push({ mode: 'scale', axis: 'xz', anchor: [-1, 1], pos: camera.worldToScreen(ru), visible: true });
		result.push({ mode: 'scale', axis: 'xz', anchor: [-1, -1], pos: camera.worldToScreen(rd), visible: true });
		result.push({ mode: 'scale', axis: 'xz', anchor: [1, -1], pos: camera.worldToScreen(ld), visible: true });
		result.push({ mode: 'scale', axis: 'y', anchor: [0, 1], pos: camera.worldToScreen(top), visible: true });
		result.push({ mode: 'translate', axis: 'xz', pos: objCenterPos, visible: true });
		result.push({ mode: 'translate', axis: 'y', pos: objCenterPos, visible: true });
		result.push({ mode: 'angle', axis: 'y', anchor: [0, 0], pos: rotateYPos, visible: true });

		return result;
	}

	_calculation_orientedBox() {
		const curScale = this.object.scale;
		const sizeX = MathUtils.abs(this.__oldSize[0] * curScale[0]);
		const sizeY = MathUtils.abs(this.__oldSize[1] * curScale[1]);
		const sizeZ = MathUtils.abs(this.__oldSize[2] * curScale[2]);
		const afterAngles = MathUtils.vec3ApplyQuat([
			this.__oldOffsetPos[0] * curScale[0],
			this.__oldOffsetPos[1] * curScale[1],
			this.__oldOffsetPos[2] * curScale[2]
		], this.object.quaternion);

		this._orientedBox.size = [sizeX, sizeY, sizeZ];
		this._orientedBox.center = [
			this.object.position[0] + afterAngles[0],
			this.object.position[1] + afterAngles[1],
			this.object.position[2] + afterAngles[2]
		];
	}

	_moveY(x, y) {
		const normal = this.object.app.camera.transform.getForward();
		const constant = -MathUtils.dotVector(normal, this._orientedBox.center);
		const position = this.object.app.camera.intersectPlane(x, y, normal, constant);

		if (!this._mouseOffsetV3) {
			this._oldPosition = this.object.position;
			this._mouseOffsetV3 = MathUtils.subVector(position, this._oldPosition);
		}
		else {
			this.object.position = [
				this._oldPosition[0],
				position[1] - this._mouseOffsetV3[1],
				this._oldPosition[2]
			];
		}
	}

	_moveXZ(x, y) {
		const normal = MathUtils.vec3ApplyQuat([0, 1, 0], this._workPlane.quaternion);
		const constant = -MathUtils.dotVector(normal, this._orientedBox.center);
		const position = this.object.app.camera.intersectPlane(x, y, normal, constant);

		if (!position) { return; }
		if (!this._mouseOffsetV3) {
			this._oldPosition = this.object.position;
			this._mouseOffsetV3 = [position[0] - this._oldPosition[0], position[1] - this._oldPosition[1], position[2] - this._oldPosition[2]];
		}
		else {
			this.object.position = [
				position[0] - this._mouseOffsetV3[0],
				this._oldPosition[1],
				position[2] - this._mouseOffsetV3[2]
			];
		}
	}

	_angleY(x, y, step) {
		const objCenterPos = this.object.app.camera.worldToScreen(this._orientedBox.center);

		const _to = MathUtils.normalizeVector(MathUtils.subVector([x, y, 0], [objCenterPos[0], objCenterPos[1], 0]));

		if (!this._mousePos) {
			this._mousePos = _to.slice();
			this._objQuaterStart = this.object.quaternion;
			this._oldAxis = MathUtils.vec3ApplyQuat([0, 1, 0], this.object.quaternion);
		}
		const angleY = this._angleTwoVectorsY(this._mousePos, _to);

		if (step) {
			const _objCurrentQuaterEng = MathUtils.getQuatFromAxisAngle(this._oldAxis, angleY - (angleY % step));
			this.object.quaternion = MathUtils.multiplyQuat(_objCurrentQuaterEng, this._objQuaterStart);
		}
		else {
			this.object.quaternion = MathUtils.getQuatFromAxisAngle(this._oldAxis, angleY - (angleY % this._snapRotationStep));
		}
		this._angleBtnDir = _to;
	}

	_scaleY(x, y) {
		const normal = this.object.app.camera.transform.getForward();
		const constant = -MathUtils.dotVector(normal, this._selfToWorld([0, -this.__oldSize[1] / 2, 0]));
		const position = this.object.app.camera.intersectPlane(x, y, normal, constant);

		if (!this._mouseOffsetV3) {
			this._mouseOffsetV3 = position.slice();
			if (!this._oldScale) { this._oldScale = this.object.scale; }
			if (!this._oldSize) { this._oldSize = this._orientedBox.size; }
		}
		else {
			let scaleOffset = MathUtils.divideVector(MathUtils.subVector(position, this._mouseOffsetV3), this.__oldSize);
			let axisX = MathUtils.vec3ApplyQuat([1, 0, 0], this.object.quaternion);
			let newX = MathUtils.dotVector(scaleOffset, axisX);
			let axisY = MathUtils.vec3ApplyQuat([0, 1, 0], this.object.quaternion);
			let newY = MathUtils.dotVector(scaleOffset, axisY);
			let axisZ = MathUtils.vec3ApplyQuat([0, 0, 1], this.object.quaternion);
			let newZ = MathUtils.dotVector(scaleOffset, axisZ);

			this.object.scale = [
				this._oldScale[0],
				this._oldScale[1] + axisX[1] * newX + axisY[1] * newY + axisZ[1] * newZ,
				this._oldScale[2]
			];
		}
	}

	_scaleY_equalProportion(x, y) {
		if (this._cancelShiftScale) { return; }
		const normal = this.object.app.camera.transform.getForward();
		const constant = -MathUtils.dotVector(normal, this._selfToWorld([0, -this.__oldSize[1] / 2, 0]));
		const position = this.object.app.camera.intersectPlane(x, y, normal, constant);

		if (!this._mouseOffsetV3) {
			this._mouseOffsetV3 = position.slice();
			if (!this._oldScale) { this._oldScale = this.object.scale; }
			if (!this._oldSize) { this._oldSize = this._orientedBox.size; }
		}
		else {
			const oldScaleY = this._oldScale[1] > 0 ? 1 : -1;

			const dir = MathUtils.normalizeVector(MathUtils.subVector(
				[0, position[1], 0],
				[0, this._mouseOffsetV3[1], 0]
			));
			const oriDir = MathUtils.normalizeVector(MathUtils.subVector(this._mouseOffsetV3, this.object.position));
			const result = MathUtils.dotVector(dir, oriDir) > 0 ? 1 : -1;

			const distance = MathUtils.getDistance([0, position[1], 0], [0, this._mouseOffsetV3[1], 0]);

			const scaleY = distance / this._oldSize[1] * oldScaleY * result + 1;
			this.object.scale = [
				this._oldScale[0] * scaleY,
				this._oldScale[1] * scaleY,
				this._oldScale[2] * scaleY
			];
		}
	}

	_scaleXZ(anchor, x, y) {
		const beforeProportion = this._beforeScaleXZ_equalProportion(anchor, x, y);
		if (beforeProportion) {
			const [newpos, scalex, scalez] = beforeProportion.slice();
			if (anchor[0] !== 0 && anchor[1] === 0) {
				this.object.scale = [scalex, this._oldScale[1], this._oldScale[2]];

				const afterAngle = MathUtils.vec3ApplyQuat([
					this.__oldOffsetPos[0] * this.object.scale[0],
					this.__oldOffsetPos[1] * this.object.scale[1],
					this.__oldOffsetPos[2] * this.object.scale[2]
				], this.object.quaternion);
				let nowVec3x = MathUtils.vec3ApplyQuat([1, 0, 0], this.object.quaternion);
				let newX = MathUtils.dotVector(newpos, nowVec3x);
				this.object.position = [
					this._downCenter[0] - afterAngle[0] + newX * nowVec3x[0],
					this._downCenter[1] - afterAngle[1] + newX * nowVec3x[1],
					this._downCenter[2] - afterAngle[2] + newX * nowVec3x[2]
				];
			}
			else if (anchor[0] === 0 && anchor[1] !== 0) {
				this.object.scale = [this._oldScale[0], this._oldScale[1], scalez];

				const afterAngle = MathUtils.vec3ApplyQuat([
					this.__oldOffsetPos[0] * this.object.scale[0],
					this.__oldOffsetPos[1] * this.object.scale[1],
					this.__oldOffsetPos[2] * this.object.scale[2]
				], this.object.quaternion);
				let nowVec3z = MathUtils.vec3ApplyQuat([0, 0, 1], this.object.quaternion);
				let newZ = MathUtils.dotVector(newpos, nowVec3z);
				this.object.position = [
					this._downCenter[0] - afterAngle[0] + newZ * nowVec3z[0],
					this._downCenter[1] - afterAngle[1] + newZ * nowVec3z[1],
					this._downCenter[2] - afterAngle[2] + newZ * nowVec3z[2]
				];
			}
			else {
				this.object.scale = [scalex, this._oldScale[1], scalez];

				const afterAngle = MathUtils.vec3ApplyQuat([
					this.__oldOffsetPos[0] * this.object.scale[0],
					this.__oldOffsetPos[1] * this.object.scale[1],
					this.__oldOffsetPos[2] * this.object.scale[2]
				], this.object.quaternion);
				let nowVec3x = MathUtils.vec3ApplyQuat([1, 0, 0], this.object.quaternion);
				let newX = MathUtils.dotVector(newpos, nowVec3x);
				let nowVec3z = MathUtils.vec3ApplyQuat([0, 0, 1], this.object.quaternion);
				let newZ = MathUtils.dotVector(newpos, nowVec3z);

				this.object.position = [
					this._downCenter[0] - afterAngle[0] + newX * nowVec3x[0] + newZ * nowVec3z[0],
					this._downCenter[1] - afterAngle[1] + newX * nowVec3x[1] + newZ * nowVec3z[1],
					this._downCenter[2] - afterAngle[2] + newX * nowVec3x[2] + newZ * nowVec3z[2]
				];
			}
		}
	}

	_scaleXZ_equalProportion(anchor, x, y) {
		const beforeProportion = this._beforeScaleXZ_equalProportion(anchor, x, y);
		if (beforeProportion) {
			const [newpos, scalex, scalez] = beforeProportion.slice();
			if (anchor[0] !== 0 && anchor[1] === 0) {
				const addScaleX = scalex * this.__oldSize[0] / this._oldSize[0];
				this.object.scale = [
					scalex,
					this._oldScale[1] * addScaleX,
					this._oldScale[2] * addScaleX
				];
				const afterAngle = MathUtils.vec3ApplyQuat([
					this.__oldOffsetPos[0] * this.object.scale[0],
					this.__oldOffsetPos[1] * this._oldScale[1],
					this.__oldOffsetPos[2] * this.object.scale[2]
				], this.object.quaternion);
				let nowVec3x = MathUtils.vec3ApplyQuat([1, 0, 0], this.object.quaternion);
				let newX = MathUtils.dotVector(newpos, nowVec3x);
				this.object.position = [
					this._downCenter[0] - afterAngle[0] + newX * nowVec3x[0],
					this._downCenter[1] - afterAngle[1] + newX * nowVec3x[1],
					this._downCenter[2] - afterAngle[2] + newX * nowVec3x[2]
				];
			}
			else if (anchor[0] === 0 && anchor[1] !== 0) {
				const addScaleZ = scalez * this.__oldSize[2] / this._oldSize[2];
				this.object.scale = [
					this._oldScale[0] * addScaleZ,
					this._oldScale[1] * addScaleZ,
					scalez
				];
				const afterAngle = MathUtils.vec3ApplyQuat([
					this.__oldOffsetPos[0] * this.object.scale[0],
					this.__oldOffsetPos[1] * this._oldScale[1],
					this.__oldOffsetPos[2] * this.object.scale[2]
				], this.object.quaternion);
				let nowVec3z = MathUtils.vec3ApplyQuat([0, 0, 1], this.object.quaternion);
				let newZ = MathUtils.dotVector(newpos, nowVec3z);
				this.object.position = [
					this._downCenter[0] - afterAngle[0] + newZ * nowVec3z[0],
					this._downCenter[1] - afterAngle[1] + newZ * nowVec3z[1],
					this._downCenter[2] - afterAngle[2] + newZ * nowVec3z[2]
				];
			}
		}
	}

	_beforeScaleXZ_equalProportion(anchor, x, y) {
		const obbSizeX = this.__oldSize[0] / 2;
		const obbSizeY = this.__oldSize[1] / 2;
		const obbSizeZ = this.__oldSize[2] / 2;
		if (!this._anchorPosV3) {
			this._anchorPosV3 = this._selfToWorld([obbSizeX * anchor[0], -obbSizeY, obbSizeZ * anchor[1]]);
			this._edgeXPosV3 = this._selfToWorld([obbSizeX * -anchor[0], -obbSizeY, obbSizeZ * anchor[1]]);
			this._edgeZPosV3 = this._selfToWorld([obbSizeX * anchor[0], -obbSizeY, obbSizeZ * -anchor[1]]);
			this._oldForward = this.object.forward;
			if (!this._oldScale) { this._oldScale = this.object.scale; }
			if (!this._oldPosition) { this._oldPosition = this.object.positio; }
			if (!this._downCenter) { this._downCenter = this._orientedBox.center; }
			if (!this._oldSize) { this._oldSize = this._orientedBox.size; }
		}
		else {
			const normal = MathUtils.vec3ApplyQuat(MathUtils.vec3ApplyQuat([0, 1, 0], this._workPlane.quaternion), this.object.quaternion);
			const constant = -MathUtils.dotVector(normal, this._selfToWorld([0, -this.__oldSize[1] / 2, 0]));
			const position = this.object.app.camera.intersectPlane(x, y, normal, constant);

			const footpointX = this._footPoint(this._anchorPosV3, this._edgeXPosV3, position);
			const footpointZ = this._footPoint(this._anchorPosV3, this._edgeZPosV3, position);

			const v1 = this._anchorPosV3.slice();
			const v2_x = footpointX.slice();
			const xdistance = MathUtils.sqrt(MathUtils.getDistanceToSquared(v1, v2_x));
			const v2_z = footpointZ.slice();
			const zdistance = MathUtils.sqrt(MathUtils.getDistanceToSquared(v1, v2_z));
			const oldScaleDir = this._oldScale[2] > 0 ? 1 : -1;

			const _v2_xSub_v1 = [v2_x[0] - v1[0], v2_x[1] - v1[1], v2_x[2] - v1[2]];
			const xresult = MathUtils.crossVector(this._oldForward.slice(), MathUtils.normalizeVector(_v2_xSub_v1));
			const xscaleDir = xresult[1] < 0 ? -1 : 1;
			const scalex = xdistance / (obbSizeX * 2) * xscaleDir * oldScaleDir * -anchor[0];

			const _v2_zSub_v1 = [v2_z[0] - v1[0], v2_z[1] - v1[1], v2_z[2] - v1[2]];
			const zresult = MathUtils.dotVector(MathUtils.normalizeVector(_v2_zSub_v1), this._oldForward.slice());
			const zscaleDir = zresult > 0 ? 1 : -1;
			const scalez = zdistance / (obbSizeZ * 2) * zscaleDir * oldScaleDir * -anchor[1];

			const v2 = position.slice();
			const distance = MathUtils.sqrt(MathUtils.getDistanceToSquared(v2, v1));
			const v2_Sub_v1Nor = MathUtils.normalizeVector([v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2]]);

			const newpos = [
				v2_Sub_v1Nor[0] * (distance / 2) + v1[0] - this._downCenter[0],
				v2_Sub_v1Nor[1] * (distance / 2) + v1[1] - this._downCenter[1],
				v2_Sub_v1Nor[2] * (distance / 2) + v1[2] - this._downCenter[2]
			];

			return [newpos, scalex, scalez];
		}
	}

	_selfToWorld(val) {
		if (this._centerPos) {
			return this.object.selfToWorld(MathUtils.addVector(val, this.__oldOffsetPos));
		}

		const yPos = val[1] > 0 ? val[1] * 2 : 0;
		const offsetXYZ = [
			this.__oldOffsetPos[0],
			this.__oldOffsetPos[1] - this.__oldSize[1] / 2,
			this.__oldOffsetPos[2]
		];

		return this.object.selfToWorld([val[0] + offsetXYZ[0], yPos + offsetXYZ[1], val[2] + offsetXYZ[2]]);
	}

	_footPoint(a, b, c) {
		let result = [];
		let dx = a[0] - b[0];
		let dy = a[1] - b[1];
		let dz = a[2] - b[2];
		if (MathUtils.abs(dx) < 0.000001 && MathUtils.abs(dy) < 0.000001 && MathUtils.abs(dz) < 0.000001) {
			result = [a[0], a[1], a[2]];
		}
		else {
			let u = (c[0] - a[0]) * (a[0] - b[0]) + (c[1] - a[1]) * (a[1] - b[1]) + (c[2] - a[2]) * (a[2] - b[2]);
			u = u / ((dx * dx) + (dy * dy) + (dz * dz));
			result[0] = a[0] + u * dx;
			result[1] = a[1] + u * dy;
			result[2] = a[2] + u * dz;
		}
		return result;
	}

	_angleTwoVectorsY(from, to) {
		const angle = MathUtils.getAngleBetweenVectors(from, to);
		const v3 = MathUtils.crossVector(from, to);
		return v3[2] < 0 ? angle : 360 - angle;
	}

	/**
	 * 更新对象的变换操作
	 * @param {object} param
	 * @param {string} param.mode - 当前对象的变换方式
	 * @param {string} param.axis - 当前对象的操作方向
	 * @param {Array.<Number>} param.anchor - 局部坐标系中x和z的负方向
	 * @param {boolean} param.shift - 是否按下shift键
	 * @param {number} param.step - 对象旋转的精度值
	 * @param {Array.<Number>} param.btnOffset - 旋转按钮相对于对象中心的屏幕坐标偏移
	 * @param {Array.<Number>} param.offset - 用户屏幕坐标的偏移量
	 * @param {number} param.x - 屏幕x坐标
	 * @param {number} param.y - 屏幕y坐标
	 * @public
	 */
	update(param) {
		this._mode = param.mode;
		const axis = param.axis;
		const anchor = param.anchor;
		const shift = param.shift;
		const step = param.step < 0 ? 0.1 : param.step;
		this._btnOffset = param.btnOffset || [0, 0];
		const offset = param.offset || [0, 0];

		const x = param.x - offset[0];
		const y = param.y - offset[1];

		if (this._mode == 'translate') {
			if (axis == 'y') {
				this._moveY(x, y);
			}
			else if (axis == 'xz') {
				this._moveXZ(x, y);
			}
		}
		else if (this._mode == 'angle') {
			if (shift) {
				this._angleY(x, y);
			}
			else {
				this._angleY(x, y, step);
			}
		}
		else if (this._mode == 'scale') {
			if (axis == 'y') {
				if (shift) {
					this._scaleY_equalProportion(x, y);
				}
				else {
					this._scaleY(x, y);
				}
			}
			else {
				if (shift) {
					this._scaleXZ_equalProportion(anchor, x, y);
				}
				else {
					this._scaleXZ(anchor, x, y);
				}
			}
		}
		this._updateRealObjectsTransform();
	}

	/**
	 * 停止拖拽
	 * @public
	 */
	stop() {
		this._mouseOffsetV3 = null;
		this._mousePos = null;
		this._objQuaterStart = null;
		this._anchorPosV3 = null;
		this._oldForward = null;
		this._oldScale = null;
		this._oldPosition = null;
		this._edgeXPosV3 = null;
		this._edgeZPosV3 = null;
		this._angleBtnDir = null;
		this._mode = null;
		this._downCenter = null;
		this._oldSize = null;
	}

	/**
	 * 取消等比例缩放,默认为false
	 * @type {boolean}
	 * @public
	 */
	get cancelShiftScale() {
		return this._cancelShiftScale;
	}

	set cancelShiftScale(val) {
		if (typeof val === 'boolean') {
			this._cancelShiftScale = val;
		}
	}

	get snapRotationStep() {
		return this._snapRotationStep;
	}
	set snapRotationStep(val) {
		if (this._snapRotationStep > 0) {
			this._snapRotationStep = val;
		}
		else {
			this._snapRotationStep = 45;
		}
	}
	/**
	 * 设置变换对象
	 * @param {Array.<Object>} objs - 变换对象
	 * @param {boolean} ignoreCenter - 是否忽略中心位置
	 * @public
	 */
	setObjects(objs = [], ignoreCenter = false) {
		let objects = Utils.uniqueObjects(objs);
		objects = objects.filter(object => {
			return !object.destroyed;
		});

		this._destroyBaseObject3D();
		if (!objects.length) {
			return;
		}

		this._createObjMap(objects, ignoreCenter);
	}

	getObjects() {
		return Object.values(this._objMap);
	}

	_destroyBaseObject3D() {
		if (this._emptyObjects.length) {
			for (let i = 0; i < this._emptyObjects.length; i++) {
				const emptyObject = this._emptyObjects[i];
				emptyObject.destroy();
			}
			this._emptyObjects = [];
		}
		this._objMap = {};
	}

	async _createObjMap(objs, ignoreCenter) {
		// get obb
		let obbs = [];
		for (let i = 0; i < objs.length; i++) {
			await objs[i].waitForComplete();
			obbs.push(objs[i].getOBB(false));
		}

		let max = obbs[0].max, min = obbs[0].min;
		for (let i = 1; i < objs.length; i++) {
			max[0] = MathUtils.max(obbs[i].max[0], max[0]);
			max[1] = MathUtils.max(obbs[i].max[1], max[1]);
			max[2] = MathUtils.max(obbs[i].max[2], max[2]);

			min[0] = MathUtils.min(obbs[i].min[0], min[0]);
			min[1] = MathUtils.min(obbs[i].min[1], min[1]);
			min[2] = MathUtils.min(obbs[i].min[2], min[2]);
		}

		this.object.position = ignoreCenter ? [0, 0, 0] : [min[0] + (max[0] - min[0]) / 2, min[1], min[2] + (max[2] - min[2]) / 2];
		this.object.angles = objs.length > 1 ? [0, 0, 0] : objs[0].angles;

		for (let i = 0; i < objs.length; i++) {
			const obj = objs[i];
			this._objMap[obj.uuid] = obj;
			const emptyObject = new Object3D({
				tags: ['helper']
			});
			emptyObject.parent = this.object;
			emptyObject.name = obj.uuid;
			emptyObject.setQueryable(false, true);

			const halfSize = MathUtils.divideVector(MathUtils.divideVector(obbs[i].size, obj.scale), 2);
			const center = MathUtils.divideVector(
				MathUtils.vec3ApplyQuat(
					MathUtils.subVector(obbs[i].center, obj.position), MathUtils.quat.invert([], obj.quaternion)
				), obj.scale);

			emptyObject.initialLocalBoundingBox = { halfSize, center };

			emptyObject.matrixWorld = obj.matrixWorld;
			this._emptyObjects.push(emptyObject);
		}

		this._initOrientedBoxData(this.object);
	}

	_updateRealObjectsTransform() {
		if (!this._emptyObjects.length) { return; }
		for (let i = 0; i < this._emptyObjects.length; i++) {
			const emptyObject = this._emptyObjects[i];
			const realObject = this._objMap[emptyObject.name];
			realObject.matrixWorld = emptyObject.matrixWorld;
		}
	}

}

export { DragControlComponent };