package net.halbear.Terrain4J.EngineCore.Display;

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.Main.PrimaryRuntime;
import org.lwjgl.glfw.GLFWImage;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.glfw.GLFWWindowFocusCallback;
import org.lwjgl.system.MemoryUtil;
import org.tinylog.Logger;

import java.io.File;

import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.glfw.GLFWVulkan.glfwVulkanSupported;

public class Window {
    private final long handle;
    private final KeyboardInput keyboardInput;
    private final MouseListener mouseInput;
    private int height;
    private int width;
    private boolean displayFPS = false;

    public Window(String title){
        if (!glfwInit()) {
            throw new RuntimeException("Unable to initialize GLFW");
        }
        if (!glfwVulkanSupported()) {
            throw new RuntimeException("Cannot find a compatible Vulkan installable client driver (ICD)");
        }
        GLFWVidMode vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        if (vidMode == null) {
            throw new RuntimeException("Error getting primary monitor");
        }
        width = vidMode.width(); //Globals.getInstance().getWidth();
        height = vidMode.height();//Globals.getInstance().getHeight();

        glfwDefaultWindowHints();
        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
        glfwWindowHint(GLFW_MAXIMIZED, GLFW_FALSE);

        handle = glfwCreateWindow(width, height, title, MemoryUtil.NULL, MemoryUtil.NULL);
        if (handle == MemoryUtil.NULL) {
            //throw new RuntimeException("Failed to create the GLFW window");
        }
        keyboardInput = new KeyboardInput(handle);

        glfwSetFramebufferSizeCallback(handle, (window, w, h) -> {
            width = w;
            height = h;
        });

        File IconLocation = new File("src/main/resources" + EngineConfig.getInstance().FetchIcon());
        GLFWImage Icon = GLFWImage.malloc();
        GLFWImage.Buffer IconBuffer = GLFWImage.malloc(1);
        String IconPath = "src/main/java/net/halbear/Terrain4J/EngineCore/EngineSpecificAssets/terrain4JlogoFullBlocks.png";
        if (IconLocation.exists()){
            IconPath =IconLocation.getPath();
            Logger.debug("Loading Icon From Location [{}]",IconPath);
        } else Logger.warn("Icon specified in config at Location [{}] does not exist, resorting to Engine Icon", IconLocation.getPath());
        Icon.set(GLFWImageParser.LoadImage(IconPath).GetWidth(),
                GLFWImageParser.LoadImage(IconPath).GetHeight(),
                GLFWImageParser.LoadImage(IconPath).GetImage());
        IconBuffer.put(0,Icon);

        glfwSetWindowIcon(handle, IconBuffer);

        IconBuffer.free(); //free the buffer once icon is set so when the program closes there isn't memory still floating around
        Logger.debug("Freeing icon Buffer");
        glfwSetWindowFocusCallback(handle, new GLFWWindowFocusCallback() {
            @Override
            public void invoke(long window, boolean focused) {
                if(EngineConfig.getInstance().IsEngineThrottledOnUnfocus()) {
                    if (focused) {
                        EngineConfig.getInstance().SetEngineThrottle(false);
                    } else {
                        EngineConfig.getInstance().SetEngineThrottle(true);
                    }
                }
                PrimaryRuntime.UpdateFrameAccuracy();
            }
        });
        glfwMakeContextCurrent(handle);
        if (EngineConfig.VSYNC) glfwSwapInterval(1);
        else glfwSwapInterval(0);
        mouseInput = new MouseListener(handle);
    }
    public void cleanup() {
        glfwFreeCallbacks(handle);
        glfwDestroyWindow(handle);
        glfwTerminate();
        Logger.debug("Destroyed Window");
    }
    public long getHandle(){
        return handle;
    }
    public int getHeight(){return height;}
    public int getWidth(){return width;}
    public KeyboardInput getKeyboardInput(){
        return keyboardInput;
    }
    public MouseListener getMouseInput(){
        return mouseInput;
    }
    public void pollEvents() {
        if (keyboardInput.keySinglePress(GLFW_KEY_V)) {
            SetVsync();
        }
        keyboardInput.input();
        mouseInput.input();
        EngineConfig.RENDERFRAMES++;
    }
    public void UpdateWindowTitle(){
        if (displayFPS) glfwSetWindowTitle(handle, EngineConfig.SOFTWARE_TITLE + " - FPS: " + EngineConfig.FrameRate_PRIMARYTHREAD + "   TPS: " + EngineConfig.TPS + "   RenderFPS: " + EngineConfig.FrameRate_RENDERTHREAD + "   VSYNC: " + EngineConfig.VSYNC);
  }
    public void SetVsync(){
        Logger.info("Vsync Swap");
        if (EngineConfig.VSYNC) {
            glfwSwapInterval(0);
            EngineConfig.VSYNC = false;
        }
        else {
            glfwSwapInterval(1);
            EngineConfig.VSYNC = true;
        }
    }
    public void setShouldClose() {
        glfwSetWindowShouldClose(handle, true);
    }
    public boolean shouldClose() {
        //return false;
        return glfwWindowShouldClose(handle);
    }
}
