package net.halbear.Terrain4J.EngineCore.Logic;

import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import org.tinylog.Logger;

import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;

public class EngineConfig {
    public static final double DegToRad = Math.PI/180.0;
    public static final double RadToDeg = 180.0/Math.PI;
    private static final long DEFAULT_ACCURACY = 0;
    private static final int DEFAULT_TICKRATE = 60;
    private static final int DEFAULT_WIDTH = 640;
    private static final int DEFAULT_HEIGHT = 480;
    private static final String FILENAME = "terrain4j.properties";
    public static String SOFTWARE_TITLE;
    private static EngineConfig instance;
    private long accuracy;
    private int TickRate;
    private int MainTickRate;
    private int FastTickRate;
    private boolean CapMainToTickRate;
    private boolean CapFastToTickRate;
    private boolean CapPrimaryToTickRate;
    private int Width;
    private int Height;
    public static int FrameRate_PRIMARYTHREAD =0;
    public static int TPS =0;
    public static int FrameRate_MAINENGINETHREAD =0;
    public static int FrameRate_FASTENGINETHREAD =0;
    public static int FrameRate_RENDERTHREAD =0;
    public static int FrameRate_WINDOW =0;
    public static int RENDERFRAMES =0;
    public static boolean VSYNC = false;
    public static boolean ShaderDebug = false;
    public static boolean RecompileShaders = false;
    private static long ThrottleAccuracy = 100000;
    private static boolean ThrottleEngine = false;
    private static boolean ThrottleEngineOnUnfocus = true;
    private boolean vkValidated;
    private String PhysicalDeviceName;
    private int RequestedImages;
    private String IconPath = "/WindowResources/Icon/";
    private String DefaultTexturePath = "resources/EngineResources/Texture/DefaultTexture.png";
    private String IconName = "ProgramIcon.png";
    private float FOV = 60f;
    private float zFarPlane;
    private float zNearPlane;
    private boolean GPU_SUPPORTS_LOGGING = true;
    private int MaxDescriptors = 1000;

    public void GPU_Does_Not_Support_Logging(){
        GPU_SUPPORTS_LOGGING = false;
    }
    public boolean doesGPUSupportLogging(){return GPU_SUPPORTS_LOGGING;}
    public String GetDefaultTexturePath(){return DefaultTexturePath;}
    public int GetMaxDescriptors(){return MaxDescriptors;}

