import XMonad
import XMonad.Actions.CopyWindow
import XMonad.Actions.DynamicProjects
import XMonad.Actions.Navigation2D
import XMonad.Actions.SpawnOn
import XMonad.Actions.WithAll
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.StatusBar
import XMonad.Layout.Decoration
import XMonad.Layout.Hidden
import XMonad.Layout.Spacing
import XMonad.Layout.Spiral
import XMonad.Layout.ThreeColumns
import XMonad.Prompt
import XMonad.Util.EZConfig
import XMonad.Util.Loggers
import XMonad.Util.SpawnOnce

----------
-- Init --
----------

main :: IO ()
main =
  xmonad
    -- ewmhFullscreen and ewmh don't commute, they MUST be in this order
    . dynamicProjects projects
    . ewmhFullscreen
    . ewmh
    . withNavigation2DConfig myNav2DConf
    . (`additionalKeysP` myKeybinds)
    . withEasySB (statusBarProp "polybar" (pure myPolybarPP)) defToggleStrutsKey
    $ myConfig

-- myConfig :: XConfig (Choose (ModifiedLayout Spacing Tall) (Choose (Mirror (ModifiedLayout Spacing Tall)) Full))
myConfig =
  def
    { borderWidth = 1,
      clickJustFocuses = False,
      focusFollowsMouse = False,
      focusedBorderColor = myFocusedBorderColor,
      layoutHook = myLayout,
      modMask = mod4Mask,
      startupHook = myStartupHook,
      normalBorderColor = myNormalBorderColor,
      workspaces = myWorkspaces
    }

myStartupHook :: X ()
myStartupHook = do
  spawnOnce myBackground
  spawnOnce myScreenLockCmd
  spawnOnce notificationDaemon

------------
-- polybar --
------------

myPolybarPP :: PP
myPolybarPP =
  def
    { ppCurrent = wrap "" "" . xmobarBorder "Top" blue 2,
      ppExtras = [logTitles formatFocused formatUnfocused],
      ppHidden = xmobarBase0 . wrap "[" "]",
      ppHiddenNoWindows = const "",
      ppOrder = \[ws, l, _, wins] -> [" " ++ ws, l], -- and wins to [ws, l] to get window names
      ppSep = xmobarMagenta " • ",
      ppTitleSanitize = xmobarStrip,
      ppUrgent = xmobarRed . wrap (xmobarYellow "!") (xmobarYellow "!")
    }
  where
    formatFocused :: String -> String
    formatFocused = wrap (xmobarBase0 "[") (xmobarBase0 "]") . xmobarBlue . ppWindow

    formatUnfocused :: String -> String
    formatUnfocused = wrap (xmobarBase0 "[") (xmobarBase0 "]") . xmobarBase0 . ppWindow

    ppWindow :: String -> String
    ppWindow = xmobarRaw . (\w -> if null w then "untitled" else w) . shorten 30

    xmobarBase0, xmobarBase2, xmobarYellow, xmobarRed, xmobarMagenta, xmobarBlue :: String -> String
    xmobarBase0 = xmobarColor base0 ""
    xmobarBase2 = xmobarColor base2 ""
    xmobarYellow = xmobarColor yellow ""
    xmobarRed = xmobarColor red ""
    xmobarMagenta = xmobarColor magenta ""
    xmobarBlue = xmobarColor blue ""

----------------
-- Workspaces --
----------------

myWorkspaces :: [String]
myWorkspaces = [wsSIG, wsIOG, wsMIO, wsSEC, wsLOG, wsMUS, wsREC, wsVID, wsADA]

wsIOG, wsSEC, wsSIG, wsMIO, wsLOG, wsMUS, wsREC, wsVID, wsADA :: String
wsIOG = "iog"
wsSEC = "sec"
wsSIG = "sig"
wsMIO = "mio"
wsLOG = "log"
wsMUS = "mus"
wsREC = "rec"
wsVID = "vid"
wsADA = "ada"

--------------
-- Projects --
--------------

