import { Vec3 } from '../../core/math/vec3.js';
import { Quat } from '../../core/math/quat.js';
import { TransformGizmo } from './transform-gizmo.js';
import { BoxShape } from './shape/box-shape.js';
import { PlaneShape } from './shape/plane-shape.js';
import { BoxLineShape } from './shape/boxline-shape.js';

const v1 = new Vec3();
const v2 = new Vec3();
const point = new Vec3();
const delta = new Vec3();
const q = new Quat();
const GLANCE_EPSILON = 0.01;
class ScaleGizmo extends TransformGizmo {
		constructor(camera, layer){
				super(camera, layer, 'gizmo:scale'), this._shapes = {
						xyz: new BoxShape(this._device, {
								axis: 'xyz',
								layers: [
										this._layer.id
								],
								defaultColor: this._theme.shapeBase.xyz,
								hoverColor: this._theme.shapeHover.xyz,
								disabledColor: this._theme.disabled
						}),
						yz: new PlaneShape(this._device, {
								axis: 'x',
								layers: [
										this._layer.id
								],
								rotation: new Vec3(0, 0, -90),
								defaultColor: this._theme.shapeBase.x,
								hoverColor: this._theme.shapeHover.x,
								disabledColor: this._theme.disabled
						}),
						xz: new PlaneShape(this._device, {
								axis: 'y',
								layers: [
										this._layer.id
								],
								rotation: new Vec3(0, 0, 0),
								defaultColor: this._theme.shapeBase.y,
								hoverColor: this._theme.shapeHover.y,
								disabledColor: this._theme.disabled
						}),
						xy: new PlaneShape(this._device, {
								axis: 'z',
								layers: [
										this._layer.id
								],
								rotation: new Vec3(90, 0, 0),
								defaultColor: this._theme.shapeBase.z,
								hoverColor: this._theme.shapeHover.z,
								disabledColor: this._theme.disabled
						}),
						x: new BoxLineShape(this._device, {
								axis: 'x',
								layers: [
										this._layer.id
								],
								rotation: new Vec3(0, 0, -90),
								defaultColor: this._theme.shapeBase.x,
								hoverColor: this._theme.shapeHover.x,
								disabledColor: this._theme.disabled
						}),
						y: new BoxLineShape(this._device, {
								axis: 'y',
								layers: [
										this._layer.id
								],
								rotation: new Vec3(0, 0, 0),
								defaultColor: this._theme.shapeBase.y,
								hoverColor: this._theme.shapeHover.y,
								disabledColor: this._theme.disabled
						}),
						z: new BoxLineShape(this._device, {
								axis: 'z',
								layers: [
										this._layer.id
								],
								rotation: new Vec3(90, 0, 0),
								defaultColor: this._theme.shapeBase.z,
								hoverColor: this._theme.shapeHover.z,
								disabledColor: this._theme.disabled
						})
				}, this._coordSpace = 'local', this._nodeScales = new Map(), this._uniform = false, this.snapIncrement = 1, this.flipAxes = true, this.flipPlanes = true, this.lowerBoundScale = new Vec3(-Infinity, -Infinity, -Infinity);
				this._createTransform();
				this.on(TransformGizmo.EVENT_TRANSFORMSTART, ()=>{
						this._storeNodeScales();
						this._drag(true);
				});
				this.on(TransformGizmo.EVENT_TRANSFORMMOVE, (point)=>{
						const scaleDelta = delta.copy(point).sub(this._selectionStartPoint);
						if (this.snap) {
								scaleDelta.mulScalar(1 / this.snapIncrement);
								scaleDelta.round();
								scaleDelta.mulScalar(this.snapIncrement);
						}
						scaleDelta.mulScalar(1 / this._scale);
						this._setNodeScales(scaleDelta.add(Vec3.ONE));
				});
				this.on(TransformGizmo.EVENT_TRANSFORMEND, ()=>{
						this._drag(false);
				});
				this.on(TransformGizmo.EVENT_NODESDETACH, ()=>{
						this._nodeScales.clear();
				});
		}
		set coordSpace(value) {}
		get coordSpace() {
				return this._coordSpace;
		}
		set uniform(value) {
				this._uniform = value ?? this._uniform;
		}
		get uniform() {
				return this._uniform;
		}
		set axisGap(value) {
				this._setArrowProp('gap', value);
		}
		get axisGap() {
				return this._shapes.x.gap;
		}
		set axisLineThickness(value) {
				this._setArrowProp('lineThickness', value);
		}
		get axisLineThickness() {
				return this._shapes.x.lineThickness;
		}
		set axisLineLength(value) {
				this._setArrowProp('lineLength', value);
		}
		get axisLineLength() {
				return this._shapes.x.lineLength;
		}
		set axisLineTolerance(value) {
				this._setArrowProp('tolerance', value);
		}
		get axisLineTolerance() {
				return this._shapes.x.tolerance;
		}
		set axisBoxSize(value) {
				this._setArrowProp('boxSize', value);
		}
		get axisBoxSize() {
				return this._shapes.x.boxSize;
		}
		set axisPlaneSize(value) {
				this._setPlaneProp('size', value);
		}
		get axisPlaneSize() {
				return this._shapes.yz.size;
		}
		set axisPlaneGap(value) {
				this._setPlaneProp('gap', value);
		}
		get axisPlaneGap() {
				return this._shapes.yz.gap;
		}
		set axisCenterSize(value) {
				this._shapes.xyz.size = value;
		}
		get axisCenterSize() {
				return this._shapes.xyz.size;
		}
		set axisCenterTolerance(value) {
				this._shapes.xyz.tolerance = value;
		}
		get axisCenterTolerance() {
				return this._shapes.xyz.tolerance;
		}
		set flipShapes(value) {
				this.flipAxes = value;
				this.flipPlanes = value;
		}
		get flipShapes() {
				return this.flipAxes && this.flipPlanes;
		}
		_setArrowProp(prop, value) {
				this._shapes.x[prop] = value;
				this._shapes.y[prop] = value;
				this._shapes.z[prop] = value;
		}
		_setPlaneProp(prop, value) {
				this._shapes.yz[prop] = value;
				this._shapes.xz[prop] = value;
				this._shapes.xy[prop] = value;
		}
		_shapesLookAtCamera() {
				const cameraDir = this.cameraDir;
				let dot = cameraDir.dot(this.root.right);
				this._shapes.x.entity.enabled = 1 - Math.abs(dot) > GLANCE_EPSILON;
				if (this.flipAxes) {
						this._shapes.x.flipped = dot < 0;
				}
				dot = cameraDir.dot(this.root.up);
				this._shapes.y.entity.enabled = 1 - Math.abs(dot) > GLANCE_EPSILON;
				if (this.flipAxes) {
						this._shapes.y.flipped = dot < 0;
				}
				dot = cameraDir.dot(this.root.forward);
				this._shapes.z.entity.enabled = 1 - Math.abs(dot) > GLANCE_EPSILON;
				if (this.flipAxes) {
						this._shapes.z.flipped = dot > 0;
				}
				v1.cross(cameraDir, this.root.right);
				this._shapes.yz.entity.enabled = 1 - v1.length() > GLANCE_EPSILON;
				if (this.flipPlanes) {
						this._shapes.yz.flipped = v2.set(0, +(v1.dot(this.root.forward) < 0), +(v1.dot(this.root.up) < 0));
				}
				v1.cross(cameraDir, this.root.forward);
				this._shapes.xy.entity.enabled = 1 - v1.length() > GLANCE_EPSILON;
				if (this.flipPlanes) {
						this._shapes.xy.flipped = v2.set(+(v1.dot(this.root.up) < 0), +(v1.dot(this.root.right) > 0), 0);
				}
				v1.cross(cameraDir, this.root.up);
				this._shapes.xz.entity.enabled = 1 - v1.length() > GLANCE_EPSILON;
				if (this.flipPlanes) {
						this._shapes.xz.flipped = v2.set(+(v1.dot(this.root.forward) > 0), 0, +(v1.dot(this.root.right) > 0));
				}
		}
		_drag(state) {
				for(const axis in this._shapes){
						const shape = this._shapes[axis];
						switch(this.dragMode){
								case 'show':
										{
												continue;
										}
								case 'hide':
										{
												shape.visible = !state;
												continue;
										}
								case 'selected':
										{
												if (this._selectedAxis === 'xyz') {
														shape.visible = state ? axis.length === 1 : true;
														continue;
												}
												if (this._selectedIsPlane) {
														shape.visible = state ? axis.length === 1 && !axis.includes(this._selectedAxis) : true;
														continue;
												}
												shape.visible = state ? axis === this._selectedAxis : true;
												continue;
										}
						}
				}
				this.fire(TransformGizmo.EVENT_RENDERUPDATE);
		}
		_storeNodeScales() {
				for(let i = 0; i < this.nodes.length; i++){
						const node = this.nodes[i];
						this._nodeScales.set(node, node.getLocalScale().clone());
				}
		}
		_setNodeScales(scaleDelta) {
				for(let i = 0; i < this.nodes.length; i++){
						const node = this.nodes[i];
						const scale = this._nodeScales.get(node);
						if (!scale) {
								continue;
						}
						node.setLocalScale(v1.copy(scale).mul(scaleDelta).max(this.lowerBoundScale));
				}
		}
		_screenToPoint(x, y) {
				const gizmoPos = this.root.getLocalPosition();
				const mouseWPos = this._camera.screenToWorld(x, y, 1);
				const axis = this._selectedAxis;
				const isPlane = this._selectedIsPlane;
				const ray = this._createRay(mouseWPos);
				const plane = this._createPlane(axis, axis === 'xyz', !isPlane);
				if (!plane.intersectsRay(ray, point)) {
						return point;
				}
				if (axis === 'xyz') {
						const projDir = v2.add2(this._camera.entity.up, this._camera.entity.right).normalize();
						const dir = v1.sub2(point, gizmoPos);
						const v = dir.length() * dir.normalize().dot(projDir);
						point.set(v, v, v);
						return point;
				}
				q.copy(this._rootStartRot).invert().transformVector(point, point);
				if (!isPlane) {
						this._projectToAxis(point, axis);
				}
				if (this.flipAxes) {
						const cameraDir = this.cameraDir;
						const rot = q.copy(this._rootStartRot);
						let dot = cameraDir.dot(rot.transformVector(Vec3.RIGHT, v1));
						point.x *= dot < 0 ? -1 : 1;
						dot = cameraDir.dot(rot.transformVector(Vec3.UP, v1));
						point.y *= dot < 0 ? -1 : 1;
						dot = cameraDir.dot(rot.transformVector(Vec3.FORWARD, v1));
						point.z *= dot > 0 ? -1 : 1;
				}
				if (this._uniform && isPlane) {
						v1.set(1, 1, 1);
						v1[axis] = 0;
						point.copy(v1.mulScalar(v1.dot(point)));
						point[axis] = 0;
				}
				return point;
		}
		prerender() {
				super.prerender();
				if (!this.enabled) {
						return;
				}
				this._shapesLookAtCamera();
		}
}

export { ScaleGizmo };
