--
-- Xmonad config for Solomon Laing
--

-- Base
import XMonad
import System.IO (hPutStrLn)
import System.Exit (ExitCode(ExitSuccess), exitWith )
import qualified XMonad.StackSet as W

    -- Actions
import XMonad.Actions.CopyWindow (kill1)
import XMonad.Actions.CycleWS (moveTo, shiftTo, WSType(..), nextScreen, prevScreen)
import XMonad.Actions.GridSelect
import XMonad.Actions.MouseResize
import XMonad.Actions.Submap
import XMonad.Actions.Promote
import XMonad.Actions.RotSlaves (rotSlavesDown, rotAllDown)
import qualified XMonad.Actions.TreeSelect as TS
import XMonad.Actions.WindowGo (runOrRaise)
import XMonad.Actions.WithAll (sinkAll, killAll)
import qualified XMonad.Actions.Search as S

    -- Data
import Data.Char (isSpace, toUpper)
import Data.Maybe (fromJust)
import Data.Monoid
import Data.Maybe (isJust)
import Data.Tree
import qualified Data.Map as M

    -- Hooks
import XMonad.Hooks.DynamicLog (dynamicLogWithPP, wrap, xmobarPP, xmobarColor, shorten, PP(..))

import XMonad.Hooks.FadeInactive
import XMonad.Hooks.ManageDocks (docks, avoidStruts, docksEventHook, manageDocks, ToggleStruts(..))
import XMonad.Hooks.ManageHelpers (isFullscreen, doFullFloat)
import XMonad.Hooks.EwmhDesktops (ewmh, fullscreenEventHook)
import XMonad.Hooks.ServerMode
import XMonad.Hooks.SetWMName
import XMonad.Hooks.WorkspaceHistory


    -- Layouts
import XMonad.Layout.GridVariants (Grid(Grid))
import XMonad.Layout.SimplestFloat
import XMonad.Layout.Spiral
import XMonad.Layout.ResizableTile
import XMonad.Layout.Tabbed
import XMonad.Layout.ThreeColumns

    -- Layouts modifiers
--import XMonad.Layout.Fullscreen
import XMonad.Layout.LayoutModifier
import XMonad.Layout.LimitWindows (limitWindows, increaseLimit, decreaseLimit)
import XMonad.Layout.Magnifier
import XMonad.Layout.MultiToggle (mkToggle, single, EOT(EOT), (??))
import XMonad.Layout.MultiToggle.Instances (StdTransformers(NBFULL, MIRROR, NOBORDERS))
import XMonad.Layout.NoBorders
import XMonad.Layout.Renamed
import XMonad.Layout.ShowWName
import XMonad.Layout.Simplest
import XMonad.Layout.Spacing
import XMonad.Layout.SubLayouts
import XMonad.Layout.WindowNavigation
import XMonad.Layout.WindowArranger (windowArrange, WindowArrangerMsg(..))
import qualified XMonad.Layout.ToggleLayouts as T (toggleLayouts, ToggleLayout(Toggle))
import qualified XMonad.Layout.MultiToggle as MT (Toggle(..))


   -- Utilities
import XMonad.Util.EZConfig (additionalKeysP)
import XMonad.Util.Run (runProcessWithInput, safeSpawn, spawnPipe)
import XMonad.Util.SpawnOnce

--THEME
-- #dfdfef     (120,120,120)
-- #9da5ae 	(157,165,174)
-- #6b788b 	(107,120,139)
-- #5c6776 	(92,103,118)
-- #45526d 	(69,82,109)
-- #121e32 	(18,30,49)


-- The preferred terminal program, which is used in a binding below and by
-- certain contrib modules.
myTerminal :: String
myTerminal = "alacritty"

myFileManager :: String
myFileManager = "thunar"

-- The font to be used
myFont :: String
myFont = "xft:Fira Code:antialias=true:hinting=true"

-- Whether focus follows the mouse pointer.
myFocusFollowsMouse :: Bool
myFocusFollowsMouse = True

-- Whether clicking on a window to focus also passes the click to the window
myClickJustFocuses :: Bool
myClickJustFocuses = False

-- Width of the window border in pixels.
myBorderWidth :: Dimension
myBorderWidth = 3

