import { DoubleSide, Matrix4, MeshBasicMaterial, PlaneGeometry, Quaternion, Vector3 } from "three";
import { ShadedGeometry } from "../../../src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js";
import { GizmoNode } from "./GizmoNode.js";
import { TransformMode } from "./TransformMode.js";

// Reusable utility variables
const _alignVector = new Vector3(0, 1, 0);
const _identityQuaternion = new Quaternion();
const _dirVector = new Vector3();
const _tempMatrix = new Matrix4();
const _unitX = new Vector3(1, 0, 0);
const _unitY = new Vector3(0, 1, 0);
const _unitZ = new Vector3(0, 0, 1);
const _v1 = new Vector3();
const _v2 = new Vector3();
const _v3 = new Vector3();

const _tempVector = new Vector3();

export class TransformControlsPlane extends GizmoNode {

    constructor() {

        super();

        this.entity.add(ShadedGeometry.from(new PlaneGeometry(100000, 100000, 2, 2),
            new MeshBasicMaterial({
                visible: false,
                wireframe: true,
                side: DoubleSide,
                transparent: true,
                opacity: 0.1,
                toneMapped: false
            })));

        this.isTransformControlsPlane = true;

        this.type = 'TransformControlsPlane';
    }

    update() {

        let space = this.space;

        /**
         *
         * @type {Transform}
         */
        const transform = this.transform;

        transform.position.copy(this.worldPosition);

        if (this.mode === TransformMode.Scale) space = 'local'; // scale always oriented to local rotation

        _v1.copy(_unitX).applyQuaternion(space === 'local' ? this.worldQuaternion : _identityQuaternion);
        _v2.copy(_unitY).applyQuaternion(space === 'local' ? this.worldQuaternion : _identityQuaternion);
        _v3.copy(_unitZ).applyQuaternion(space === 'local' ? this.worldQuaternion : _identityQuaternion);

        // Align the plane for current transform mode, axis and space.

        _alignVector.copy(_v2);

        switch (this.mode) {

            case TransformMode.Translate:
            case TransformMode.Scale:
                switch (this.axis) {

                    case 'X':
                        _alignVector.copy(this.eye).cross(_v1);
                        _dirVector.copy(_v1).cross(_alignVector);
                        break;
                    case 'Y':
                        _alignVector.copy(this.eye).cross(_v2);
                        _dirVector.copy(_v2).cross(_alignVector);
                        break;
                    case 'Z':
                        _alignVector.copy(this.eye).cross(_v3);
                        _dirVector.copy(_v3).cross(_alignVector);
                        break;
                    case 'XY':
                        _dirVector.copy(_v3);
                        break;
                    case 'YZ':
                        _dirVector.copy(_v1);
                        break;
                    case 'XZ':
                        _alignVector.copy(_v3);
                        _dirVector.copy(_v2);
                        break;
                    case 'XYZ':
                    case 'E':
                        _dirVector.set(0, 0, 0);
                        break;

                }

                break;
            case TransformMode.Rotate:
            default:
                // special case for rotate
                _dirVector.set(0, 0, 0);

        }

        if (_dirVector.length() === 0) {

            // If in rotate mode, make the plane parallel to camera
            transform.rotation.copy(this.cameraQuaternion);

        } else {

            _tempMatrix.lookAt(_tempVector.set(0, 0, 0), _dirVector, _alignVector);

            transform.rotation.setFromRotationMatrix(_tempMatrix.elements);

        }

        super.update();
    }

}