{-# OPTIONS_HADDOCK hide #-}

-- | 	The main display function.
module	Graphics.Gloss.Internals.Interface.Window
	( createWindow )
where
import Graphics.Gloss.Data.Color
import Graphics.Gloss.Internals.Color
import Graphics.Gloss.Internals.Interface.Debug
import Graphics.Gloss.Internals.Interface.Callback		(Callback)
import qualified Graphics.Gloss.Internals.Interface.Callback	as Callback

import Graphics.UI.GLUT					(($=), get)
import qualified Graphics.Rendering.OpenGL.GL		as GL
import qualified Graphics.UI.GLUT			as GLUT

import Control.Monad

-- | Open a window and use the supplied callbacks to handle window events.
createWindow	
	:: String 		-- ^ Name of the window.
	-> (Int, Int) 		-- ^ Initial size of the window, in pixels.
	-> (Int, Int)		-- ^ Initial position of the window, in pixels relative to 
				--	the top left corner of the screen.
	-> Color		-- ^ Color to use when clearing.
	-> [Callback]		-- ^ Callbacks to use
	-> IO ()

createWindow
	windowName
	(sizeX, sizeY) 
	(posX,  posY)
	clearColor
	callbacks
 = do
	-- Turn this on to spew debugging info to stdout
	let debug	= False

	-- Initialize GLUT
 	(_progName, _args)	<- GLUT.getArgsAndInitialize
	glutVersion		<- get GLUT.glutVersion

	when debug
	 $ do 	putStr	$ "* displayInWindow\n"
	 	putStr	$ "  glutVersion        = " ++ show glutVersion		++ "\n"

	-- Setup and create a new window.
	--	Be sure to set initialWindow{Position,Size} before calling
	--	createWindow. If we don't do this we get wierd half-created
	--	windows some of the time.
	--
	GLUT.initialWindowPosition	
	 $= 	GL.Position		
	 		(fromIntegral posX)
			(fromIntegral posY)

	GLUT.initialWindowSize	
	 $= 	GL.Size 
			(fromIntegral sizeX) 
			(fromIntegral sizeY)

	GLUT.initialDisplayMode
	 $= 	[ GLUT.RGBMode
		, GLUT.DoubleBuffered]

	-- See if our requested display mode is possible
	displayMode		<- get GLUT.initialDisplayMode
	displayModePossible	<- get GLUT.displayModePossible
	when debug
	 $ do	putStr	$  "  displayMode        = " ++ show displayMode ++ "\n"
	 		++ "       possible      = " ++ show displayModePossible ++ "\n"
			++ "\n"
		
	-- Here we go!
	when debug
	 $ do	putStr	$ "* creating window\n\n"

	_ <- GLUT.createWindow windowName
	GLUT.windowSize	
	 $= 	GL.Size 
			(fromIntegral sizeX)
			(fromIntegral sizeY)
	
	-- Setup callbacks
	GLUT.displayCallback		$= callbackDisplay clearColor callbacks
	GLUT.reshapeCallback		$= Just (callbackReshape  	callbacks)
	GLUT.keyboardMouseCallback	$= Just (callbackKeyMouse 	callbacks)
	GLUT.motionCallback		$= Just (callbackMotion   	callbacks)
	GLUT.idleCallback		$= Just (callbackIdle 		callbacks)

	--  Switch some things.
	--  auto repeat interferes with key up / key down checks.
	--	BUGS: this doesn't seem to work?
	GLUT.perWindowKeyRepeat		$= GLUT.PerWindowKeyRepeatOff	

	-- we don't need the depth buffer for 2d.
	GL.depthFunc	$= Just GL.Always

	-- always clear the buffer to white
	GL.clearColor	$= glColor4OfColor clearColor

	-- Dump some debugging info
	when debug
	 $ do	dumpGlutState
	 	dumpFramebufferState
		dumpFragmentState

	---------------
	-- Call the GLUT mainloop. 
	--	This function will return when something calls GLUT.leaveMainLoop
	--
	--	We can ask for this in freeglut, but it doesn't seem to work :(.
	--	GLUT.actionOnWindowClose	$= GLUT.MainLoopReturns
	when debug
	 $ do	putStr	$ "* entering mainloop..\n"
	
	GLUT.mainLoop

	when debug
	 $	putStr	$ "* all done\n"
	 
	return ()


callbackDisplay :: t -> [Callback] -> IO ()
callbackDisplay _ callbacks
 = do
	-- clear the display
	GL.clear [GL.ColorBuffer, GL.DepthBuffer]
	GL.color $ GL.Color4 0 0 0 (1 :: GL.GLfloat)

	-- get the display callbacks from the chain
	let funs	= [f	| (Callback.Display f) <- callbacks]
	sequence_ funs

	-- swap front and back buffers
	GLUT.swapBuffers
	GLUT.reportErrors 
 	return ()


callbackReshape :: [Callback] -> GLUT.Size -> IO ()
callbackReshape callbacks size
 	= sequence_
	$ map 	(\f -> f size)
		[f | Callback.Reshape f 	<- callbacks]


callbackKeyMouse
	:: [Callback]
	-> GLUT.Key
	-> GLUT.KeyState
	-> GLUT.Modifiers
	-> GLUT.Position
	-> IO ()

callbackKeyMouse callbacks key keystate modifiers pos
 	= sequence_ 
	$ map 	(\f -> f key keystate modifiers pos) 
		[f | Callback.KeyMouse f 	<- callbacks]


callbackMotion
	:: [Callback]
	-> GLUT.Position
	-> IO ()

callbackMotion callbacks pos
 	= sequence_
	$ map	(\f -> f pos)
		[f | Callback.Motion f 		<- callbacks]


callbackIdle
	:: [Callback]
	-> IO ()

callbackIdle callbacks
 	= sequence_
	$ [f | Callback.Idle f 			<- callbacks]