package net.halbear.Terrain4J.EngineCore.Threads;

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.Main.PrimaryRuntime;
import org.tinylog.Logger;

public class EngineThread implements Runnable{
    public boolean CappedToTickRate;
    public int TickRate;
    public double TickThreshold = 0;
    public static boolean running = true;
    public final GameLogic gameLogic;
    public int Frames = 0;
    public int TickFrames = 0;
    public final Thread EngineThread;
    public long EngineAccuracy;
    public long OriginalAccuracy;
    public boolean Throttle;
    public String ThreadName;

    public void KillThread(){
        running = false;
    }

    public void cleanup(){
        gameLogic.cleanup();
        PrimaryRuntime.ThreadsOpen--;
        Logger.debug("Closing "+ThreadName+" Thread, Thread: [{}]",Thread.currentThread().toString());
    }

    public Thread FetchThread(){
        return EngineThread;
    }

    public void StartThread(){
        EngineThread.start();
    }

    public EngineThread(EngineInstance engineInstance, GameLogic appLogic, String ThreadName){
        this.ThreadName = ThreadName;
        this.gameLogic = appLogic;
        Logger.debug("Starting {} Thread",ThreadName);
        EngineThread = new Thread(this);
        EngineThread.setName("Terrain4J "+ThreadName+" Engine Thread");
    }

    public void UpdateFrameAccuracy(){
        Throttle = EngineConfig.getInstance().IsEngineThrottled();
        if(Throttle){
            EngineAccuracy = EngineConfig.getInstance().GetThrottleAccuracy();
        } else{
            EngineAccuracy= OriginalAccuracy;
        }
    }

    public void TickEvent(long nsTimeDiff){

    }
    public void FrameEvent(long now, long InitialTime){

    }
    public void CycleEvent(){

    }
    public void SecondEvent(){

    }

    @Override
    public void run() {
        PrimaryRuntime.ThreadsOpen++;
        long InitialTime = System.nanoTime();
        long updateTime = InitialTime;
        double[] FrameTimes = new double[]{0,0,0,0};
        Logger.debug("{} Thread Started",ThreadName);
        EngineAccuracy = EngineConfig.getInstance().getAccuracy();
        OriginalAccuracy = EngineAccuracy;
        while (running){
            long now = System.nanoTime();
            FrameTimes[0] = now - InitialTime;
            FrameTimes[1] += FrameTimes[0];
            FrameTimes[2] += FrameTimes[0];
            FrameTimes[3] += FrameTimes[0];
            if (FrameTimes[1] >= EngineAccuracy){
                FrameEvent(now, InitialTime);
                Frames++;
                if (FrameTimes[2] >= TickThreshold) {
                    TickFrames++;
                    long nsTimeDiff = now - updateTime;
                    TickEvent(nsTimeDiff);
                    updateTime = now;
                    FrameTimes[2] = 0;
                }
                if (FrameTimes[3] >= 1000000000L){
                    FrameTimes[3] = 0;
                    SecondEvent();
                    Frames = 0;
                    TickFrames = 0;
                }
                FrameTimes[1] = 0;
            }
            if(Throttle) {
                long endTime = System.nanoTime();
                long LoopFrameDiff = endTime - now;
                long sleepTime = ((EngineAccuracy - LoopFrameDiff) / 1000000);

                if (sleepTime > 0) {
                    try {
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        Logger.error("Thread Sleep Exception [{}]", e);
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
            InitialTime = now;
        }
        cleanup();
    }
}
