    {-# 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