    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Prompt.Window
    -- Copyright   :  Devin Mullins <me@twifkak.com>
    --                Andrea Rossato <andrea.rossato@unibz.it>
    -- License     :  BSD-style (see LICENSE)
    --
    -- Maintainer  :  Devin  Mullins <me@twifkak.com>
    --                Andrea Rossato <andrea.rossato@unibz.it>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- xprompt operations to bring windows to you, and bring you to windows.
    --
    -----------------------------------------------------------------------------

    module XMonad.Prompt.Window
        (
        -- * Usage
        -- $usage
        WindowPrompt(..),
        windowPrompt,
        windowMultiPrompt,
        allWindows,
        wsWindows,
        XWindowMap,

        -- * Deprecated
        windowPromptGoto,
        windowPromptBring,
        windowPromptBringCopy,
        ) where

    import Control.Monad (forM)
    import qualified Data.Map as M

    import qualified XMonad.StackSet as W
    import XMonad
    import XMonad.Prompt
    import XMonad.Actions.CopyWindow
    import XMonad.Actions.WindowBringer
    import XMonad.Util.NamedWindows

    -- $usage
    -- WindowPrompt brings windows to you and you to windows. That is to
    -- say, it pops up a prompt with window names, in case you forgot
    -- where you left your XChat. It also offers helpers to build the
    -- subset of windows which is used for the prompt completion.
    --
    -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
    --
    -- > import XMonad.Prompt
    -- > import XMonad.Prompt.Window
    --
    -- and in the keys definition:
    --
    -- > , ((modm .|. shiftMask, xK_g     ), windowPrompt def Goto wsWindows)
    -- > , ((modm .|. shiftMask, xK_b     ), windowPrompt def Bring allWindows)
    --
    -- The autoComplete option is a handy complement here:
    --
    -- > , ((modm .|. shiftMask, xK_g     ), windowPrompt
    -- >                                        def { autoComplete = Just 500000 }
    -- >                                        Goto allWindows)
    --
    -- The \'500000\' is the number of microseconds to pause before sending you to
    -- your new window. This is useful so that you don't accidentally send some
    -- keystrokes to the selected client.
    --
    -- For detailed instruction on editing the key binding see
    -- "XMonad.Doc.Extending#Editing_key_bindings".

    -- Describe actions that can applied  on the selected window
    data WindowPrompt = Goto | Bring | BringCopy | BringToMaster
    instance XPrompt WindowPrompt where
        showXPrompt Goto      = "Go to window: "
        showXPrompt Bring     = "Bring window: "
        showXPrompt BringToMaster
                              = "Bring window to master: "
        showXPrompt BringCopy = "Bring a copy: "
        commandToComplete _ c = c
        nextCompletion      _ = getNextCompletion

    -- | Internal type used for the multiple mode prompt.
    data WindowModePrompt =
      WindowModePrompt WindowPrompt (M.Map String Window) (String -> String -> Bool)

    instance XPrompt WindowModePrompt where
        showXPrompt (WindowModePrompt action _ _) =
            showXPrompt action

        completionFunction (WindowModePrompt _ winmap predicate) =
            \s -> return . filter (predicate s) . map fst . M.toList $ winmap

        modeAction (WindowModePrompt action winmap _) buf auto = do
            let name = if null auto then buf else auto
                a = case action of
                      Goto          -> gotoAction  winmap
                      Bring         -> bringAction winmap
                      BringCopy     -> bringCopyAction winmap
                      BringToMaster -> bringToMaster winmap
            a name
          where
            winAction a m    = flip whenJust (windows . a) . flip M.lookup m
            gotoAction       = winAction W.focusWindow
            bringAction      = winAction bringWindow
            bringCopyAction  = winAction bringCopyWindow
            bringToMaster    = winAction (\w s -> W.shiftMaster . W.focusWindow w $ bringWindow w s)

    -- | Deprecated. Use windowPrompt instead.
    {-# DEPRECATED windowPromptGoto      "Use windowPrompt instead." #-}
    {-# DEPRECATED windowPromptBring     "Use windowPrompt instead." #-}
    {-# DEPRECATED windowPromptBringCopy "Use windowPrompt instead." #-}
    windowPromptGoto, windowPromptBring, windowPromptBringCopy :: XPConfig -> X ()
    windowPromptGoto c = windowPrompt c Goto windowMap
    windowPromptBring c = windowPrompt c Bring windowMap
    windowPromptBringCopy c = windowPrompt c BringCopy windowMap

    -- | A helper to get the map of all windows.
    allWindows :: XWindowMap
    allWindows = windowMap

    -- | A helper to get the map of windows of the current workspace.
    wsWindows :: XWindowMap
    wsWindows = withWindowSet (return . W.index) >>= winmap
        where
          winmap = fmap M.fromList . mapM pair
          pair w = do name <- fmap show $ getName w
                      return (name, w)

    -- | A Map where keys are pretty printable window names and values are
    -- Xmonad windows identifier.
    type XWindowMap = X (M.Map String Window)

    -- | Pops open a prompt with window titles belonging to
    -- winmap. Choose one, and an action is applied on the
    -- selected window, according to WindowPrompt.
    windowPrompt :: XPConfig -> WindowPrompt -> XWindowMap -> X ()
    windowPrompt c t winmap = do
      wm <- winmap
      let mode     = WindowModePrompt t wm (searchPredicate c)
          action   = modeAction mode
          compList = completionFunction mode
      mkXPrompt t c compList (\s -> action s s)

    -- | Like 'windowPrompt', but uses the multiple modes feature of
    -- @Prompt@ (via 'mkXPromptWithModes').
    --
    -- Given a list of actions along with the windows they should work
    -- with, display the appropriate prompt with the ability to switch
    -- between them using the @changeModeKey@.
    --
    -- For example, to have a prompt that first shows you all windows, but
    -- allows you to narrow the list down to just the windows on the
    -- current workspace:
    --
    -- > windowMultiPrompt config [(Goto, allWindows), (Goto, wsWindows)]
    windowMultiPrompt :: XPConfig -> [(WindowPrompt, XWindowMap)] -> X ()
    windowMultiPrompt c modes = do
      modes' <- forM modes $ \(t, wm) -> do
        wm' <- wm
        return . XPT $ WindowModePrompt t wm' (searchPredicate c)

      mkXPromptWithModes modes' c

    -- | Brings a copy of the specified window into the current workspace.
    bringCopyWindow :: Window -> WindowSet -> WindowSet
    bringCopyWindow w ws = copyWindow w (W.currentTag ws) ws