projects :: [Project]
projects =
  [ Project
      { projectName = wsIOG,
        projectDirectory = "~/source/IOG/blockfrost-ops",
        projectStartHook = Just $ do
          spawnOn wsIOG iogBrowser
          spawnOn wsIOG slack
          spawnOn wsIOG iogTerminal
      },
    Project
      { projectName = wsSEC,
        projectDirectory = "~/.dotfiles",
        projectStartHook = Just $ do spawnOn wsSEC incognitoBrowser
      },
    Project
      { projectName = wsSIG,
        projectDirectory = "~/",
        projectStartHook = Just $ do
          spawnOn wsSIG signal
      },
    Project
      { projectName = wsMIO,
        projectDirectory = "~/forge/mio-ops",
        projectStartHook = Just $ do
          spawnOn wsMIO browser
          spawnOn wsMIO element
          spawnOn wsMIO mastodon
          spawnOn wsMIO mioTerminal
      },
    Project
      { projectName = wsLOG,
        projectDirectory = "~/",
        projectStartHook = Just $ do spawnOn wsIOG myTerminal
      },
    Project
      { projectName = wsMUS,
        projectDirectory = "~/",
        projectStartHook = Just $ do spawnOn wsMUS musicPlayer
      },
    Project
      { projectName = wsREC,
        projectDirectory = "~/Videos/obs-recordings",
        projectStartHook = Just $ do spawnOn wsREC obs
      },
    Project
      { projectName = wsVID,
        projectDirectory = "~/",
        projectStartHook = Nothing
      },
    Project
      { projectName = wsADA,
        projectDirectory = "~/",
        projectStartHook = Just $ do spawnOn wsADA daedalus
      }
  ]

promptTheme :: XPConfig
promptTheme =
  def
    { bgColor = base03,
      bgHLight = base02,
      fgColor = base0,
      fgHLight = base1,
      height = 31,
      position = Top,
      promptBorderWidth = 0
    }

------------------
-- Applications --
------------------

brightnessDown, brightnessUp, browser, discord, editor, element, hibernate, incognitoBrowser, iogBrowser, launcher, logseq, musicPlayer, notificationDaemon, obs, screenshot, signal, slack, iogTerminal, mioTerminal, myTerminal, volumeDown, volumeMute, volumeUp :: String
brightnessDown = "light -U 10 ; notify-send -h int:value:$(light -G) \"Brightness\""
brightnessUp = "light -A 10 ; notify-send -h int:value:$(light -G) \"Brightness\""
browser = "firefox"
daedalus = "daedalus"
discord = "discord"
editor = "codium"
element = "element-desktop"
enableEDP1 = "xrandr --output eDP-1 --primary --auto --output HDMI-1 --off"
enableHDMI1 = "xrandr --output eDP-1 --off --output HDMI-1 --primary --mode 3840x2160"
hibernate = "sudo systemctl hibernate"
incognitoBrowser = "firefox --private-window"
iogBrowser = "brave --profile-directory='Profile 1'"
launcher = "rofi -show drun"
logseq = "logseq"
musicPlayer = "spotify"
myBackground = "feh --bg-fill ~/Documents/images/posters/auroraAustralis.jpg"
notificationDaemon = "dunst"
obs = "obs"
screenshot = "flameshot gui"
signal = "signal-desktop"
slack = "slack"
mastodon = "whalebird"
iogTerminal = "terminology -e 'tmux new-session -f /etc/tmux.conf -c ~/source/IOG/blockfrost/blockfrost-ops -s blockfrost'"
mioTerminal = "terminology -e 'tmux new-session -f /etc/tmux.conf -c ~/forge/mio-ops -s mio'"
myTerminal = "terminology"
myScreenLockCmd = "xscreensaver --no-splash"
myScreenLock = "xscreensaver-command -lock"
volumeDown = "amixer set Master 2%- ; notify-send \"Volume\" -h int:value:$(amixer sget Master | awk '$0~/%/{print $5}' | tr -d '[]') -h string:x-canonical-private-synchronous:volume"
volumeMute = "amixer set Master toggle"
volumeUp = "amixer set Master 2%+ ; notify-send \"Volume\" -h int:value:$(amixer sget Master | awk '$0~/%/{print $5}' | tr -d '[]') -h string:x-canonical-private-synchronous:volume"

