package net.halbear.Terrain4J.EngineCore.Main;

import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Input.KeyboardInput;
import net.halbear.Terrain4J.EngineCore.Input.MouseListener;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.GameLogic;
import net.halbear.Terrain4J.EngineCore.Logic.InitData;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.ModelLoader;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Camera;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Scene;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.MaterialData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelData;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.tinylog.Logger;

import java.util.ArrayList;
import java.util.List;

import static org.lwjgl.glfw.GLFW.*;

public class GameCore implements GameLogic {

    private static final float MOUSE_SENSITIVITY = 0.1f;
    private static final float MOVEMENT_SPEED = 0.01f;
    private final Vector3f rotatingAngle = new Vector3f(0, 1, 0);
    private float angle = 180;
    private List<Float> angles = new ArrayList<>();
    private List<Vector3f> rotatingAngles = new ArrayList<>();
    private List<Vector3f> Velocities = new ArrayList<>();
    private Actor CubeActor;
    private List<Actor> CubeActors = new ArrayList<>();

    @Override
    public void cleanup() {

    }

    @Override
    public InitData Initialise(EngineInstance engineInstance) {
        Scene scene = engineInstance.scene();
        List<ModelData> models = new ArrayList<>();

        ModelData SponzaData = ModelLoader.LoadModel("resources/models/Sponza/Sponza.json");
        List<MaterialData> SponzaMaterial = ModelLoader.LoadMaterials("resources/models/Sponza/Sponza_mat.json");
        scene.AddActor(new Actor("Sponza", SponzaData.ID(), new Vector3f(0,0,-5f)));

        models.add(ModelLoader.LoadModel("resources/models/Metro/OBJ/MetroLeadCar.json"));
        models.add(ModelLoader.LoadModel("resources/models/Corvette/CorvetteOBJ.json"));
        //models.add(ModelLoader.LoadModel("resources/models/Sloop/SloopOBJ.json"));
        models.add(ModelLoader.LoadModel("resources/models/melona/melona.json"));
        models.add(ModelLoader.LoadModel("resources/models/Alice/Welsh040Alice.json"));
        for(int i = 0; i < 50000; i++) {
            CubeActors.add(new Actor("CubeEntity" + i, models.get((int)Math.round(Math.min(Math.max((Math.random() * models.size() - 1),0),models.size() - 1))).ID(), new Vector3f((float)(-200 + Math.random() * 400), (float)(-50 + Math.random() * 100), (float)(250 + Math.random() * -500))));
            angles.add((float)(Math.random() * 360));
            rotatingAngles.add(new Vector3f((float)(Math.random()*2), (float)(Math.random()*2), (float)(Math.random()*2)));
            Velocities.add(new Vector3f((float)(Math.random()*2), (float)(Math.random()*2), (float)(Math.random()*2)));
        }
        CubeActors.forEach(scene::AddActor);

        List<MaterialData> materials = new ArrayList<>();
        materials.addAll(ModelLoader.LoadMaterials("resources/models/Metro/OBJ/MetroLeadCar_mat.json"));
        materials.addAll(ModelLoader.LoadMaterials("resources/models/Corvette/CorvetteOBJ_mat.json"));
        materials.addAll(ModelLoader.LoadMaterials("resources/models/Sloop/SloopOBJ_mat.json"));
        materials.addAll(ModelLoader.LoadMaterials("resources/models/melona/melona_mat.json"));
        materials.addAll(ModelLoader.LoadMaterials("resources/models/Alice/Welsh040Alice_mat.json"));
        materials.addAll(SponzaMaterial);
        models.add(SponzaData);
        Camera camera = scene.GetCamera();
        camera.SetPosition(0.0f, 0.0f, 0.0f);
        camera.SetRotation((float) Math.toRadians(0.0f), (float) Math.toRadians(0.0f),0);
        return new InitData(models,materials);
    }

    @Override
    public void CameraInput(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
        Scene scene = engineInstance.scene();
        Window window = engineInstance.window();
        Camera camera = scene.GetCamera();

        MouseListener MouseInput = window.getMouseInput();
        if(MouseInput.isRightButtonPressed()){
            Vector2f deltaPos = MouseInput.getDeltaPos();
            camera.AddRotation((float)Math.toRadians(-deltaPos.y * MOUSE_SENSITIVITY),(float)Math.toRadians(-deltaPos.x * MOUSE_SENSITIVITY),0);
        }
    }

    @Override
    public void Input(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
        Scene scene = engineInstance.scene();
        Window window = engineInstance.window();

        KeyboardInput input = window.getKeyboardInput();
        float MovementDist = (float)(FrameDiffNanoSeconds / 1000000L) * MOVEMENT_SPEED;
        Camera camera = scene.GetCamera();
        if(input.keyPressed(GLFW_KEY_W)){
            camera.MoveForward(MovementDist);
        }
        if(input.keyPressed(GLFW_KEY_S)){
            camera.MoveBackward(MovementDist);
        }
        if(input.keyPressed(GLFW_KEY_A)){
            camera.MoveLeft(MovementDist);
        }
        if(input.keyPressed(GLFW_KEY_D)){
            camera.MoveRight(MovementDist);
        }
        if(input.keyPressed(GLFW_KEY_SPACE)){
            camera.MoveUp(MovementDist);
        }
        if(input.keyPressed(GLFW_KEY_LEFT_SHIFT)){
            camera.MoveDown(MovementDist);
        }
    }

    @Override
    public void Update(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
        float DeltaTime = (float)(FrameDiffNanoSeconds/16000000.0);
        for(int i = 0; i < angles.size()/2; i++) {
            angles.set(i, (angles.get(i) + 0.1f* DeltaTime) % 360);
            Camera camera = engineInstance.scene().GetCamera();
            CubeActors.get(i).GetRotation().identity().rotateAxis((float) EngineConfig.DegToRad * angles.get(i), rotatingAngles.get(i));
            //CubeActors.get(i).GetPosition().add((Velocities.get(i).x/100.0f) * DeltaTime,(Velocities.get(i).y/100.0f) * DeltaTime,(Velocities.get(i).z/100.0f) * DeltaTime);
            CubeActors.get(i).UpdateModelMatrix();
        }
    }

    @Override
    public void UpdateFastThread(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
        float DeltaTime = (float)(FrameDiffNanoSeconds/16000000.0);
        for(int i = angles.size()/2; i < angles.size(); i++) {
            angles.set(i, (angles.get(i) + 1.0f* DeltaTime) % 360);
            CubeActors.get(i).GetRotation().identity().rotateAxis((float) EngineConfig.DegToRad * angles.get(i), rotatingAngles.get(i));
           // CubeActors.get(i).GetPosition().add((Velocities.get(i).x/100.0f) * DeltaTime,(Velocities.get(i).y/100.0f) * DeltaTime,(Velocities.get(i).z/100.0f) * DeltaTime);
           CubeActors.get(i).UpdateModelMatrix();
        }
    }
}
