module Graphics.UI.WX.Menu
    ( 
      
      MenuBar, Menu, menuBar, menuPopup, menuPane, menuHelp
    
    , menu, menuId
      
    , MenuItem, menuItem, menuQuit, menuAbout, menuItemEx
    , menuLine, menuSub, menuRadioItem
    
    , ToolBar, toolBar, toolBarEx
    , ToolBarItem, toolMenu, toolItem, toolControl, tool
    
    , StatusField, statusBar, statusField, statusWidth
    
    , menuList, menubar, statusbar
    ) where
import Data.Char( toUpper )
import Data.List( partition, intersperse )
import System.IO.Unsafe (unsafePerformIO)
import Foreign.Ptr( nullPtr )
import Graphics.UI.WXCore hiding (Event)
import Graphics.UI.WX.Types
import Graphics.UI.WX.Attributes
import Graphics.UI.WX.Layout
import Graphics.UI.WX.Classes
import Graphics.UI.WX.Events
menubar :: WriteAttr (Frame a) [Menu ()]
menubar
  = menuBar
menuBar :: WriteAttr (Frame a) [Menu ()]
menuBar
  = writeAttr "menubar" setter
  where
    setter frame menus
      = do mb <- menuBarCreate wxMB_DOCKABLE
           mapM_ (append mb) menus
           frameSetMenuBar frame mb
           
           mapM_ (evtHandlerSetAndResetMenuCommands frame) menus
           
           vis <- windowIsShown frame
           if (vis && wxToolkit == WxMac && (div wxVersion 100) >= 25)
            then do windowHide frame
                    windowShow frame
                    return ()
            else return ()
    append mb menu
      = do title <- menuGetTitle menu
           menuSetTitle menu ""
           menuBarAppend mb menu title
menuPopup :: Menu b -> Point -> Window a -> IO ()
menuPopup menu pt parent
  = do windowPopupMenu parent menu pt
       return ()
menuList :: [Prop (Menu ())] -> IO (Menu ())
menuList 
  = menuPane 
menuPane :: [Prop (Menu ())] -> IO (Menu ())
menuPane props
  = do m <- menuCreate "" wxMENU_TEAROFF
       set m props
       return m
menuHelp :: [Prop (Menu ())] -> IO (Menu ())
menuHelp props
  = menuPane ([text := "&Help"] ++ props)
instance Textual (Menu a) where
  text
    = newAttr "text" menuGetTitle menuSetTitle
menuSub :: Menu b -> Menu a -> [Prop (MenuItem ())] -> IO (MenuItem ())
menuSub parent menu props
  = do id <- idCreate
       label <- case (findProperty text "" props) of 
                  Just (txt,_) -> return txt
                  Nothing      -> do title <- menuGetTitle menu
                                     if (null title) 
                                      then return "<empty>"
                                      else return title                  
       menuSetTitle menu ""           
       menuAppendSub parent id label menu ""
       menuPropagateEvtHandlers menu  
       item <- menuFindItem parent id nullPtr
       set item props
       return item
menuLine :: Menu a -> IO ()
menuLine menu
  = menuAppendSeparator menu
menuItem :: Menu a -> [Prop (MenuItem ())] -> IO (MenuItem ())
menuItem menu props
  = do let kind = case (findProperty checkable False props) of
                    Just (True,_) -> wxITEM_CHECK
                    _             -> wxITEM_NORMAL
       menuItemKind menu kind props                     
menuRadioItem :: Menu a -> [Prop (MenuItem ())] -> IO (MenuItem ())
menuRadioItem menu props
  = menuItemKind menu wxITEM_RADIO ([checked := True] ++ props)
menuItemKind menu kind props
  = do id <- idCreate
       let label = case (findProperty text "" props) of 
                     Nothing      -> "<empty>"
                     Just (txt,_) -> txt
       menuItemEx menu id label kind props
       
menuAbout :: Menu a -> [Prop (MenuItem ())] -> IO (MenuItem ())
menuAbout menu props
  = menuItemId menu wxID_ABOUT "&About..." props
menuQuit :: Menu a -> [Prop (MenuItem ())] -> IO (MenuItem ())
menuQuit menu props
  = menuItemId menu wxID_EXIT "&Quit\tCtrl+Q" props
menuItemId :: Menu a -> Id -> String -> [Prop (MenuItem ())] -> IO (MenuItem ())
menuItemId menu id label props
  = menuItemEx menu id label wxITEM_NORMAL props
menuItemEx :: Menu a -> Id -> String -> Int -> [Prop (MenuItem ())] -> IO (MenuItem ())
menuItemEx menu id label kind props
  = do if (kind == wxITEM_RADIO)
        then menuAppendRadioItem menu id label ""
        else menuAppend menu id label "" (kind == wxITEM_CHECK)
       item <- menuFindItem menu id nullPtr
       set item props
       return item
instance Able (MenuItem a) where
  enabled = newAttr "enabled" menuItemIsEnabled menuItemEnable
