-- Haskell libraries
import Data.Ratio
import System.IO
import Data.List

-- XMonad Core
import XMonad hiding ((|||))
import qualified XMonad.StackSet as S
 
-- XMonad Contribs
-- Layouts
import XMonad.Layout.LayoutCombinators
import XMonad.Layout.Tabbed
import XMonad.Layout.IM
import XMonad.Layout.PerWorkspace
import XMonad.Layout.DecorationMadness
import XMonad.Layout.Grid
import XMonad.Layout.Decoration
import XMonad.Layout.Column
import XMonad.Layout.TwoPane
import XMonad.Layout.ComboP
import XMonad.Layout.Named
-- Utils
import XMonad.Util.EZConfig
-- Actions
import XMonad.Actions.DwmPromote
import XMonad.Actions.CycleSelectedLayouts
import XMonad.Actions.UpdatePointer
import XMonad.Actions.SpawnOn
-- Hooks
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.ManageDocks
import XMonad.Hooks.ManageHelpers
import XMonad.Hooks.SetWMName
-- Prompt
import XMonad.Prompt

import System.Posix.Process
import System.Posix.IO

-- workspace names
wsMain = "1:main"
wsBook = "5:book"
wsIrc  = "6:irc"
wsGimp  = "7:gimp"
wsIm   = "8:im"
wsWeb  = "9:web"

wslist         = [wsMain, "2:work", "3:work", "4:work", wsBook, wsIrc, wsGimp, wsIm, wsWeb]
-- entry point
main = do
  (read_fd, write_fd) <- createPipe 
  forkProcess $ do -- fixme: wait for the subprocess?
    _ <- dupTo read_fd stdInput
    closeFd write_fd
    executeFile "/home/feuerbach/bin/xmobar" False [] Nothing
  closeFd read_fd
  setFdOption write_fd CloseOnExec True
  write_h <- fdToHandle write_fd
  hSetBuffering write_h LineBuffering

  xmonad $ defaultConfig
    { logHook            = dynamicLogWithPP (myPP write_h) >> updatePointer (Relative 0.5 0.5)
    , workspaces         = wslist
    , layoutHook         = myLayouts
    , manageHook         = myManageHook <+> manageDocks <+> manageSpawn
    , borderWidth        = 0
    , focusFollowsMouse  = True
    , startupHook        = setWMName "LG3D"
    } `additionalKeys` myKeys

-- custom keybindings; are added to defaults from Config.hs and can overwrite them
myKeys =
    [ ((0,        xK_F2    ), launch "exec xfce4-terminal")
    , ((mod4Mask, xK_r     ), launch "firefox http://www.google.com/reader")
    , ((mod4Mask, xK_o     ), launch "exec konqueror")
    , ((mod4Mask, xK_i     ), launch "exec firefox")
    , ((mod4Mask, xK_q     ), launch "cd ~/photo; exec gqview")
    , ((mod4Mask, xK_t     ), launch "exec xfce4-terminal -e mutt -T mutt")
    , ((mod4Mask, xK_g     ), launch "exec gvim")
    , ((mod4Mask, xK_z     ), xmms2 "prev")
    , ((mod4Mask, xK_x     ), xmms2 "play")
    , ((mod4Mask, xK_c     ), xmms2 "pause")
    , ((mod4Mask, xK_v     ), xmms2 "stop")
    , ((mod4Mask, xK_b     ), xmms2 "next")
    , ((mod4Mask, xK_Up    ), xmms2 "server volume +5")
    , ((mod4Mask, xK_Down  ), xmms2 "server volume -5")
    , ((mod1Mask, xK_p     ), spawn "sudo hibernate-disk")
    , ((mod1Mask, xK_o     ), spawn "sudo hibernate-ram")
    , ((mod1Mask, xK_F4    ), kill)
    , ((mod1Mask, xK_Return), dwmpromote)
    , ((mod1Mask, xK_b     ), sendMessage ToggleStruts)
    , ((0,        xK_F4    ), shellPromptHere defaultXPConfig)
    , ((mod1Mask, xK_c     ), launch "gvim ~/.xmonad/xmonad.hs")
    , ((mod1Mask, xK_r     ), spawn "randr")
    , ((mod1Mask, xK_a     ), sendMessage NextLayout)
    , ((mod1Mask, xK_space ), cycleThroughLayouts ["Tabbed Simplest", "DefaultDecoration Grid"])
    , ((mod1Mask, xK_s     ), cycleThroughLayouts ["DefaultDecoration Mirror Tall", "DefaultDecoration Tall"])
    , ((mod4Mask, xK_a     ), launch "firefox ~/my/tw/tw.html")
    , ((mod4Mask, xK_l     ), launch "xscreensaver-command -lock")
    , ((mod1Mask, xK_g     ), launch "pomodoro")
    ]
    ++
    [((m .|. mod1Mask, key), screenWorkspace sc >>= flip whenJust (windows . f))
        | (key, sc) <- zip [xK_e, xK_w] [0..]
        , (f, m) <- [(S.view, 0), (S.shift, shiftMask)]]

    where
    silent = spawn.("exec " ++).(++" > /dev/null")
    -- xmms2 = silent.("env XMMS_PATH=tcp://flit xmms2 "++)
    xmms2 = silent.("xmms2 "++)
    launch = spawnHere

