package de.blight.game.control;

import com.jme3.bullet.control.CharacterControl;
import com.jme3.input.InputManager;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Spatial;
import de.blight.game.config.KeyBindings;

public class PlayerInputControl {

    private static final float MOVE_SPEED   = 0.07f;
    private static final float SPRINT_MULT  = 1.5f;
    private static final float ROTATE_SPEED = 10f;

    private static final String[] ACTION_NAMES =
        {"Forward", "Backward", "Left", "Right", "Jump", "Sprint"};

    private final InputManager inputManager;
    private final Camera       cam;

    private CharacterControl physicsChar;
    private Spatial          visual;

    private boolean forward, backward, left, right, sprint;
    private boolean paused = false;

    // Listener als Feld, damit er bei reload nicht doppelt registriert wird
    private final ActionListener actionListener = (name, isPressed, tpf) -> {
        if (paused) return;
        switch (name) {
            case "Forward"  -> forward  = isPressed;
            case "Backward" -> backward = isPressed;
            case "Left"     -> left     = isPressed;
            case "Right"    -> right    = isPressed;
            case "Sprint"   -> sprint   = isPressed;
            case "Jump"     -> { if (isPressed && physicsChar != null) physicsChar.jump(); }
        }
    };

    public PlayerInputControl(InputManager inputManager, Camera cam, KeyBindings kb) {
        this.inputManager = inputManager;
        this.cam          = cam;
        registerMappings(kb);
    }

    public void setPhysicsCharacter(CharacterControl physicsChar) {
        this.physicsChar = physicsChar;
    }

    public void setVisual(Spatial visual) {
        this.visual = visual;
    }

    public void setPaused(boolean paused) {
        this.paused = paused;
        if (paused) {
            forward = backward = left = right = sprint = false;
            if (physicsChar != null) physicsChar.setWalkDirection(Vector3f.ZERO);
        }
    }

    /** Löscht alte Mappings und registriert neue aus den übergebenen KeyBindings. */
    public void reloadBindings(KeyBindings kb) {
        for (String a : ACTION_NAMES) inputManager.deleteMapping(a);
        registerMappings(kb);
        // Zustand zurücksetzen, damit keine Taste „hängt"
        forward = backward = left = right = sprint = false;
        if (physicsChar != null) physicsChar.setWalkDirection(Vector3f.ZERO);
    }

    private void registerMappings(KeyBindings kb) {
        inputManager.addMapping("Forward",  new KeyTrigger(kb.forward));
        inputManager.addMapping("Backward", new KeyTrigger(kb.backward));
        inputManager.addMapping("Left",     new KeyTrigger(kb.left));
        inputManager.addMapping("Right",    new KeyTrigger(kb.right));
        inputManager.addMapping("Jump",     new KeyTrigger(kb.jump));
        inputManager.addMapping("Sprint",   new KeyTrigger(kb.sprint));
        inputManager.addListener(actionListener, ACTION_NAMES);
    }

    public void update(float tpf) {
        if (physicsChar == null || paused) return;

        Vector3f camDir  = cam.getDirection().clone().setY(0).normalizeLocal();
        Vector3f camLeft = cam.getLeft().clone().setY(0).normalizeLocal();

        Vector3f moveDir = new Vector3f();
        if (forward)  moveDir.addLocal(camDir);
        if (backward) moveDir.subtractLocal(camDir);
        if (left)     moveDir.addLocal(camLeft);
        if (right)    moveDir.subtractLocal(camLeft);

        if (moveDir.lengthSquared() > 0.001f) {
            moveDir.normalizeLocal();
            float speed = sprint ? MOVE_SPEED * SPRINT_MULT : MOVE_SPEED;
            physicsChar.setWalkDirection(moveDir.mult(speed));

            if (visual != null) {
                Quaternion targetRot = new Quaternion();
                targetRot.lookAt(moveDir, Vector3f.UNIT_Y);
                Quaternion current = visual.getLocalRotation().clone();
                current.slerp(targetRot, ROTATE_SPEED * tpf);
                visual.setLocalRotation(current);
            }
        } else {
            physicsChar.setWalkDirection(Vector3f.ZERO);
        }
    }
}