    private EngineConfig() {
        var EngineConfigVar = new Properties();
        Path path = Paths.get("");
        File ConfigFile = new File(path.toAbsolutePath().toString() + "/" + FILENAME);
        boolean SuccessfulLoad = false;
        try(InputStream stream = new FileInputStream(path.toAbsolutePath().toString() + "/" + FILENAME)){
            try {
                EngineConfigVar.load(stream);
                SuccessfulLoad = true;
                Logger.debug("File [{}] read", FILENAME);
            } catch (IOException excp) {
                Logger.error("File [{}] not read", FILENAME, excp);
            }
            try {
                stream.close();
            } catch(IOException excp) {
                Logger.error("stream unable to be closed", excp);
            }
        } catch(IOException | NullPointerException error){
            Logger.error("FileStream for file [{}] is null, Error: [{}]", ConfigFile.getAbsolutePath(),error);
        }
        if (!SuccessfulLoad){
            InputStream NewStream = (EngineConfig.class.getResourceAsStream("DefaultEngineConfig.properties"));
            try {
                EngineConfigVar.load(NewStream);
                SuccessfulLoad = true;
            } catch (IOException error){
                Logger.error("FileStream for file [{}] is null", "DefaultEngineConfig.properties",error);
            }
            try {
                assert NewStream != null;
                NewStream.close();
            } catch (IOException error){
                Logger.error("FileStream Failed to close",error);
            }
        }
        if(SuccessfulLoad) {
            IconName = (EngineConfigVar.getOrDefault("Software_Icon", "ProgramIcon.png").toString());
            IconPath = (EngineConfigVar.getOrDefault("Software_Icon_Path", "/WindowResources/Icon/").toString());
            CapMainToTickRate = Boolean.parseBoolean(EngineConfigVar.getOrDefault("cap_main_to_tickrate", true).toString());
            CapFastToTickRate = Boolean.parseBoolean(EngineConfigVar.getOrDefault("cap_fast_to_tickrate", true).toString());
            CapPrimaryToTickRate = Boolean.parseBoolean(EngineConfigVar.getOrDefault("cap_master_to_tickrate", false).toString());
            MainTickRate = Integer.parseInt(EngineConfigVar.getOrDefault("main_thread_tickrate", "120").toString());
            FastTickRate = Integer.parseInt(EngineConfigVar.getOrDefault("fast_thread_tickrate", "240").toString());
            SOFTWARE_TITLE = EngineConfigVar.getOrDefault("Software_Title", "Terrain4J").toString();
            PhysicalDeviceName = EngineConfigVar.getOrDefault("PhysicalDeviceName", "").toString();
            accuracy = Long.parseLong(EngineConfigVar.getOrDefault("frame_accuracy", DEFAULT_ACCURACY).toString());
            ThrottleAccuracy = Long.parseLong(EngineConfigVar.getOrDefault("throttle_accuracy", DEFAULT_ACCURACY).toString());
            ThrottleEngineOnUnfocus = Boolean.parseBoolean(EngineConfigVar.getOrDefault("throttle_on_unfocus", true).toString());
            TickRate = Integer.parseInt(EngineConfigVar.getOrDefault("master_tick_rate", DEFAULT_TICKRATE).toString());
            Width = Integer.parseInt(EngineConfigVar.getOrDefault("display_width", "1280").toString());
            Height = Integer.parseInt(EngineConfigVar.getOrDefault("display_height", "720").toString());
            MaxDescriptors = Integer.parseInt(EngineConfigVar.getOrDefault("MaxDescriptors", "1000").toString());
            VSYNC = Boolean.parseBoolean(EngineConfigVar.getOrDefault("vsync", true).toString());
            vkValidated = Boolean.parseBoolean(EngineConfigVar.getOrDefault("vkValidated", false).toString());
            RequestedImages = Integer.parseInt(EngineConfigVar.getOrDefault("RequestedImages", 3).toString());
            ShaderDebug =Boolean.parseBoolean(EngineConfigVar.getOrDefault("Debug_Shaders", false).toString());
            RecompileShaders =Boolean.parseBoolean(EngineConfigVar.getOrDefault("ShaderRecompiling", true).toString());
            FOV = (float)(DegToRad * Float.parseFloat(EngineConfigVar.getOrDefault("field_of_view", 60.0f).toString()));
            zNearPlane = (Float.parseFloat(EngineConfigVar.getOrDefault("z_near_plane", 1.0f).toString()));
            zFarPlane = (Float.parseFloat(EngineConfigVar.getOrDefault("z_far_plane", 100.0f).toString()));
            DefaultTexturePath = EngineConfigVar.getOrDefault("DefaultTexturePath","/EngineResources/Texture/DefaultTexture.png").toString();
            Logger.debug("\n\nsuccessfully loaded configuration: \n{}\n\n",EngineConfigVar.toString());
        }
        else if(!ConfigFile.exists()){
            try {
                EngineConfigVar.setProperty("Software_Icon","ProgramIcon.png");
                EngineConfigVar.setProperty("Software_Icon_Path","/WindowResources/Icon/");
                EngineConfigVar.setProperty("cap_master_to_tickrate","false");
                EngineConfigVar.setProperty("cap_main_to_tickrate","true");
                EngineConfigVar.setProperty("cap_fast_to_tickrate","true");
                EngineConfigVar.setProperty("main_thread_tickrate","120");
                EngineConfigVar.setProperty("fast_thread_tickrate","240");
                EngineConfigVar.setProperty("Software_Title","Terrain4J Software");
                EngineConfigVar.setProperty("PhysicalDeviceName",null);
                EngineConfigVar.setProperty("frame_accuracy", String.valueOf(DEFAULT_ACCURACY));
                EngineConfigVar.setProperty("master_tick_rate", String.valueOf(DEFAULT_TICKRATE));
                EngineConfigVar.setProperty("display_width","1280");
                EngineConfigVar.setProperty("display_height","720");
                EngineConfigVar.setProperty("vsync","true");
                EngineConfigVar.setProperty("vkValidated","false");
                EngineConfigVar.setProperty("Debug_Shaders","false");
                EngineConfigVar.setProperty("ShaderRecompiling","true");
                EngineConfigVar.setProperty("throttle_accuracy","100000");
                EngineConfigVar.setProperty("throttle_on_unfocus","true");
                EngineConfigVar.setProperty("field_of_view","60");
                EngineConfigVar.setProperty("z_near_plane","1");
                EngineConfigVar.setProperty("z_far_plane","100");
                EngineConfigVar.setProperty("DefaultTexturePath",DefaultTexturePath);
                EngineConfigVar.setProperty("MaxDescriptors","1000");
                EngineConfigVar.store(new FileWriter(path.toAbsolutePath().toString() + "/" + FILENAME), "created new properties file");
                Logger.debug("Wrote New Config File [{}]", ConfigFile.getAbsolutePath());
            } catch (IOException excp2) {
                Logger.error("File [{}] not written", FILENAME, excp2);
            }
        } else {
            Logger.debug("Config File [{}] exists but was unable to be loaded", ConfigFile.getAbsolutePath());
        }
    }



    public static synchronized EngineConfig getInstance() {
        if (instance == null) {instance = new EngineConfig();}
        return instance;
    }

    public void SetEngineThrottle(boolean isThrottling){ThrottleEngine = isThrottling; }
    public boolean IsEngineThrottled(){return ThrottleEngine;}
    public boolean IsEngineThrottledOnUnfocus(){return ThrottleEngineOnUnfocus;}
    public long GetThrottleAccuracy(){return ThrottleAccuracy;}
    public int GetRequestedImages(){
        return RequestedImages;
    }
    public boolean GetVSYNC(){
        return VSYNC;
    }
    public boolean DebugShaders(){
        return ShaderDebug;
    }
    public boolean RecompileShaders(){
        return RecompileShaders;
    }
    public float GetFOV(){return FOV;}
    public float GetZFarPlane(){return zFarPlane;}
    public float GetZNearPlane(){return zNearPlane;}
    public boolean VkValidated(){
        return vkValidated;
    }
    public int getWidth(){
        return Width;
    }
    public int getHeight(){
        return Height;
    }
    public boolean GetMainTickCap(){return CapMainToTickRate;}
    public boolean GetFastTickCap(){return CapFastToTickRate;}
    public boolean GetPrimaryTickCap(){return CapPrimaryToTickRate;}
    public int GetMainTickrate(){return MainTickRate;}
    public int GetFastTickrate(){return FastTickRate;}
    public long getAccuracy() {
        return accuracy;
    }
    public int getTickRate() {
        return TickRate;
    }
    public String GetPhysicalDeviceName(){
        return PhysicalDeviceName;
    }
    public String GetSoftwareName(){
        return SOFTWARE_TITLE;
    }
    public String FetchIcon(){
        return IconPath + IconName;
    }
}
