-----------------------------------------------------------------------------
-- |
-- Module      :  Graphics.HGL.Window
-- Copyright   :  (c) Alastair Reid, 1999-2003
-- License     :  BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer  :  libraries@haskell.org
-- Stability   :  provisional
-- Portability :  non-portable (requires concurrency)
--
-- Windows in a simple graphics library.
--
-----------------------------------------------------------------------------

module Graphics.HGL.Window
	(
	-- * Windows
	  Window
	, Title			-- = String
	, RedrawMode(Unbuffered, DoubleBuffered)
	, openWindowEx		-- :: Title -> Maybe Point -> Maybe Size ->
				--    RedrawMode -> Maybe Time -> IO Window
	, getWindowRect		-- :: Window -> IO (Point,Point)
	, closeWindow		-- :: Window -> IO ()

	-- * Drawing in a window
	, setGraphic		-- :: Window -> Graphic -> IO ()
	, getGraphic		-- :: Window -> IO Graphic
	, modGraphic		-- :: Window -> (Graphic -> Graphic) -> IO ()
	, directDraw		-- :: Window -> Graphic -> IO ()
	-- not in X11: , redrawWindow		-- :: Window -> IO ()

	-- * Events in a window
	, Event(..)
	-- , Event(Char,Key,Button,MouseMove,Resize,Closed) -- deriving(Show)
	-- , char		-- :: Event -> Char
	-- , keysym		-- :: Event -> Key
	-- , isDown		-- :: Event -> Bool
	-- , pt			-- :: Event -> Point
	-- , isLeft		-- :: Event -> Bool
	, getWindowEvent	-- :: Window -> IO Event
	, maybeGetWindowEvent	-- :: Window -> IO (Maybe Event)

	-- * Timer ticks
	-- | Timers that tick at regular intervals are set up by 'openWindowEx'.
	, getWindowTick		-- :: Window -> IO ()
	, getTime		-- :: IO Time
	) where

#ifdef __HADDOCK__
import Graphics.HGL.Key
#endif
import Graphics.HGL.Units
import Graphics.HGL.Draw( Graphic )
import Graphics.HGL.Internals.Event( Event(..) )
import Graphics.HGL.Internals.Types( Title, RedrawMode(..), getTime )
import qualified Graphics.HGL.Internals.Events as E
import Graphics.HGL.Internals.Utilities( modMVar, modMVar_ )
#if !X_DISPLAY_MISSING
import Graphics.HGL.X11.Window (Window(..))
import qualified Graphics.HGL.X11.Window as X (openWindowEx, closeWindow,
	redrawWindow, directDraw, getWindowRect )
#else
import Graphics.HGL.Win32.WND
	(WND, openWND, getHWND, closeWND, wndRect, redrawWND, drawWND)
import Graphics.HGL.Win32.Types
import Graphics.HGL.Win32.Draw( 
	drawGraphic, drawBufferedGraphic 
	)
import Graphics.HGL.Draw	(Draw)
-- import Graphics.HGL.Internals.Types

import qualified Graphics.Win32 as Win32
#endif

import Control.Concurrent.MVar

----------------------------------------------------------------
-- Interface
----------------------------------------------------------------

-- | Wait for the next event on the given window.
getWindowEvent      :: Window -> IO Event

-- | Check for a pending event on the given window.
maybeGetWindowEvent :: Window -> IO (Maybe Event)

-- | Wait for the next tick event from the timer on the given window.
getWindowTick       :: Window -> IO ()

-- | Get the current drawing in a window.
getGraphic :: Window -> IO Graphic

-- | Set the current drawing in a window.
setGraphic :: Window -> Graphic -> IO ()

-- | Update the drawing for a window.
-- Note that this does not force a redraw.
modGraphic :: Window -> (Graphic -> Graphic) -> IO ()

-- | General window creation.
openWindowEx
  :: Title		-- ^ title of the window
  -> Maybe Point	-- ^ the optional initial position of a window
  -> Size		-- ^ initial size of the window
  -> RedrawMode		-- ^ how to display a graphic on the window
  -> Maybe Time		-- ^ the time between ticks (in milliseconds) of an
			-- optional timer associated with the window
  -> IO Window

-- | Close the window.
closeWindow   :: Window -> IO ()

redrawWindow  :: Window -> IO ()

directDraw    :: Window -> Graphic -> IO ()

-- | The position of the top left corner of the window on the screen,
-- and the size of the window.
getWindowRect :: Window -> IO (Point, Size)

----------------------------------------------------------------
-- Implementation
----------------------------------------------------------------

getWindowEvent w     = E.getEvent (events w)

maybeGetWindowEvent w
  = do noEvent <- E.isNoEvent (events w)
       if noEvent 
          then return Nothing
          else do ev <- getWindowEvent w
                  return (Just ev)

getWindowTick w      = E.getTick (events w)

getGraphic w = readMVar (graphic w)

setGraphic w p = do
  modMVar (graphic w) (const p)
  redrawWindow w

modGraphic w = modMVar_ (graphic w)

#if !X_DISPLAY_MISSING

openWindowEx    = X.openWindowEx
closeWindow     = X.closeWindow
getWindowRect   = X.getWindowRect
redrawWindow    = X.redrawWindow
directDraw      = X.directDraw

#else /* X_DISPLAY_MISSING */

data Window = MkWindow { 
	events  :: E.Events,	    -- the event stream
	graphic :: MVar (Draw ()),  -- the current graphic
	wnd     :: WND	    	    -- the real window
	}

openWindowEx name pos size redrawMode tickRate = do
	graphic <- newMVar (return ())
	events  <- E.newEvents
	let draw = \ hwnd hdc -> do
                      p <- readMVar graphic 
		      repaint p hwnd hdc
	wnd     <- openWND name (fmap fromPoint pos) (Just $ fromPoint size) 
			   events draw (fmap fromInteger tickRate)
	mkWindow wnd events graphic
 where
  repaint = case redrawMode of
            Unbuffered     -> drawGraphic
            DoubleBuffered -> drawBufferedGraphic

mkWindow       :: WND -> E.Events -> MVar (Draw ()) -> IO Window
mkWindow wnd events graphic = do
	return (MkWindow { wnd=wnd, events=events, graphic=graphic })

closeWindow w   = closeWND  (wnd w)
getWindowRect w = wndRect   (wnd w)
redrawWindow w  = redrawWND (wnd w)
directDraw w p  = drawWND   (wnd w) p

-- in case you need low level access
windowHWND     :: Window -> IO Win32.HWND
windowHWND w    = getHWND   (wnd w)

#endif /* X_DISPLAY_MISSING */

----------------------------------------------------------------
-- End
----------------------------------------------------------------