--------------
-- Keybinds --
--------------

myKeybinds :: [(String, X ())]
myKeybinds =
  [ -- Spawn/kill
    ("M-b", spawn browser),
    ("M-i", spawn incognitoBrowser),
    ("M-f", spawn screenshot),
    ("M-p", spawn launcher),
    ("M-S-e", spawn enableEDP1 >> spawn myBackground ),
    ("M-S-h", spawn enableHDMI1 >> spawn myBackground ),
    ("M-S-<Return>", spawn myTerminal),
    ("M-<Backspace>", kill1),
    -- Layout control
    ("M-z", sendMessage Expand),
    ("M-d", sendMessage NextLayout),
    ("M-v", sendMessage Shrink),
    ("M-C-S-l", spawn myScreenLock),
    ("M-C-d", setLayout $ Layout (layoutHook myConfig)),
    ("M-C-w", shiftToProjectPrompt promptTheme),
    ("M-w", switchProjectPrompt promptTheme),
    ("M-S-f", sinkAll), -- re-tile all floating windows
    -- Environment controls
    ("<XF86AudioLowerVolume>", spawn volumeDown),
    ("<XF86AudioMute>", spawn volumeMute),
    ("<XF86AudioRaiseVolume>", spawn volumeUp),
    ("<XF86MonBrightnessDown>", spawn brightnessDown),
    ("<XF86MonBrightnessUp>", spawn brightnessUp),
    ("<XF86Sleep>", spawn hibernate)
  ]
    -- Navigation
    ++ zipWith (makeKeybindZipper "M-" windowGo) htnsKeys dirs
    ++ zipWith (makeKeybindZipper "M-" windowSwap) gcrlKeys dirs
    ++ zipWith (makeKeybindZipper "M-C-" screenGo) htnsKeys dirs
    ++ zipWith (makeKeybindZipper "M-C-" windowToScreen) gcrlKeys dirs
  where
    makeKeybindZipper :: String -> (direction2D -> Bool -> xUnit) -> String -> direction2D -> (String, xUnit)
    makeKeybindZipper startOfCmd func restOfCmd dir = (startOfCmd ++ restOfCmd, func dir True)

    dirs :: [Direction2D]
    dirs = [L, D, U, R]

    gcrlKeys :: [String]
    gcrlKeys = ["g", "c", "r", "l"]

    htnsKeys :: [String]
    htnsKeys = ["h", "t", "n", "s"]

------------
-- Layout --
------------

-- myLayout :: Choose (ModifiedLayout Spacing Tall) (Choose (Mirror (ModifiedLayout Spacing Tall)) Full) a
myLayout = Mirror tiled
    ||| Full
    ||| spacedThreeColMid
    ||| tiled
    ||| spacedSpiral
  where
    tiled = spacing 3 $ Tall nmaster delta ratio
    spacedSpiral = spacing 3 $ spiral (6/7)
    spacedThreeColMid = spacing 3 $ ThreeColMid nmaster delta ratio
    nmaster = 1
    ratio = 1 / 2
    delta = 3 / 100

----------------
-- Navigation --
----------------

myNav2DConf :: Navigation2DConfig
myNav2DConf = def {defaultTiledNavigation = sideNavigation}

-------------
-- Palette --
-------------

-- solarized

base03, base02, base01, base00, base0, base1, base2, base3, yellow, orange, red, magenta, violet, blue, cyan, green :: String
base03 = "#002b36"
base02 = "#073642"
base01 = "#586e75"
base00 = "#657b83"
base0 = "#839496"
base1 = "#93a1a1"
base2 = "#eee8d5"
base3 = "#fdf6e3"
yellow = "#b58900"
orange = "#cb4b16"
red = "#dc322f"
magenta = "#d33682"
violet = "#6c71c4"
blue = "#268bd2"
cyan = "#2aa198"
green = "#859900"

-- uses of palette

myNormalBorderColor :: String
myNormalBorderColor = violet

myFocusedBorderColor :: String
myFocusedBorderColor = red