-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Hooks.ManageDebug
-- Description :  A manageHook and associated logHook for debugging ManageHooks.
-- Copyright   :  (c) Brandon S Allbery KF8NH, 2014
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  allbery.b@gmail.com
-- Stability   :  unstable
-- Portability :  not portable
--
-- A @manageHook@ and associated @logHook@ for debugging 'ManageHook's.
-- Simplest usage: wrap your xmonad config in the @debugManageHook@ combinator.
-- Or use @debugManageHookOn@ for a triggerable version, specifying the
-- triggering key sequence in "XMonad.Util.EZConfig" syntax. Or use the
-- individual hooks in whatever way you see fit.
--
-----------------------------------------------------------------------------
--
--

module XMonad.Hooks.ManageDebug (debugManageHook
                                ,debugManageHookOn
                                ,manageDebug
                                ,maybeManageDebug
                                ,manageDebugLogHook
                                ,debugNextManagedWindow
                                ) where

import           XMonad
import           XMonad.Hooks.DebugStack
import           XMonad.Util.DebugWindow
import           XMonad.Util.EZConfig
import qualified XMonad.Util.ExtensibleState                                                 as XS

-- state for manageHook debugging to trigger logHook debugging
data MSDFinal = DoLogHook | SkipLogHook deriving Show
data MSDTrigger = MSDActivated | MSDInactive deriving Show
data ManageStackDebug = MSD MSDFinal MSDTrigger deriving Show
instance ExtensionClass ManageStackDebug where
  initialValue = MSD SkipLogHook MSDInactive

-- | A combinator to add full 'ManageHook' debugging in a single operation.
debugManageHook :: XConfig l -> XConfig l
debugManageHook cf = cf {logHook    = manageDebugLogHook <> logHook    cf
                        ,manageHook = manageDebug        <> manageHook cf
                        }

-- | A combinator to add triggerable 'ManageHook' debugging in a single operation.
--   Specify a key sequence as a string in "XMonad.Util.EZConfig" syntax; press
--   this key before opening the window to get just that logged.
debugManageHookOn :: String -> XConfig l -> XConfig l
debugManageHookOn key cf = cf {logHook    = manageDebugLogHook <> logHook    cf
                              ,manageHook = maybeManageDebug   <> manageHook cf
                              }
                           `additionalKeysP`
                           [(key,debugNextManagedWindow)]

-- | Place this at the start of a 'ManageHook', or possibly other places for a
--   more limited view. It will show the current 'StackSet' state and the new
--   window, and set a flag so that @manageDebugLogHook@ will display the
--   final 'StackSet' state.
--
--   Note that the initial state shows only the current workspace; the final
--   one shows all workspaces, since your 'manageHook' might use e.g. 'doShift',
manageDebug :: ManageHook
manageDebug = do
  w <- ask
  liftX $ do
    trace "== manageHook; current stack =="
    debugStackString >>= trace
    ws <- debugWindow w
    trace $ "new window:\n  " ++ ws
    -- technically we don't care about go here, since only maybeManageDebug
    -- uses it
    XS.modify $ \(MSD _ go') -> MSD DoLogHook go'
  idHook

-- | @manageDebug@ only if the user requested it with @debugNextManagedWindow@.
maybeManageDebug :: ManageHook
maybeManageDebug = do
  go <- liftX $ do
    MSD _ go' <- XS.get
    -- leave it active, as we may manage multiple windows before the logHook
    -- so we now deactivate it in the logHook
    return go'
  case go of
    MSDActivated -> manageDebug
    _            -> idHook

-- | If @manageDebug@ has set the debug-stack flag, show the stack.
manageDebugLogHook :: X ()
manageDebugLogHook = do
                       MSD log' _ <- XS.get
                       case log' of
                         DoLogHook -> do
                                  trace "== manageHook; final stack =="
                                  debugStackFullString >>= trace
                                  -- see comment in maybeManageDebug
                                  XS.put $ MSD SkipLogHook MSDInactive
                         _         -> idHook

-- | Request that the next window to be managed be @manageDebug@-ed. This can
--   be used anywhere an X action can, such as key bindings, mouse bindings
--   (presumably with 'const'), 'startupHook', etc.
debugNextManagedWindow :: X ()
debugNextManagedWindow = XS.modify $ \(MSD log' _) -> MSD log' MSDActivated
