from std/os import symlinkExists, getConfigDir, `/`
from std/envvars import existsEnv, getEnv, delEnv, putEnv
from std/options import Option, some, get, isNone
from std/strutils import toLowerAscii, split
import pkg/owlkettle, ./sharedModule

const
  dataDir = "/usr/share/tromjaro-layout-switcher"
  iconsDir = dataDir / "icons"
  profilesDir = dataDir / "profiles"
  # GTK CSS for changing the default icon size and shape of our layout buttons
  gtkCSS = """
.layout-button {
  min-width: 16.2em;
  min-height: 10.4em;
}
"""
  appID = "com.tromjaro.LayoutSwitcher"
  layoutsGrid = [
    [ "Windows-Like (default)", "MacOS-Like (experimental)", "MX-Like" ],
    [ "Gnome-Like", "Unity-Like (experimental)", "TopX-Like" ]
  ]

proc enableTopBarIntegration(): bool =
  if not isGlobalMenuEnabled():
    # Enable global menu
    let exitCode = runCommand("/usr/bin/pkexec", ["/usr/bin/toggle-global-menu", "enable"])
    case exitCode
    of 0:
      discard
    of 126:
      return false
    of 127:
      sendNotification(appID, "Layout Switcher", "Authentication failed!")
      return false
    else:
      sendNotification(appID, "Layout Switcher", "Failed to enable global menu!")
      return false
  # Hide window borders when maximized
  discard runCommand("/usr/bin/xfconf-query",
    args=["--channel=xfwm4", "--property=/general/borderless_maximize", "--create", "--type=bool", "--set=true"])
  # Hide top bar of windows when maximized
  discard runCommand("/usr/bin/xfconf-query",
    args=["--channel=xfwm4", "--property=/general/titleless_maximize", "--create", "--type=bool", "--set=true"])
  # Put window buttons on left side
  discard runCommand("/usr/bin/xfconf-query",
    args=["--channel=xfwm4", "--property=/general/button_layout", "--create", "--type=string", "--set=CMH|"])
  return true

proc disableTopBarIntegration(): bool =
  if isGlobalMenuEnabled():
    # Disable global menu
    let exitCode = runCommand("/usr/bin/pkexec", ["/usr/bin/toggle-global-menu", "disable"])
    case exitCode
    of 0:
      discard
    of 126:
      return false
    of 127:
      sendNotification(appID, "Layout Switcher", "Authentication failed!")
    else:
      sendNotification(appID, "Layout Switcher", "Failed to disable global menu!")
      return false
  # Hide window borders when maximized
  discard runCommand("/usr/bin/xfconf-query",
    args=["--channel=xfwm4", "--property=/general/borderless_maximize", "--create", "--type=bool", "--set=true"])
  # Don't hide top bar of windows when maximized
  discard runCommand("/usr/bin/xfconf-query",
    args=["--channel=xfwm4", "--property=/general/titleless_maximize", "--create", "--type=bool", "--set=false"])
  # Put window buttons on right side
  discard runCommand("/usr/bin/xfconf-query",
    args=["--channel=xfwm4", "--property=/general/button_layout", "--create", "--type=string", "--set=|HMC"])
  return true

var
  oldConfigDir: Option[string]
  configDirChanged: bool

# Prevent loading GTK theme from ~/.config/gtk-4.0 directory when it is a symlink
if symlinkExists(getConfigDir() / "gtk-4.0"):
  if existsEnv("XDG_CONFIG_HOME"):
    oldConfigDir = some(getEnv("XDG_CONFIG_HOME"))
  putEnv("XDG_CONFIG_HOME", "/dev/null")
  configDirChanged = true

viewable App:
  loading: bool
  hooks:
    build:
      # Reset the user's XDG_CONFIG_HOME variable back to what it was before
      if configDirChanged == true:
        if oldConfigDir.isNone():
          delEnv("XDG_CONFIG_HOME")
        else:
          putEnv("XDG_CONFIG_HOME", get(oldConfigDir))

proc enableLayout(args: tuple[layoutName: string, app: AppState]) {.thread.} =
  let
    layoutName = args.layoutName
    app = args.app

  defer:
    app.loading = false
    app.redrawFromThread()

  case layoutName
  of "Windows-Like", "MX-Like", "Gnome-Like", "TopX-Like":
    if not disableTopBarIntegration():
      return
  of "Unity-Like", "MacOS-Like":
    if not enableTopBarIntegration():
      return
  else:
    return

  # Load the layout profile
  discard runCommand("/usr/bin/xfce4-panel-profiles", ["load", profilesDir / layoutName & ".tar.bz2"])
  # Restart mate-hud
  if runCommand("/usr/bin/killall", ["mate-hud"]) == 0:
    discard runCommand("/usr/bin/setsid", ["-f", "/usr/lib/mate-hud/mate-hud"])
  sendNotification(appID, "Layout Switcher", layoutName & " layout was enabled",
    icon = iconsDir / layoutName.toLowerAscii() & "-layout.svg")

var thread: Thread[(string, AppState)]

method view(app: AppState): Widget =
  result = gui:
    Window:
      title = "TROMjaro Layout Switcher"
      # Shrink window to the smallest size
      defaultSize = (1, 1)
      iconName = "tromjaro-layout-switcher"
      if app.loading:
        Box(orient = OrientY):
          Spinner {.vAlign: AlignEnd.}:
            spinning = true
            sizeRequest = (300, 300)
          Label {.vAlign: AlignStart.}:
            text = "Loading your layout, please wait..."
      else:
        Box(orient = OrientY, margin = 7, spacing = 5):
          Box(orient = OrientX) {.vAlign: AlignEnd.}:
            Label {.hAlign: AlignEnd.}:
              text = "Please use the"
            LinkButton {.expand: false.}:
              text = "Panel Profiles"
              proc clicked() =
                discard runCommand("/usr/bin/setsid", ["-f", "/usr/bin/xfce4-panel-profiles"])
            Label {.hAlign: AlignStart.}:
              text = "to save your current configuration in case you did any manual changes, else you may lose them."
          Label {.vAlign: AlignStart.}:
            text = "Changing to or from any layout that has global menu will require your admin password."
          for row in layoutsGrid:
            Box(orient = OrientX, spacing = 5) {.vAlign: AlignStart.}:
              for tooltip in row:
                let layoutName = tooltip.split(' ', 1)[0]
                Button {.hAlign: AlignCenter.}:
                  Picture:
                    pixbuf = loadPixbuf(iconsDir / layoutName.toLowerAscii() & "-layout.svg")
                  tooltip = tooltip
                  style = [ButtonFlat, StyleClass("layout-button")]
                  proc clicked() =
                    app.loading = true
                    createThread(thread, enableLayout, (layoutName, app))

brew(appID, gui(App()), stylesheets=[newStylesheet(gtkCSS)])

if running(thread):
  joinThread(thread)