-- Border colors for unfocused and focused windows, respectively.
myNormalBorderColor :: String
myNormalBorderColor = "#121e32"

myFocusedBorderColor :: String
myFocusedBorderColor = "#6b788b"

-- modMask lets you specify which modkey you want to use. The default
-- is mod1Mask ("left alt").  You may also consider using mod3Mask
-- ("right alt"), which does not conflict with emacs keybindings. The
-- "windows key" is usually mod4Mask.
myModMask :: KeyMask
myModMask = mod4Mask

-- The default number of workspaces (virtual screens) and their names.
-- By default we use numeric strings, but any string may be used as a
-- workspace name. The number of workspaces is determined by the length

-- of this list.
--
-- A tagging example:
--
-- > workspaces = ["web", "irc", "code" ] ++ map show [4..9]
xmobarEscape :: String -> String
xmobarEscape = concatMap doubleLts
  where
        doubleLts '<' = "<<"
        doubleLts x   = [x]

myWorkspaces :: [String]


-- myWorkspaces = [" 1 ", " 2 ", " 3 ", " 4 ", " 5 ", " 6 ", " 7 ", " 8 ", " 9 "]
myWorkspaces = ["web", "game", "dev", "doc", "mus", "vid", "sys", "chat", "virt"]
myWorkspaceIndices = M.fromList $ zipWith (,) myWorkspaces [1..] -- (,) == \x y -> (x,y)

clickable ws = "<action=xdotool key super+"++show i++">"++ws++"</action>"
    where i = fromJust $ M.lookup ws myWorkspaceIndices