instance Textual (MenuItem a) where
  text
    = reflectiveAttr "text" menuItemGetText menuItemSetText
instance Help (MenuItem a) where
  help  = newAttr "help" menuItemGetHelp menuItemSetHelp
instance Checkable (MenuItem a) where
  checkable = reflectiveAttr "checkable" menuItemIsCheckable (\m c -> menuItemSetCheckable m (intFromBool c))
  checked   = newAttr "checked"   menuItemIsChecked menuItemCheck
instance Identity (MenuItem a) where
  identity  = newAttr "identity" menuItemGetId menuItemSetId
menu :: MenuItem a -> Event (Window w) (IO ())
menu item
  = let id = unsafePerformIO (get item identity)
    in  menuId id
menuId :: Id -> Event (Window w) (IO ())
menuId id
  = newEvent "menu" (\w -> evtHandlerGetOnMenuCommand w id) (\w h -> evtHandlerOnMenuCommand w id h)
              
instance Commanding (MenuItem a) where
  command
    = newEvent "command" menuItemGetOnCommand menuItemOnCommand
menuItemGetOnCommand :: MenuItem a -> IO (IO ())
menuItemGetOnCommand item 
  = do id      <- get item identity
       topmenu <- menuItemGetTopMenu item
       evtHandlerGetOnMenuCommand topmenu id
menuItemOnCommand :: MenuItem a -> IO () -> IO ()
menuItemOnCommand item io
  = do id      <- get item identity
       topmenu <- menuItemGetTopMenu item
       
       evtHandlerOnMenuCommand topmenu id io
       
       menuUpdateEvtHandlers topmenu (insert id io)
       
       frame   <- menuGetFrame topmenu
       when (not (objectIsNull frame)) (evtHandlerOnMenuCommand frame id io)
  where
    insert key val []         = [(key,val)]
    insert key val ((k,v):xs) | key == k  = (key,val):xs
                              | otherwise = (k,v):insert key val xs
menuPropagateEvtHandlers :: Menu a -> IO ()
menuPropagateEvtHandlers menu
  = do parent   <- menuGetTopMenu menu
       handlers <- menuGetEvtHandlers menu
       menuSetEvtHandlers menu []
       menuSetEvtHandlers parent handlers
menuGetFrame :: Menu a -> IO (Frame ())
menuGetFrame menu
  = do menubar <- menuGetMenuBar menu
       if (objectIsNull menubar) 
        then return objectNull
        else menuBarGetFrame menubar
menuItemGetTopMenu :: MenuItem a -> IO (Menu ())
menuItemGetTopMenu item
  = do menu <- menuItemGetMenu item
       menuGetTopMenu menu
menuGetTopMenu :: Menu a -> IO (Menu ())
menuGetTopMenu menu
  = do parent <- menuGetParent menu
       if (objectIsNull parent)
        then return (downcastMenu menu)
        else menuGetTopMenu parent
evtHandlerSetAndResetMenuCommands :: EvtHandler a -> Menu b -> IO ()
evtHandlerSetAndResetMenuCommands evtHandler menu
  = do handlers <- menuGetEvtHandlers menu
       menuSetEvtHandlers menu []
       mapM_ (\(id,io) -> evtHandlerOnMenuCommand evtHandler id io) handlers
menuUpdateEvtHandlers menu f
  = do hs <- menuGetEvtHandlers menu
       menuSetEvtHandlers menu (f hs)
menuGetEvtHandlers :: Menu a -> IO [(Id,IO ())]
menuGetEvtHandlers menu 
  = do mbHandlers <- unsafeEvtHandlerGetClientData menu
       case mbHandlers of
         Nothing -> return []
         Just hs -> return hs
menuSetEvtHandlers :: Menu a -> [(Id,IO ())] -> IO ()
menuSetEvtHandlers menu hs
  = evtHandlerSetClientData menu (return ()) hs 
toolBar :: Frame a -> [Prop (ToolBar ())] -> IO (ToolBar ())
toolBar parent props
  = toolBarEx parent True True props
toolBarEx :: Frame a -> Bool -> Bool -> [Prop (ToolBar ())] -> IO (ToolBar ())
toolBarEx parent showText showDivider props
  = do let style = ( wxTB_DOCKABLE .+. wxTB_FLAT
                   .+. (if showText then wxTB_TEXT else 0)
                   .+. (if showDivider then 0 else wxTB_NODIVIDER)
                   )
       t <- toolBarCreate parent idAny rectNull style
       frameSetToolBar parent t
       
       set t props
       return t
data ToolBarItem  = ToolBarItem (ToolBar ()) Id Bool
instance Able ToolBarItem  where
  enabled 
    = newAttr "enabled" getter setter
    where
      getter (ToolBarItem toolbar id isToggle)
        = toolBarGetToolEnabled toolbar id
      setter (ToolBarItem toolbar id isToggle) enable
        = toolBarEnableTool toolbar id enable
         
