content: {-# OPTIONS_HADDOCK hide #-}
  {-# LANGUAGE RankNTypes #-}
  
  module Graphics.Gloss.Internals.Interface.Simulate.Idle
          ( callback_simulate_idle )
  where
  import Graphics.Gloss.Data.ViewPort
  import Graphics.Gloss.Internals.Interface.Callback
  import qualified Graphics.Gloss.Internals.Interface.Backend             as Backend
  import qualified Graphics.Gloss.Internals.Interface.Animate.State       as AN
  import qualified Graphics.Gloss.Internals.Interface.Simulate.State      as SM
  import Data.IORef
  import Control.Monad
  import GHC.Float (double2Float)
  
  
  -- | The graphics library calls back on this function when it's finished drawing
  --      and it's time to do some computation.
  callback_simulate_idle
          :: IORef SM.State                               -- ^ the simulation state
          -> IORef AN.State                               -- ^ the animation statea
          -> IO ViewPort
          -- ^ action to get the 'ViewPort'.  We don't use an 'IORef'
          -- directly because sometimes we hold a ref to a 'ViewPort' (in
          -- Game) and sometimes a ref to a 'ViewState'.
          -> IORef world                                  -- ^ the current world
          -> (ViewPort -> Float -> world -> IO world)     -- ^ fn to advance the world
          -> Float                                        -- ^ how much time to advance world by
                                                          --      in single step mode
          -> IdleCallback
  
  callback_simulate_idle simSR animateSR viewSA worldSR worldAdvance _singleStepTime backendRef
   = {-# SCC "callbackIdle" #-}
     do   simulate_run simSR animateSR viewSA worldSR worldAdvance backendRef
  
  
  -- take the number of steps specified by controlWarp
  simulate_run
          :: IORef SM.State
          -> IORef AN.State
          -> IO ViewPort
          -> IORef world
          -> (ViewPort -> Float -> world -> IO world)
          -> IdleCallback
  
  simulate_run simSR _ viewSA worldSR worldAdvance backendRef
   = do   viewS           <- viewSA
          simS            <- readIORef simSR
          worldS          <- readIORef worldSR
  
          -- get the elapsed time since the start simulation (wall clock)
          elapsedTime     <- fmap double2Float $ Backend.elapsedTime backendRef
  
          -- get how far along the simulation is
          simTime                 <- simSR `getsIORef` SM.stateSimTime
  
          -- we want to simulate this much extra time to bring the simulation
          --      up to the wall clock.
          let thisTime    = elapsedTime - simTime
  
          -- work out how many steps of simulation this equals
          resolution      <- simSR `getsIORef` SM.stateResolution
          let timePerStep = 1 / fromIntegral resolution
          let thisSteps_  = truncate $ fromIntegral resolution * thisTime
          let thisSteps   = if thisSteps_ < 0 then 0 else thisSteps_
  
          let newSimTime  = simTime + fromIntegral thisSteps * timePerStep
  
  {-      putStr  $  "elapsed time    = " ++ show elapsedTime     ++ "\n"
                  ++ "sim time        = " ++ show simTime         ++ "\n"
                  ++ "this time       = " ++ show thisTime        ++ "\n"
                  ++ "this steps      = " ++ show thisSteps       ++ "\n"
                  ++ "new sim time    = " ++ show newSimTime      ++ "\n"
                  ++ "taking          = " ++ show thisSteps       ++ "\n\n"
  -}
          -- work out the final step number for this display cycle
          let nStart      = SM.stateIteration simS
          let nFinal      = nStart + thisSteps
  
          -- keep advancing the world until we get to the final iteration number
          (_,world')
           <- untilM (\(n, _)        -> n >= nFinal)
                     (\(n, w)        -> liftM (\w' -> (n+1,w')) ( worldAdvance viewS timePerStep w))
                     (nStart, worldS)
  
          -- write the world back into its IORef
          -- We need to seq on the world to avoid space leaks when the window is not showing.
          world' `seq` writeIORef worldSR world'
  
          -- update the control state
          modifyIORef' simSR $ \c -> c
                  { SM.stateIteration     = nFinal
                  , SM.stateSimTime       = newSimTime }
  
          -- tell glut we want to draw the window after returning
          Backend.postRedisplay backendRef
  
  
  getsIORef :: IORef a -> (a -> r) -> IO r
  getsIORef ref fun
   = liftM fun $ readIORef ref
  
  untilM :: (Monad m) => (a -> Bool) -> (a -> m a) -> a -> m a
  untilM test op i = go i
    where
    go x | test x    = return x
         | otherwise = op x >>= go
  
  