------------------------------------------------------------------------
-- Key bindings. Add, modify or remove key bindings here.
-- These are general keybindings custom script bindings are not included.
myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $

    [ 
        -- launch a terminal
        ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)

        -- launch rofi
        , ((modm,               xK_o     ), spawn "rofi -show run")

        -- launch dmenu
        , ((modm,               xK_a     ), spawn "dmenu_run -bw 3 -c -l 15 -h 26")

        -- launch rofi ssh
        , ((modm .|. shiftMask, xK_o     ), spawn "rofi -show ssh")

        -- launch my file manager
        , ((modm,               xK_l     ), spawn myFileManager)

        -- close focused window
        , ((modm .|. shiftMask, xK_j     ), kill)

        -- Rotate through the available layout algorithms
        , ((modm,               xK_space ), sendMessage NextLayout)

        --  Reset the layouts on the current workspace to default
        , ((modm .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)

        -- Resize viewed windows to the correct size
        , ((modm,               xK_b     ), refresh)

        -- Move focus to the next window
        , ((modm,               xK_Tab   ), windows W.focusDown)

        -- Move focus to the next window
        , ((modm .|. shiftMask, xK_Tab   ), windows W.focusUp)

        -- Move focus to the next window
        , ((modm,               xK_h     ), windows W.focusDown)

        -- Move focus to the previous window
        , ((modm,               xK_t     ), windows W.focusUp  )

        -- Move focus to the master window
        , ((modm,               xK_m     ), windows W.focusMaster  )

        -- Swap the focused window and the master window
        , ((modm,               xK_Return), windows W.swapMaster)

        -- Swap the focused window with the next window
        , ((modm .|. shiftMask, xK_Down  ), windows W.swapDown  )

        -- Swap the focused window with the previous window
        , ((modm .|. shiftMask, xK_Up    ), windows W.swapUp    )

        -- Shrink the master area
        , ((modm,               xK_Left  ), sendMessage Shrink)

        -- Expand the master area
        , ((modm,               xK_Right ), sendMessage Expand)

        -- Push window back into tiling
        , ((modm,               xK_y     ), withFocused $ windows . W.sink)

        -- Increment the number of windows in the master area
        , ((modm,               xK_w     ), sendMessage (IncMasterN 1))

        -- Deincrement the number of windows in the master area
        , ((modm,               xK_v     ), sendMessage (IncMasterN (-1)))

        -- Toggle the status bar gap
        -- Use this binding with avoidStruts from Hooks.ManageDocks.
        -- See also the statusBar function from Hooks.DynamicLog.
        --
        , ((modm,               xK_b     ), sendMessage ToggleStruts)

        -- Quit xmonad
        , ((modm .|. shiftMask, xK_apostrophe     ), io (exitWith ExitSuccess))

        -- Restart xmonad
        , ((modm,               xK_apostrophe     ), spawn "xmonad --recompile; xmonad --restart")

        -- Run xmessage with a summary of the default keybindings (useful for beginners)
        , ((modm .|. shiftMask, xK_z ), spawn ("echo \"" ++ help ++ "\" | xmessage -file -"))
    ]
    ++

    --
    -- these are my general custom bindings and scripts
    --
    [        
        -- prompt computer shutdown
        ((modm .|. shiftMask,     xK_s     ), spawn "/home/solomon/.local/bin/prompt \"Are you sure you want to Shutdown?\" \"shutdown now\"")

        -- prompt computer reboot
        , ((modm .|. shiftMask,   xK_r     ), spawn "/home/solomon/.local/bin/prompt \"Are you sure you want to Restart?\" \"reboot\"")


        -- prompt computer lock
        , ((modm,                 xK_Escape), spawn "/home/solomon/.local/bin/prompt \"Are you sure you want to lock?\" \"slock\" 1")
    
        -- unlock bitwarden cli and store session key
        , ((modm .|. shiftMask,   xK_t     ), spawn "/home/solomon/.local/bin/bw-unlock")
    
        -- search for password using bitwarden cli through dmenu
        , ((modm,                 xK_t     ), spawn "/home/solomon/.local/bin/passwords")

        -- launch searcher
        , ((modm,                 xK_s     ), spawn "/home/solomon/.local/bin/dmenusearch")

        , ((modm,                 xK_s), submap . M.fromList $
            [
                -- just open dmenusearch
                ((0, xK_a), spawn "/home/solomon/.local/bin/dmenusearch")

                -- open dmenusearch with duckduckgo preselected
                , ((0, xK_d), spawn "/home/solomon/.local/bin/dmenusearch duckduckgo")
            ])

        -- launch mounter
        , ((modm,                 xK_c     ), spawn "/home/solomon/.local/bin/dmenumount")

        -- launch unmounter
        , ((modm .|. shiftMask,   xK_c     ), spawn "/home/solomon/.local/bin/dmendduumount")

    ]
    ++

    --
    -- music controlls as I figure them out
    --por
    [
        -- decrease volume with mod+f11
        ((modm, xK_F11), spawn "/home/solomon/.local/bin/dec-sink-volume")
    
        -- increase volume with mod+f12
        , ((modm, xK_F12), spawn "/home/solomon/.local/bin/inc-sink-volume")

	    -- toggle mute with mod+f10
	    , ((modm, xK_F10), spawn "/home/solomon/.local/bin/toggle-sink-mute")

        -- decrease volume with mod+f11
        , ((modm .|. shiftMask, xK_F11), spawn "/home/solomon/.local/bin/dec-source-volume")
    
        -- increase volume with mod+f12
        , ((modm .|. shiftMask, xK_F12), spawn "/home/solomon/.local/bin/inc-source-volume")

	    -- toggle mute with mod+f10
	    , ((modm .|. shiftMask, xK_F10), spawn "/home/solomon/.local/bin/toggle-source-mute")

        -- set default sink for pactl to allow above scripts to run
        , ((modm, xK_F7), spawn "/home/solomon/.local/bin/set-default-sink")

        -- set default source for pactl
        , ((modm .|. shiftMask, xK_F7), spawn "/home/solomon/.local/bin/set-default-source")
    ]
    ++

    --
    -- hardware controls, currently just backlight
    --
    [
        -- increase backlight by 10 with mod+f9
        ((modm, xK_F9), spawn "backlight_control +5")

        -- decrese backlight by 10 with mod+f8x
        , ((modm, xK_F8), spawn "backlight_control -5")
    ]
    ++

    --
    -- mod-[1..9], Switch to workspace N
    -- mod-shift-[1..9], Move client to workspace N
    --
    [((m .|. modm, k), windows $ f i)
        | (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
        , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
    ++

    --
    -- mod-{w,e,r}, Switch to physical/Xinerama screens 1, 2, or 3
    -- mod-shift-{w,e,r}, Move client to screen 1, 2, or 3
    --
    [((m .|. modm, key), screenWorkspace sc >>= flip whenJust (windows . f))
        | (key, sc) <- zip [xK_comma, xK_period, xK_p] [0..]
        , (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]


-- Mouse bindings: default actions bound to mouse events
myMouseBindings (XConfig {XMonad.modMask = modm}) = M.fromList $

    -- mod-button1, Set the window to floating mode and move by dragging
    [ ((modm, button1), (\w -> focus w >> mouseMoveWindow w
                                       >> windows W.shiftMaster))

    -- mod-button2, Raise the window to the top of the stack
    , ((modm, button2), (\w -> focus w >> windows W.shiftMaster))

    -- mod-button3, Set the window to floating mode and resize by dragging
    , ((modm, button3), (\w -> focus w >> mouseResizeWindow w
                                       >> windows W.shiftMaster))

    -- you may also bind events to the mouse scroll wheel (button4 and button5)
    ]

------------------------------------------------------------------------
-- Layouts:

--Makes setting the spacingRaw simpler to write. The spacingRaw module adds a configurable amount of space around windows.
mySpacing :: Integer -> l a -> XMonad.Layout.LayoutModifier.ModifiedLayout Spacing l a
mySpacing i = spacingRaw True (Border i i i i) True (Border i i i i) True

-- Defining a bunch of layouts, many that I don't use.
-- limitWindows n sets maximum number of windows displayed for layout.
-- mySpacing n sets the gap size around the windows.
tall     = renamed [Replace "tall"]
           $ limitWindows 12
           $ mySpacing 8
           $ ResizableTall 1 (3/100) (1/2) []
magnify  = renamed [Replace "magnify"]
           $ magnifier
           $ limitWindows 12
           $ mySpacing 8
           $ ResizableTall 1 (3/100) (1/2) []
monocle  = renamed [Replace "monocle"]
           $ limitWindows 20 Full
floats   = renamed [Replace "floats"]
           $ limitWindows 20 simplestFloat
grid     = renamed [Replace "grid"]
           $ limitWindows 12
           $ mySpacing 8
           $ mkToggle (single MIRROR)
           $ Grid (16/10)
spirals  = renamed [Replace "spirals"]
           $ mySpacing 8
           $ spiral (6/7)
threeCol = renamed [Replace "threeCol"]
           $ limitWindows 7
           $ ThreeCol 1 (3/100) (1/2)
threeRow = renamed [Replace "threeRow"]
           $ limitWindows 7
           -- Mirror takes a layout and rotates it by 90 degrees.
           -- So we are applying Mirror to the ThreeCol layout.
           $ Mirror
           $ ThreeCol 1 (3/100) (1/2)
tabs     = renamed [Replace "tabs"]
           -- I cannot add spacing to this layout because it will
           -- add spacing between window and tabs which looks bad.
           $ tabbed shrinkText myTabTheme


-- setting colors for tabs layout and tabs sublayout.
myTabTheme = def { fontName            = myFont ++ ":size=12"
                 , activeColor         = "#6b788b"
                 , inactiveColor       = "#121e32"
                 , activeBorderColor   = "#6b788b"
                 , inactiveBorderColor = "#121e32"
                 , activeTextColor     = "#121e32"
                 , inactiveTextColor   = "#dfdfef"
                 }

-- Theme for showWName which prints current workspace when you change workspaces.
myShowWNameTheme :: SWNConfig
myShowWNameTheme = def
    { swn_font              = myFont ++ ":size=32"
    , swn_fade              = 1.0
    , swn_bgcolor           = "#121e32"
    , swn_color             = "#dfdfef"
    }



-- The layout hook
myLayout = avoidStruts $ mouseResize $ windowArrange $ T.toggleLayouts floats
        $ mkToggle (NBFULL ?? NOBORDERS ?? EOT) myDefaultLayout
        where
        -- I've commented out the layouts I don't use.
        myDefaultLayout =
                spirals
            ||| grid
            ||| noBorders monocle
            -- ||| tall
            ||| noBorders tabs
            -- ||| floats
            -- ||| magnify
            -- ||| threeCol
            -- ||| threeRow


------------------------------------------------------------------------
-- Window rules:

-- Execute arbitrary actions and WindowSet manipulations when managing
-- a new window. You can use this to, for example, always float a
-- particular program, or have a client always appear on a particular
-- workspace.
--
-- To find the property name associated with a program, use
-- > xprop | grep WM_CLASS
-- and click on the client you're interested in.
--
-- To match on the WM_NAME, you can use 'title' in the same way that
-- 'className' and 'resource' are used below.
myManageHook = composeAll
    [ className =? "Gimp"                               --> doFloat
    , className =? "guake"                              --> doIgnore
    , className =? "barrier"                            --> doFloat
    , className =? "net-runelite-client-RuneLite"       --> doFloat
    , className =? "net-runelite-launcher-Launcher"     --> doIgnore
    , resource  =? "desktop_window"                     --> doIgnore
    , (className =? "firefox" <&&> resource =? "Dialog")--> doFloat  -- Float Firefox Dialog
    , resource  =? "kdesktop"                           --> doIgnore 
    , className =? "MusicBrainz Picard"                 --> doIgnore
    , className =? "Steam"                              --> doShift ( myWorkspaces !! (7-1) )
    , className =? "discord"                            --> doShift ( myWorkspaces !! (8-1) )
    , className =? "telegram-desktop"                   --> doShift ( myWorkspaces !! (8-1) )
    , className =? "Pavucontrol"                        --> doShift ( myWorkspaces !! (5-1) )]
------------------------------------------------------------------------
-- Event handling

-- * EwmhDesktops users should change this to ewmhDesktopsEventHook
--
-- Defines a custom handler function for X Events. The function should
-- return (All True) if the default handler is to be run afterwards. To
-- combine event hooks use mappend or mconcat from Data.Monoid.
myEventHook = mempty
-- Status bars and logging

-- Perform an arbitrary action on each internal state change or X event.
-- See the 'XMonad.Hooks.DynamicLog' extension for examples.
myLogHook :: X ()
myLogHook = fadeInactiveLogHook fadeAmount
    where fadeAmount = 1.0

------------------------------------------------------------------------
-- Startup hook

-- Perform an arbitrary action each time xmonad starts or is restarted
-- with mod-q.  Used by, e.g., XMonad.Layout.PerWorkspace to initialize
-- per-workspace layout choices.
--
-- By default, do nothing.
myStartupHook = do
    -- Startup apps (tray) and settings
    spawnOnce "volumeicon &"
    spawnOnce "nm-applet &"
    spawnOnce "blueman-applet &"
    spawnOnce "kdeconnect-indicator &"
    spawnOnce "dunst &" -- notification daemon
    
    -- Startup Applications
    spawnOnce "guake &"
    spawnOnce "barrier &"
    spawnOnce "nextcloud &"
    spawnOnce "slack &"
    spawnOnce "todoist &"

    -- Start trayer last
    spawnOnce "trayer --edge top --align right --widthtype request --padding 6 --SetDockType true --SetPartialStrut true --expand true --monitor 1 --transparent true --alpha 0 --tint 0x121e32  --height 24 &"

------------------------------------------------------------------------

-- Now run xmonad with all the defaults we set up.
-- Run xmonad with the settings you specify. No need to modify this.
main :: IO ()
main = do
    xmproc <- spawnPipe "xmobar -x 0 /home/solomon/.config/xmobar/xmobarrc.hs"
    -- xmproc <- spawnPipe "xmobar -x 0 /home/solomon/.config/xmobar/xmobarrc0"
    -- xmproc <- spawnPipe "xmobar -x 1 /home/solomon/.config/xmobar/xmobarrc1"
    -- xmproc <- spawnPipe "xmobar -x 2 /home/solomon/.config/xmobar/xmobarrc2"
    -- xmonad =<< statusBar myBar myPP toggleStrutsKey defaults
    xmonad $ ewmh $ docks def
-- defaults = def
        {
            -- simple stuff
            terminal           = myTerminal,
            focusFollowsMouse  = myFocusFollowsMouse,
            clickJustFocuses   = myClickJustFocuses,
            borderWidth        = myBorderWidth,
            modMask            = myModMask,
            workspaces         = myWorkspaces,
            normalBorderColor  = myNormalBorderColor,
            focusedBorderColor = myFocusedBorderColor,

            -- key bindings
            keys               = myKeys,
            mouseBindings      = myMouseBindings,

            -- hooks, layouts
            layoutHook         = showWName' myShowWNameTheme $ myLayout,
            manageHook         = ( isFullscreen --> doFullFloat ) <+> myManageHook <+> manageDocks,
            handleEventHook    = myEventHook <+> fullscreenEventHook,
            -- logHook            = myLogHook,
            logHook            = myLogHook <+> dynamicLogWithPP xmobarPP
                                { ppOutput = \x -> hPutStrLn xmproc x
                                , ppCurrent = xmobarColor "#a8de45" "" . wrap "[" "]" -- Current workspace in xmobar
                                , ppVisible = xmobarColor "#88ae55" "" . clickable    -- Visible but not current workspace
                                , ppHidden = xmobarColor "#82AAFF" "" . wrap "*" "" . clickable   -- Hidden workspaces in xmobar
                                , ppHiddenNoWindows = xmobarColor "#9da5ae" "" . clickable        -- Hidden workspaces (no windows)
                                -- , ppTitle = xmobarColor "#b3afc2" "" . shorten 20     -- Title of active window in xmobar
                                , ppSep =  "<fc=#6b788b> | </fc>"                     -- Separators in xmobar
                                , ppUrgent = xmobarColor "#C45500" "" . wrap "!" "!"  -- Urgent workspace
                                -- , ppExtras  = [windowCount]                           -- # of windows current workspace
                                -- , ppOrder  = \(ws:l:t:ex) -> [ws,l]++ex++[t]
                                , ppOrder  = \(ws:l:t:ex) -> [ws,l]
                                },
            startupHook        = myStartupHook
        }

-- | Finally, a copy of the default bindings in simple textual tabular format.
help :: String
help = unlines ["The default modifier key is 'alt'. Default keybindings:",
    "",
    "-- launching and killing programs",
    "mod-Shift-Enter  Launch terminal",
    "mod-a            Launch run menu",
    "mod-Shift-a      Launch ssh menu",
    "mod-o            Launch dmenu",
    "mod-l            Launch file manager",
    "mod-Shift-j      Close/kill the focused window",
    "mod-Space        Rotate through the available layout algorithms",
    "mod-Shift-Space  Reset the layouts on the current workSpace to default",
    "mod-n            Resize/refresh viewed windows to the correct size",
    "",
    "-- move focus up or down the window stack",
    "mod-Tab        Move focus to the next window",
    "mod-Shift-Tab  Move focus to the previous window",
    "mod-h          Move focus to the next window",
    "mod-t          Move focus to the previous window",
    "mod-m          Move focus to the master window",
    "",
    "-- modifying the window order",
    "mod-Return   Swap the focused window and the master window",
    "mod-Shift-h  Swap the focused window with the next window",
    "mod-Shift-t  Swap the focused window with the previous window",
    "",
    "-- resizing the master/slave ratio",
    "mod-d  Shri sk the master area",
    "mod-n  Expand the master area",
    "",
    "-- floating layer support",
    "mod-y  Push window back into tiling; unfloat and re-tile it",
    "",
    "-- increase or decrease number of windows in the master area",
    "mod-w  (mod-,)   Increment the number of windows in the master area",
    "mod-vL (mod-.)   Deincrement the number of windows in the master area",
    "",
    "-- quit, or restart",
    "mod-Shift-'  Quit xmonad",
    "mod-'       Restart xmonad",
    "mod-[1..9]   Switch to workSpace N",
    "",
    "-- Workspaces & screens",
    "mod-Shift-[1..9]   Move client to workspace N",
    "mod-{',','.','p'}        Switch to physical/Xinerama screens 1, 2, or 3",
    "mod-Shift-{',','.','p'}  Move client to screen 1, 2, or 3",
    "",
    "-- Mouse bindings: default actions bound to mouse events",
    "mod-button1  Set the window to floating mode and move by dragging",
    "mod-button2  Raise the window to the top of the stack",
    "mod-button3  Set the window to floating mode and resize by dragging"]
