{-# OPTIONS_HADDOCK hide #-}

-- | Handles timing of animation.
--	The main point is that we want to restrict the framerate to something
--	sensible, instead of just displaying at the machines maximum possible
--	rate and soaking up 100% cpu. 
--
--	We also keep track of the elapsed time since the start of the program,
--	so we can pass this to the user's animation function.
-- 
module Graphics.Gloss.Internals.Interface.Animate.Timing
	( animateBegin
	, animateEnd )
where
import Graphics.Gloss.Internals.Interface.Animate.State
import Control.Monad
import Control.Concurrent
import Data.IORef
import qualified Graphics.UI.GLUT			as GLUT
import Graphics.UI.GLUT					(get)


-- | Handles animation timing details.
--	Call this function at the start of each frame.
animateBegin :: IORef State -> IO ()
animateBegin stateRef
 = do
	-- write the current time into the display state
	displayTime		<- get GLUT.elapsedTime
	displayTimeLast		<- stateRef `getsIORef` stateDisplayTime
	let displayTimeElapsed	= displayTime - displayTimeLast

	stateRef `modifyIORef` \s -> s 
		{ stateDisplayTime	= displayTime 
		, stateDisplayTimeLast	= displayTimeLast }

{-	putStr 	$  "  displayTime        = " ++ show displayTime 		++ "\n"
	 	++ "  displayTimeLast    = " ++ show displayTimeLast 		++ "\n"
		++ "  displayTimeElapsed = " ++ show displayTimeElapsed 	++ "\n"
-}

	-- increment the animation time
	animate			<- stateRef `getsIORef` stateAnimate
	animateTime		<- stateRef `getsIORef` stateAnimateTime
	animateStart		<- stateRef `getsIORef` stateAnimateStart
	
	when (animate && not animateStart)
	 $ do	stateRef `modifyIORef` \s -> s
			{ stateAnimateTime	= animateTime + displayTimeElapsed }
			
	when animate
	 $ do	stateRef `modifyIORef` \s -> s
	 		{ stateAnimateStart	= False }


-- | Handles animation timing details.
--	Call this function at the end of each frame.
animateEnd :: IORef State -> IO ()
animateEnd stateRef
 = do
	-- timing gate, limits the maximum frame frequency (FPS)
	timeClamp	<- stateRef `getsIORef` stateDisplayTimeClamp

	gateTimeStart	<- get GLUT.elapsedTime				-- the start of this gate
	gateTimeEnd	<- stateRef `getsIORef` stateGateTimeEnd	-- end of the previous gate
	let gateTimeElapsed	
			= gateTimeStart - gateTimeEnd
	
	when (gateTimeElapsed < timeClamp)
	 $ do	threadDelay ((timeClamp - gateTimeElapsed) * 1000)

	gateTimeFinal	<- get GLUT.elapsedTime

	stateRef `modifyIORef` \s -> s 
		{ stateGateTimeEnd	= gateTimeFinal 
		, stateGateTimeElapsed	= gateTimeElapsed }


getsIORef :: IORef a -> (a -> r) -> IO r
getsIORef ref fun
 = liftM fun $ readIORef ref