module Graphics.Gloss.Internals.Interface.Animate
	(animateWithBackendIO)
where	
import Graphics.Gloss.Data.Color
import Graphics.Gloss.Data.Picture
import Graphics.Gloss.Internals.Render.Picture
import Graphics.Gloss.Internals.Render.ViewPort
import Graphics.Gloss.Internals.Interface.Backend
import Graphics.Gloss.Internals.Interface.Window
import Graphics.Gloss.Internals.Interface.Common.Exit
import Graphics.Gloss.Internals.Interface.ViewPort
import Graphics.Gloss.Internals.Interface.ViewPort.KeyMouse
import Graphics.Gloss.Internals.Interface.ViewPort.Motion
import Graphics.Gloss.Internals.Interface.ViewPort.Reshape
import Graphics.Gloss.Internals.Interface.Animate.Timing
import qualified Graphics.Gloss.Internals.Render.State	        		as RS
import qualified Graphics.Gloss.Internals.Interface.ViewPort.ControlState	as VPC
import qualified Graphics.Gloss.Internals.Interface.Animate.State		as AN
import qualified Graphics.Gloss.Internals.Interface.Callback			as Callback
import Data.IORef
import Control.Monad
import System.Mem
import GHC.Float (double2Float)

animateWithBackendIO
	:: Backend a
	=> a                     -- ^ Initial State of the backend
        -> Bool                  -- ^ Whether to allow the image to be panned around.
        -> Display               -- ^ Display mode.
	-> Color                 -- ^ Background color.
	-> (Float -> IO Picture) -- ^ Function to produce the next frame of animation.
                                 --     It is passed the time in seconds since the program started.
	-> IO ()

animateWithBackendIO backend pannable display backColor frameOp
 = do	
        -- 
	viewSR		<- newIORef viewPortInit
	viewControlSR	<- newIORef VPC.stateInit
	animateSR	<- newIORef AN.stateInit
        renderS_        <- RS.stateInit
	renderSR	<- newIORef renderS_

 	let displayFun backendRef = do
		-- extract the current time from the state
		timeS		<- animateSR `getsIORef` AN.stateAnimateTime

		-- call the user action to get the animation frame
		picture		<- frameOp (double2Float timeS)

		renderS		<- readIORef renderSR
		viewS		<- readIORef viewSR

		-- render the frame
		withViewPort
			backendRef
			viewS
			(renderPicture backendRef renderS viewS picture)

		-- perform GC every frame to try and avoid long pauses
		performGC

	let callbacks
	     = 	[ Callback.Display	(animateBegin animateSR)
		, Callback.Display 	displayFun
		, Callback.Display	(animateEnd   animateSR)
		, Callback.Idle		(\s -> postRedisplay s)
		, callback_exit () 
		, callback_viewPort_motion   viewSR viewControlSR 
		, callback_viewPort_reshape ]
 
             ++ (if pannable 
                  then [callback_viewPort_keyMouse viewSR viewControlSR]
                  else [])

        createWindow backend display backColor callbacks

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