instance Tipped ToolBarItem where
  tooltip 
    = newAttr "tooltip" getter setter
    where
      getter (ToolBarItem toolbar id isToggle)
        = toolBarGetToolShortHelp toolbar id
      setter (ToolBarItem toolbar id isToggle) txt
        = toolBarSetToolShortHelp toolbar id txt
         
instance Help ToolBarItem  where
  help  
    = newAttr "help" getter setter
    where
      getter (ToolBarItem toolbar id isToggle)
        = toolBarGetToolLongHelp toolbar id
      setter (ToolBarItem toolbar id isToggle) txt
        = toolBarSetToolLongHelp toolbar id txt
         
instance Checkable ToolBarItem where
  checkable 
    = readAttr "checkable" getter
    where
      getter (ToolBarItem toolbar id isToggle)
        = return isToggle
  checked   
    = newAttr "checked"  getter setter
    where
      getter (ToolBarItem toolbar id isToggle)
        = toolBarGetToolState toolbar id
      setter (ToolBarItem toolbar id isToggle) toggle
        = toolBarToggleTool toolbar id toggle
         
instance Identity ToolBarItem where
  identity  
    = readAttr "identity" getter
    where
      getter (ToolBarItem toolbar id isToggle)
        = return id
instance Commanding ToolBarItem where
  command
    = newEvent "command" getter setter
    where
      getter (ToolBarItem toolbar id isToggle)
        = evtHandlerGetOnMenuCommand toolbar id
      setter (ToolBarItem toolbar id isToggle) io
        = evtHandlerOnMenuCommand toolbar id io
tool :: ToolBarItem -> Event (Window w) (IO ())
tool (ToolBarItem toolbar id isToggle)
  = newEvent "tool" getter setter
  where
    getter w
      = evtHandlerGetOnMenuCommand w id
    setter w io
      = evtHandlerOnMenuCommand w id io
toolMenu :: ToolBar a -> MenuItem a -> String -> FilePath -> [Prop ToolBarItem] -> IO ToolBarItem
toolMenu toolbar menuitem label bitmapPath props
  = do isToggle <- get menuitem checkable
       id       <- get menuitem identity
       lhelp    <- get menuitem help
       shelp    <- get menuitem help
       withBitmapFromFile bitmapPath $ \bitmap ->
         do toolBarAddTool2 toolbar id label bitmap nullBitmap 
                            (if isToggle then wxITEM_CHECK else wxITEM_NORMAL)
                            shelp lhelp
            let t = ToolBarItem (downcastToolBar toolbar) id isToggle
            set t props
            toolBarRealize toolbar
            return t
       
toolItem :: ToolBar a -> String -> Bool -> FilePath -> [Prop ToolBarItem] -> IO ToolBarItem
toolItem toolbar label isCheckable bitmapPath props
  = withBitmapFromFile bitmapPath $ \bitmap ->
    do id <- idCreate
       toolBarAddTool2 toolbar id label bitmap nullBitmap 
                            (if isCheckable then wxITEM_CHECK else wxITEM_NORMAL)
                            "" ""
       let t = ToolBarItem (downcastToolBar toolbar) id isCheckable
       set t props
       toolBarRealize toolbar
       return t
toolControl :: ToolBar a -> Control b -> IO ()
toolControl toolbar control
  = do toolBarAddControl toolbar control
       return ()
   
data StatusField  = SF (Var Int) (Var (StatusBar ())) (Var Int) (Var String)
statusWidth :: Attr StatusField Int
statusWidth 
  = newAttr "statusWidth" getter setter
  where
    getter (SF vwidth _ _ _)
      = varGet vwidth
    setter (SF vwidth _ _ _) w
      = varSet vwidth w
statusField :: [Prop StatusField] -> IO StatusField
statusField props
  = do vwidth<- varCreate (1)
       vsbar <- varCreate objectNull
       vidx  <- varCreate (1)
       vtext <- varCreate ""
       let sf = SF vwidth vsbar vidx vtext
       set sf props
       return sf
instance Textual StatusField where
  text
    = newAttr "text" get set
    where
      get (SF _ vsbar vidx vtext)
        = varGet vtext
      set (SF _ vsbar vidx vtext)  text
        = do varSet vtext text
             idx <- varGet vidx
             if (idx >= 0)
              then do sbar <- varGet vsbar
                      statusBarSetStatusText sbar text idx
              else return ()
statusbar :: WriteAttr (Frame a) [StatusField]
statusbar
  = statusBar
statusBar :: WriteAttr (Frame a) [StatusField]
statusBar
  = writeAttr "statusbar" set
  where
    set f fields
      = do ws <- mapM (\field -> get field statusWidth) fields
           sb <- statusBarCreateFields f ws
           mapM_ (setsb sb) (zip [0..] fields )
    setsb sb (idx,SF _ vsbar vidx vtext)
      = do varSet vsbar sb
           varSet vidx idx
           text <- varGet vtext
           statusBarSetStatusText sb text idx