package com.rehab.world;

import java.util.Hashtable;

import com.rehab.world.Vector2D.Point;

public class WorldLoop extends Thread {
	
	// Number of ticks per second
	private int mTarTick;
	// Desired duration in nanoseconds
	private long mTickInterval;
	
	private long mLastTickStart = 0;
	private long mLastTickDuration = 0;
	
	private long mTickLast = 0, mTickDelta = 0;
	// Whether or not to keep looping
	private boolean mLoop = true;
	
	// The Singleton's instance
	private static WorldLoop mInstance;
	
	// The level to run
	private Arena mLvl;
	
	private FrameDepot mDepot = FrameDepot.getInstance();
	
	private Hashtable<Integer, Point> locations = new Hashtable<Integer, Point>();
		
	/**
	 * Gets an instance of the WorldLoop. This method should only be used if getInstance(int, Arena)
	 * was ever called at least once. Calling this method first will throw an IllegalStateException.
	 * @throws IllegalStateException
	 * 		if getInstance(int, Arena) has never been called.
	 */
	public static WorldLoop getInstance() {
		synchronized (WorldLoop.class) {
			if (mInstance == null) throw new IllegalStateException("getInstance(int, Arena) has never been called");
			return mInstance;
		}
	}
	
	/**
	 * Gets an instance of the WorldLoop.
	 * @param tickRate
	 * 		the desired ticks per second.
	 * @param arena
	 * 		the level to simulate.
	 */
	public static WorldLoop getInstance(int tickRate, Arena arena) {
		synchronized (WorldLoop.class) {
			if (mInstance == null) mInstance = new WorldLoop(tickRate, arena);
			return mInstance;
		}
	}
	
	/**
	 * Constructor for the World's state loop.
	 * @param tickRate
	 * 		the desired ticks per second.
	 * @param arena
	 * 		the level to simulate.
	 */
	private WorldLoop(int tickRate, Arena arena) {
		mLvl = arena;
		mLvl.setDeltaTime(1d / tickRate);
		// Measure time needed
		mTarTick = tickRate;
		mTickInterval = 1000000000 / mTarTick;
	}
	
	/**
	 * The set desired ticks per second.
	 * @return
	 * 		the number of ticks per second.
	 */
	public int getTickRate() { return mTarTick; }
	
	public void halt() {
		mLoop = false;
	}
	
	@Override
	public void run() {
		super.run();
		
		mLastTickStart = System.nanoTime();
		
		while (mLoop) {
			mLastTickDuration = System.nanoTime() - mLastTickStart;
			mLastTickStart = System.nanoTime();
			
			// Run physics for 1 unit
			while (mLastTickDuration > mTickInterval) {
				mLvl.stepActors();
				mLvl.stepProjectiles();
				mLastTickDuration -= mTickInterval;
			}
			
			// Send draw requests to update screen
			InstanceManager manager = InstanceManager.getInstance();
			Iterable<Actor> acts = manager.getLoadedActors();
			Iterable<Projectile> projs = manager.getLoadedProjectiles();
			Iterable<Prop> props = manager.getLoadedProps();
			
			Frame frame = mDepot.requestFrame();
			// Send draw requests for all visible game objs
			for (Actor a : acts) {
				if (a.isVisible()) {
					frame.push(a);
				}
			}
			for (Projectile p : projs) {
				if (p.isVisible()) {
					frame.push(p);
				}
			}
			for (Prop p : props) {
				if (p.isVisible()) {
					frame.push(p);
				}
			}
			ProtoRender.getInstance().requestDraw(frame);
			
			
			long currentDuration = System.nanoTime() - mLastTickStart;
			if (currentDuration < mTickInterval) {
				try {
					Thread.sleep((mTickInterval - currentDuration) / 1000000);
				} catch (InterruptedException e) { e.printStackTrace(); }
			} else if (currentDuration > mTickInterval) {
				try {
					Thread.sleep((currentDuration - mTickInterval) / 1000000);
				} catch (InterruptedException e) { e.printStackTrace(); }
			}
		}
		
	}
	
}

@@ -2329,59 +2329,71 @@
 for 
-1 unit%0A%09%09%09while (mLastTickDuration %3E mTickInterval)
+at least 1 unit (+ more based on previous frame duration)%0A%09%09%09do
  %7B%0A%09
@@ -2482,20 +2482,63 @@
 terval;%0A
-
 %09%09%09%7D
+ while (mLastTickDuration %3E mTickInterval);
 %0A%09%09%09%0A%09%09%09

Fix for loop skipping many game steps