-- available layouts
myLayouts =
    avoidStruts $
    onWorkspace wsIm (withIM (1%7) (ClassName "Tkabber" `Or` Role "buddy_list" `Or` Role "psimain") $ tiled ||| Grid) $
    onWorkspace wsBook (myTabbed ||| Full) $
    onWorkspace wsGimp (gimpLayout ||| Full) $
    tallDefault shrinkText myTabConfig |||
    myTabbed |||
    decoGrid |||
    mirrorTallDefault shrinkText myTabConfig
    where
        tiled   = Tall nmaster delta ratio
        nmaster = 1
        ratio   = 1/2
        delta   = 3/100
        decorate = decoration shrinkText myTabConfig DefaultDecoration
        decoGrid = decorate Grid
        myTabConfig = defaultTheme
            { activeColor         = "#333399"
            , activeBorderColor   = "#888888"
            , activeTextColor     = "#FFFFFF"
            , inactiveColor       = "#333344"
            , inactiveBorderColor = "#888888"
            , inactiveTextColor   = "#FFFFFF"
            , decoHeight          = 18
            }
        myTabbed = tabbed shrinkText myTabConfig

        gimpLayout = named "Gimp" $ split 0.65 gimpMain gimpTools (Role "gimp-image-window")
        gimpTools = split 0.5  decoGrid gimpDocks (Role "gimp-toolbox")
        gimpMain = decorate $ Column 1.6
        gimpDocks = decorate $ Column 1.6
        split x = combineTwoP (TwoPane 0.03 x)

-- configuration of DynamicLog (i.e. what xmobar shows)
myPP h = defaultPP
    { ppCurrent = xmobarColor "yellow" ""
    , ppTitle   = const ""
    , ppVisible = xmobarColor "cyan" ""
    , ppOutput  = hPutStrLn h
    }

-- rules for some programs
myManageHook = composeAll
    [ className =? "Chat"       --> moveTo wsIm
    , className =? "Kpdf"       --> moveTo wsBook
    , className =? "Kghostview" --> moveTo wsBook
    , className =? "Okular"     --> moveTo wsBook
    , className =? "Djview3"    --> moveTo wsBook
    , className =? "Xchat"      --> moveTo wsIrc
    , className =? "Tkabber"    --> moveTo wsIm
    , className =? "Eclipse"    --> moveTo "2:work"
    , flip fmap className (isPrefixOf "Gimp") --> moveTo wsGimp
    , isDialog --> doCenterFloat
    , className =? "java-lang-Thread" --> doFloat
    ]
    where moveTo = doF . S.shift
