import * as THREE from "three";
import * as CONSTANTS from '../../../../types/world/worldConstants';
import * as Types from "../../../../types/world/worldTypes";

function createAndMoveReferenceLine(
    point: Types.Vector3,
    cursorPosition: Types.Vector3,
    isSnapped: Types.RefBoolean,
    ispreSnapped: Types.RefBoolean,
    line: Types.RefLine,
    setRefTextUpdate: Types.NumberIncrementState,
    floorPlanGroup: Types.RefGroup,
    ReferenceLineMesh: Types.RefMesh,
    LineCreated: Types.RefBoolean,
    Tube: Types.RefTubeGeometry,
    anglesnappedPoint: Types.RefVector3,
    isAngleSnapped: Types.RefBoolean
): void {

    //////////  Creating new and maintaining the old reference line and also snap the reference line based on its angle  //////////

    const startPoint = point;

    const dx = cursorPosition.x - startPoint.x;
    const dz = cursorPosition.z - startPoint.z;
    let angle = Math.atan2(dz, dx);

    angle = (angle * 180) / Math.PI;
    angle = (angle + 360) % 360;

    const snapAngles = [0, 90, 180, 270, 360];
    const snapThreshold = 2.5;

    const closestSnapAngle = snapAngles.reduce((prev, curr) =>
        Math.abs(curr - angle) < Math.abs(prev - angle) ? curr : prev
    );

    if (!isSnapped.current && !ispreSnapped.current && line.current.length > 0) {
        if (Math.abs(closestSnapAngle - angle) <= snapThreshold) {
            const snappedAngleRad = (closestSnapAngle * Math.PI) / 180;
            const distance = Math.sqrt(dx * dx + dz * dz);
            const snappedX = startPoint.x + distance * Math.cos(snappedAngleRad);
            const snappedZ = startPoint.z + distance * Math.sin(snappedAngleRad);

            if (
                cursorPosition.distanceTo(
                    new THREE.Vector3(snappedX, 0.01, snappedZ)
                ) < 2
            ) {
                cursorPosition.set(snappedX, 0.01, snappedZ);
                isAngleSnapped.current = true;
                anglesnappedPoint.current = new THREE.Vector3(
                    snappedX,
                    0.01,
                    snappedZ
                );
            } else {
                isAngleSnapped.current = false;
                anglesnappedPoint.current = null;
            }
        } else {
            isAngleSnapped.current = false;
            anglesnappedPoint.current = null;
        }
    } else {
        isAngleSnapped.current = false;
        anglesnappedPoint.current = null;
    }

    if (!LineCreated.current) {
        setRefTextUpdate((prevUpdate) => prevUpdate - 1);
        const path = new THREE.LineCurve3(startPoint, cursorPosition);
        Tube.current = new THREE.TubeGeometry(path, CONSTANTS.lineConfig.tubularSegments, CONSTANTS.lineConfig.radius, CONSTANTS.lineConfig.radialSegments, false);
        const material = new THREE.MeshBasicMaterial({ color: CONSTANTS.lineConfig.helperColor });
        ReferenceLineMesh.current = new THREE.Mesh(Tube.current, material);
        ReferenceLineMesh.current.name = CONSTANTS.lineConfig.referenceName;
        ReferenceLineMesh.current.userData = {
            linePoints: { startPoint, cursorPosition },
        };
        floorPlanGroup.current?.add(ReferenceLineMesh.current);
        LineCreated.current = true;
    } else {
        if (ReferenceLineMesh.current) {
            const path = new THREE.LineCurve3(startPoint, new THREE.Vector3(cursorPosition.x, 0.01, cursorPosition.z));
            Tube.current = new THREE.TubeGeometry(path, CONSTANTS.lineConfig.tubularSegments, CONSTANTS.lineConfig.radius, CONSTANTS.lineConfig.radialSegments, false);

            if (ReferenceLineMesh.current) {
                ReferenceLineMesh.current.userData = {
                    linePoints: { startPoint, cursorPosition },
                };
                ReferenceLineMesh.current.geometry.dispose();
                ReferenceLineMesh.current.geometry = Tube.current;
            }
        }
    }
}

export default createAndMoveReferenceLine;
