import XMonad
import XMonad.Util.EZConfig
import XMonad.Layout.ThreeColumns
import XMonad.Layout.WindowNavigation
import XMonad.Layout.MultiToggle.Instances
import XMonad.Layout.MultiToggle
import XMonad.Layout.NoBorders
import XMonad.Layout.IndependentScreens
import XMonad.Actions.CopyWindow
import XMonad.Actions.CycleWS
import XMonad.Actions.EasyMotion (selectWindow)
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.StatusBar
import XMonad.Hooks.StatusBar.PP
import qualified XMonad.Layout.Magnifier             as Mag
import qualified Data.Map                            as M
import qualified XMonad.StackSet                     as W
import System.Exit
import XMonad.Util.Loggers

-- TODO: xmobar

myWorkspaceNames = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
myScreens = 2
conf = def { layoutHook = myLayout
           , workspaces = withScreens myScreens myWorkspaceNames
           , modMask = mod4Mask
           }
           `additionalKeysP`
           myKeys

-- main :: IO ()
main =
  xmonad $ ewmh $ withEasySB (statusBarProp "xmobar" (pure myXmobarPP)) defToggleStrutsKey conf

-- Define the status bars for each screen
-- myStatusBars :: [StatusBarConfig]
-- myStatusBars = [ statusBarProp ("xmobar -x " ++ show sc) (pure myXmobarPP) | sc <- [0..myScreens - 1] ]

-- main :: IO ()
-- main = xmonad . ewmh $ dynamicSBs myStatusBars conf

myXmobarPP :: PP
myXmobarPP = def
    { ppSep             = magenta " • "
    , ppTitleSanitize   = xmobarStrip
    , ppCurrent         = xmobarBorder "Top" borderColor 2 . xmobarBorder "Bottom" borderColor 2
    , ppUrgent          = red . wrap (yellow "!") (yellow "!")
    , ppOrder           = \[ws, l, _, wins] -> [ws, l, wins]
    , ppExtras          = [logTitles formatFocused formatUnfocused]
    }
  where
    formatFocused   = wrap (white    "[") (white    "]") . magenta . ppWindow
    formatUnfocused = wrap (lowWhite "[") (lowWhite "]") . blue    . ppWindow

    -- | Windows should have *some* title, which should not not exceed a
    -- sane length.
    ppWindow :: String -> String
    ppWindow = xmobarRaw . (\w -> if null w then "untitled" else w) . shorten 30

    blue, lowWhite, magenta, red, white, yellow :: String -> String
    magenta  = xmobarColor "#ff79c6" ""
    blue     = xmobarColor "#bd93f9" ""
    white    = xmobarColor "#f8f8f2" ""
    yellow   = xmobarColor "#f1fa8c" ""
    red      = xmobarColor "#ff5555" ""
    lowWhite = xmobarColor "#bbbbbb" ""
    borderColor = "#8be9fd"

