import { useRef, useEffect } from 'react';
import { useInput } from '../hooks/useInput';
import { useGLTF, useAnimations, OrbitControls } from '@react-three/drei';
import { useFrame, useThree } from '@react-three/fiber';
import { Quaternion, Vector3 } from 'three';

let walkDirection = new Vector3();
let rotateAngle = new Vector3(0,1,0);
let rotateQuaternion = new Quaternion();
let cameraTarget = new Vector3();


const directionOffset = ({ forward, backward, left, right }) => {
    var directionOffset = 0;

    if(forward){
        if(left){
            directionOffset = Math.PI / 4;
        } else if(right){
            directionOffset = -Math.PI / 4;
        }
    } else if(backward){
        if(left){
            directionOffset = Math.PI / 4 + Math.PI / 2;
        } else if(right){
            directionOffset = -Math.PI / 4 - Math.PI / 2;
        } else {
            directionOffset = Math.PI;
        }
    } else if(left){
        directionOffset = Math.PI / 2;
    } else if(right){
        directionOffset = -Math.PI / 2;
    }

    return directionOffset;
};



const Player = () => {

    const {forward, backward, left, right, jump, shift } = useInput();

    const model = useGLTF("models/player.glb");
    const { actions } = useAnimations(model.animations, model.scene);
    //console.log(model);

    model.scene.scale.set(0.2,0.2,0.2);

    //ombra
    model.scene.traverse((object) => {
        if(object.isMesh){
            object.castShadow = true;
        }
    });

    const currentAction = useRef("");
    
    const controlsRef = useRef(null);
    const camera = useThree((state) => state.camera);
    
    const upadateCameraTarget = (moveX = Number, moveZ = Number) => {
        //move camera
        camera.position.x += moveX;
        camera.position.z += moveZ;

        //update camera target
        cameraTarget.x = model.scene.position.x;
        cameraTarget.y = model.scene.position.y + 2;
        cameraTarget.z = model.scene.position.z;

        if(controlsRef.current) controlsRef.current.target = cameraTarget;
    };



    useEffect(() => {
        //actions.Idle.play()
        let action = "";

        if(forward || backward || left || right){
            action = "walking";
            if(shift){
                action = "running";
            }
        }
        else if(jump){
            action = "jumping";
        }
        else{
            action = "Idle";
        }

        if(currentAction.current != action){
            const nextActionToPlay = actions[action];
            const current = actions[currentAction.current];
            current?.fadeOut(0.2);
            nextActionToPlay.reset().fadeIn(0.2).play();
            currentAction.current = action;
        }
    }, [forward, backward, left, right, jump, shift]);

    useFrame((state, delta) => {
        if(currentAction.current == "running" || currentAction.current == "walking"){
            //calculate camera direction    
            let angleYCameraDirection = Math.atan2(
                camera.position.x - model.scene.position.x,
                camera.position.z - model.scene.position.z
            );

            //diagonal movment angle offset
            let newDirectionOffset = directionOffset({
                forward, backward, left, right
            });
        
            //rotate model
            rotateQuaternion.setFromAxisAngle(
                rotateAngle,
                angleYCameraDirection + newDirectionOffset
            );
            model.scene.quaternion.rotateTowards(rotateQuaternion, 0.2);
        
            //calculate direction
            camera.getWorldDirection(walkDirection);
            walkDirection.y = 0;
            walkDirection.normalize();
            walkDirection.applyAxisAngle(rotateAngle, newDirectionOffset);

            const velocity = currentAction.current == "running" ? 30 : 20;

            const moveX = walkDirection.x * velocity * delta;
            const moveZ = walkDirection.z * velocity * delta;
            model.scene.position.x += moveX;
            model.scene.position.z += moveZ;
            upadateCameraTarget(moveX, moveZ);
        }
    });


    return( 
        <>
            <OrbitControls ref={controlsRef} target={[-2.64, -0.71, 0.03]} />
            <primitive object={model.scene} />
        </>
    );
    
}

export default Player;