package com.antonjohansson.game.time;

import java.util.OptionalInt;

import com.antonjohansson.game.config.Configuration;

/**
 * Controls the game time.
 */
public class GameTime
{
    private static final float NANO_TO_SECONDS_DIVIDER = 1_000_000_000;
    private static final float NANO_TO_MILLISECONDS_DIVIDER = 1_000_000;
    private static final int ONE_SECOND_IN_MILLISECONDS = 1_000;

    private final Configuration configuration;
    private long lastUpdate;
    private float delta;
    private float updatesPerSecondTime;
    private int updatesPerSecondCounter;
    private int updatesPerSecond;

    public GameTime(Configuration configuration)
    {
        this.configuration = configuration;
    }

    /**
     * Initializes the clock.
     */
    public void initialize()
    {
        lastUpdate = time();
    }

    private long time()
    {
        return System.nanoTime();
    }

    /**
     * Updates the clock.
     */
    public void update()
    {
        long newUpdate = time();

        delta = (newUpdate - lastUpdate) / NANO_TO_SECONDS_DIVIDER;

        updatesPerSecondTime += delta;
        if (updatesPerSecondTime >= 1.0F)
        {
            updatesPerSecondTime -= 1.0F;
            updatesPerSecond = updatesPerSecondCounter;
            updatesPerSecondCounter = 1;

            String message = "Updates per second: " + updatesPerSecond;
            if (frameCap().isPresent())
            {
                message += " (requested: " + frameCap().getAsInt() + ")";
            }
            System.out.println(message);
        }
        else
        {
            updatesPerSecondCounter++;
        }

        lastUpdate = newUpdate;
    }

    /**
     * Restrains the frame count if necessary.
     */
    public void restrainIfNecessary()
    {
        if (frameCap().isPresent())
        {
            float secondsPerFrame = 1.0F / frameCap().getAsInt();

            long estimatedTimeAfterFrame = (long) (lastUpdate / NANO_TO_MILLISECONDS_DIVIDER) + (long) (secondsPerFrame * ONE_SECOND_IN_MILLISECONDS);
            long now = (long) (time() / NANO_TO_MILLISECONDS_DIVIDER);
            long millisecondsToSleep = estimatedTimeAfterFrame - now;

            if (millisecondsToSleep > 0)
            {
                sleepQuietly(millisecondsToSleep);
            }
        }
    }

    private void sleepQuietly(long millisecondsToSleep)
    {
        try
        {
            Thread.sleep(millisecondsToSleep);
        }
        catch (InterruptedException e)
        {
        }
    }

    private OptionalInt frameCap()
    {
        return configuration.getGraphics().getFrameCap();
    }

    public float getDelta()
    {
        return delta;
    }

    public int getUpdatesPerSecond()
    {
        return updatesPerSecond;
    }
}

@@ -216,115 +216,95 @@
 nal 
-float NANO_TO_SECONDS_DIVIDER = 1_000_000_000;%0D%0A    private static final float NANO_TO_MILLISECONDS_DIVIDER
+int SECOND_IN_MILLISECONDS = 1_000;%0D%0A    private static final int SECOND_IN_NANOSECONDS
  = 1
@@ -307,24 +307,28 @@
  = 1_000_000
+_000
 ;%0D%0A    priva
@@ -347,20 +347,21 @@
 nal int 
-ONE_
+MILLI
 SECOND_I
@@ -354,37 +354,36 @@
  MILLISECOND_IN_
-MILLI
+NANO
 SECONDS = 1_000;
@@ -381,16 +381,20 @@
  = 1_000
+_000
 ;%0D%0A%0D%0A   
@@ -1087,31 +1087,37 @@
 ) / 
-NANO_TO_SECONDS_DIVIDER
+(float) SECOND_IN_NANOSECONDS
 ;%0D%0A%0D
@@ -2040,24 +2040,24 @@
 pdate / 
-NANO_TO_
+(float) 
 MILLISEC
@@ -2059,25 +2059,31 @@
 LISECOND
-S_DIVIDER
+_IN_NANOSECONDS
 ) + (lon
@@ -2108,12 +2108,8 @@
 e * 
-ONE_
 SECO
@@ -2174,16 +2174,16 @@
 ) / 
-NANO_TO_
+(float) 
 MILL
@@ -2193,17 +2193,23 @@
 COND
-S_DIVIDER
+_IN_NANOSECONDS
 );%0D%0A

Improve constant names