myKeys :: [(String, X ())]
myKeys = [
        -- Screen lock & suspend
          ("C-M1-l"       , spawn "i3lock"                             )
        , ("C-M1-S-l"     , spawn "i3lock" *> spawn "systemctl suspend")
        -- Terminal
        , ("M4-<Return>",  spawn "kitty -1")
        -- Frequent programs
        , ("M4-q",         spawn "firefox"                         )
        , ("M4-a",         runOrCopy "emacs" (className =? "Emacs"))
        , ("M4-d",         spawn "discord"                         )
        , ("M4-g",         spawn "dolphin"                         )
        -- Program launcher
        , ("M4-r",         spawn "rofi -show drun -theme Paper"       )
        -- Exit
        , ("M4-M1-C-q",     io (exitWith ExitSuccess)                  )
        -- Manipulate current client
        , ("M4-w",         kill1                                      )
        , ("M4-s",         toggleFloat                                )
        -- focus client
        , ("M4-h",         sendMessage (Go L)                         )
        , ("M4-j",         sendMessage (Go D)                         )
        , ("M4-k",         sendMessage (Go U)                         )
        , ("M4-l",         sendMessage (Go R)                         )
        -- focus client (easymotion)
        , ("M4-;", selectWindow def >>= (`whenJust` windows . W.focusWindow))
        -- Swap client
        , ("M4-S-h",        sendMessage $ Swap L)
        , ("M4-S-j",        sendMessage $ Swap D)
        , ("M4-S-k",        sendMessage $ Swap U)
        , ("M4-S-l",        sendMessage $ Swap R)
        , ("M4-S-<Return>", windows $ W.swapMaster)
        -- Move client
        , ("M4-M1-S-h",     sendMessage (Move L)                       )
        , ("M4-M1-S-j",     sendMessage (Move D)                       )
        , ("M4-M1-S-k",     sendMessage (Move U)                       )
        , ("M4-M1-S-l",     sendMessage (Move R)                       )
        -- Fullscreen
        , ("M4-f",         sendMessage (Toggle FULL)                  )
        -- Magnification
        , ("M4-m",         sendMessage (Mag.Toggle)                   )
        , ("M4-[",         sendMessage (Mag.MagnifyLess)              )
        , ("M4-]",         sendMessage (Mag.MagnifyMore)              )
        -- Modify number of master windows
        , ("M4-M1-j",       sendMessage (IncMasterN (-1)))
        , ("M4-M1-k",       sendMessage (IncMasterN   1 ))
        -- Resize master area
        , ("M4-C-h",       sendMessage Shrink)
        , ("M4-C-l",       sendMessage Expand)
        -- Cycle through workspaces
        , ("M4-<Left>",    moveTo Prev spacesOnCurrentScreen          )
        , ("M4-<Right>",   moveTo Next spacesOnCurrentScreen          )
        -- Cycle through empty workspaces
        , ("M4-M1-<Left>",  moveTo Prev emptySpacesOnCurrentScreen)
        , ("M4-M1-<Right>", moveTo Next emptySpacesOnCurrentScreen)
        -- Send window through workspaces
        , ("M4-S-<Left>",  shiftTo Prev spacesOnCurrentScreen         )
        , ("M4-S-<Right>", shiftTo Next spacesOnCurrentScreen         )
        -- Music player control (void)
        , ("<XF86AudioPlay>", spawn "playerctl play-pause"            )
        , ("<Scroll_Lock>",   spawn "playerctl previous"              )
        , ("<XF86AudioNext>", spawn "playerctl next"                  )
        , ("<Pause>",         spawn "cmus-remote -C player-next-album")
        -- Music player control (t470)
        , ("<XF86Bluetooth>", spawn "playerctl play-pause")
        , ("<XF86Tools>",     spawn "playerctl previous")
        , ("<XF86Keyboard>",  spawn "playerctl next") -- Potentially broken key
        , ("<XF86Favorites>", spawn "cmus-remote -C player-next-album")
        -- Audio control
        , ("<XF86AudioMute>",          spawn "wpctl set-mute   @DEFAULT_SINK@   toggle")
        , ("<XF86AudioLowerVolume>",   spawn "wpctl set-volume @DEFAULT_SINK@   1%-")
        , ("<XF86AudioRaiseVolume>",   spawn "wpctl set-volume @DEFAULT_SINK@   1%+")
        -- Mic control
        , ("<XF86AudioMicMute>",       spawn "wpctl set-mute   @DEFAULT_SOURCE@ toggle")
        , ("M1-<XF86AudioLowerVolume>", spawn "wpctl set-volume @DEFAULT_SOURCE@ 1%-")
        , ("M1-<XF86AudioRaiseVolume>", spawn "wpctl set-volume @DEFAULT_SOURCE@ 1%+")
        -- backlight control (t470)
        , ("<XF86MonBrightnessDown>", spawn "brightnessctl set 1%-")
        , ("<XF86MonBrightnessUp>",   spawn "brightnessctl set 1%+")
        -- backlight control (void)
        , ("M4-<Prior>", spawn "ddcutil setvcp 10 + 10")
        , ("M4-<Next>",  spawn "ddcutil setvcp 10 - 10")
        -- Reload configuration
        , ("M4-<Escape>", spawn "xmonad --recompile; xmonad --restart")
        ] ++ concat [
          -- Workspace switching
          [ ("M4-"   ++ i,   windows $ onCurrentScreen W.view   $ i)
          -- Move window to workspace
          , ("M4-S-" ++ i,   windows $ onCurrentScreen W.shift  $ i)
          -- Copy window to workspace
          , ("M4-M1-S-" ++ i, windows $ onCurrentScreen copy     $ i) ]
          | i <- myWorkspaceNames
        ]

-- https://stackoverflow.com/questions/33547168/xmonad-combine-dwm-style-workspaces-per-physical-screen-with-cycling-function
isOnScreen :: ScreenId -> WindowSpace -> Bool
isOnScreen s ws = s == unmarshallS (W.tag ws)

currentScreen :: X ScreenId
currentScreen = gets (W.screen . W.current . windowset)

spacesOnCurrentScreen :: WSType
spacesOnCurrentScreen = WSIs (isOnScreen <$> currentScreen)

emptySpacesOnCurrentScreen :: WSType
emptySpacesOnCurrentScreen = spacesOnCurrentScreen :&: emptyWS

myLayout = smartBorders
  $ windowNavigation
  $ mkToggle (single FULL)
  $ (Mag.magnifiercz' 1.3 $ ThreeColMid 1 (3/100) (2/3))
  ||| Tall        1 (3/100) (2/3)

-- https://www.reddit.com/r/xmonad/comments/hm2tg0/how_to_toggle_floating_state_on_a_window/
-- Centre and float a window (retain size)
centreFloat win = do
    (_, W.RationalRect x y w h) <- floatLocation win
    windows $ W.float win (W.RationalRect ((1 - w) / 2) ((1 - h) / 2) w h)
    return ()

-- If the window is floating then (f), if tiled then (n)
floatOrNot f n = withFocused $ \windowId -> do
    floats <- gets (W.floating . windowset)
    if windowId `M.member` floats -- if the current window is floating...
       then f
       else n

-- Float and centre a tiled window, sink a floating window
toggleFloat = floatOrNot (withFocused $ windows . W.sink) (withFocused float)
