/*
    This file is a part of the SteamWar software.

    Copyright (C) 2020  SteamWar.de-Serverteam

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package de.steamwar.core;

import de.steamwar.Reflection;
import org.bukkit.Bukkit;

public class TPSWatcher {

    private static final double TICK_DEFAULT = 20.0;

    private static final TPSWatcher tps_OneSecond = new TPSWatcher(1000);
    private static final TPSWatcher tps_TenSecond = new TPSWatcher(10000);

    private long lastTime = System.currentTimeMillis();
    private double tps = TICK_DEFAULT;

    private TPSWatcher(long timeInterval) {
        Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
            long currentTime = System.currentTimeMillis();

            if (currentTime > lastTime)
                tps = (timeInterval / (double) (currentTime - lastTime)) * TICK_DEFAULT;

            lastTime = currentTime;
        }, timeInterval / 50, timeInterval / 50);
    }

    public static double getTPS() {
        return getTPS(TPSType.ONE_SECOND);
    }

    public static double getTPS(double limit) {
        return getTPS(TPSType.ONE_SECOND, limit);
    }

    public static double getTPSUnlimited() {
        return getTPSUnlimited(TPSType.ONE_SECOND);
    }

    public static double getTPS(TPSType tpsType) {
        return getTPS(tpsType, TICK_DEFAULT);
    }

    public static double getTPS(TPSType tpsType, double limit) {
        return Math.min(getTPSUnlimited(tpsType), limit);
    }

    public static double getTPSUnlimited(TPSType tpsType) {
        switch (tpsType) {
            case TEN_SECONDS:
                return round(tps_TenSecond.tps);
            case ONE_MINUTE:
                return round(getSpigotTPS()[0]);
            case FIVE_MINUTES:
                return round(getSpigotTPS()[1]);
            case TEN_MINUTES:
                return round(getSpigotTPS()[2]);
            case ONE_SECOND:
            default:
                return round(tps_OneSecond.tps);
        }
    }

    private static final Class<?> minecraftServer = Reflection.getClass("net.minecraft.server.MinecraftServer");
    private static final Reflection.Method getServer = Reflection.getTypedMethod(minecraftServer, "getServer", minecraftServer);
    private static final Reflection.Field<double[]> recentTps = Reflection.getField(minecraftServer, "recentTps", double[].class);
    private static double[] getSpigotTPS() {
        return recentTps.get(getServer.invoke(null));
    }

    private static double round(double d) {
        return Math.round(d * 10) / 10.0;
    }

    public enum TPSType {
        ONE_SECOND,
        TEN_SECONDS,
        ONE_MINUTE,
        FIVE_MINUTES,
        TEN_MINUTES
    }
}
