{-# LANGUAGE RankNTypes #-}
{-# 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.Backend
import Graphics.Gloss.Internals.Interface.Animate.State
import Control.Monad
import Data.IORef


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

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

        -- increment the animation time
        animate        <- stateRef `getsIORef` stateAnimate
        animateCount   <- stateRef `getsIORef` stateAnimateCount
        animateTime    <- stateRef `getsIORef` stateAnimateTime
        animateStart   <- stateRef `getsIORef` stateAnimateStart

{-      when (animateCount `mod` 5 == 0)
         $  putStr  $  "  displayTime        = " ++ show displayTime                ++ "\n"
                    ++ "  displayTimeLast    = " ++ show displayTimeLast            ++ "\n"
                    ++ "  displayTimeElapsed = " ++ show displayTimeElapsed         ++ "\n"
                    ++ "  fps                = " ++ show (truncate $ 1 / displayTimeElapsed)   ++ "\n"
-}
        when (animate && not animateStart)
         $ modifyIORef' stateRef $ \s -> s
               { stateAnimateTime       = animateTime + displayTimeElapsed }

        when animate
         $ modifyIORef' stateRef $ \s -> s
               { stateAnimateCount      = animateCount + 1
               , stateAnimateStart      = False  }



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

        -- the start of this gate
        gateTimeStart   <- elapsedTime backendRef

        -- end of the previous gate
        gateTimeEnd     <- stateRef `getsIORef` stateGateTimeEnd
        let gateTimeElapsed = gateTimeStart - gateTimeEnd

        when (gateTimeElapsed < timeClamp)
         $ do   sleep backendRef (timeClamp - gateTimeElapsed)

        gateTimeFinal   <- elapsedTime backendRef

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


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