#
# Configure Niri, our wayland compositor.
#
# vim: et:ts=2:sw=2:
{
  lib,
  pkgs,
  config,
  ...
}:
let
  sysConfig = config;
  cfg = config.deprekated.niri;
in
{
  options.deprekated.niri = {
    extraStartupCommands = lib.mkOption {
      type = lib.types.str;
      default = "";
      example = ''
        niri msg action spawn -- xmessage hi
      '';
      description = "Any extra commands to be added to Niri's startup.";
    };
  };

  config = {
    # Add in some basic control utilities to our system config.
    environment.systemPackages = [
      pkgs.wlrctl
    ];

    # Don't let the niri-flake fuck with our secret provider, kwallet.
    services.gnome.gnome-keyring.enable = lib.mkForce false;

    #
    # Niri configuration.
    #
    home-manager.users.deprekated =
      {
        pkgs,
        config,
        niri,
        ...
      }:
      with lib;
      let
        binds =
          {
            suffixes,
            prefixes,
            substitutions ? { },
          }:

          #
          # This quite-useful monstrosity stolen from @sodiboo.
          #
          let
            replacer = replaceStrings (attrNames substitutions) (attrValues substitutions);
            format =
              prefix: suffix:
              let
                actual-suffix =
                  if isList suffix.action then
                    {
                      action = head suffix.action;
                      args = tail suffix.action;
                    }
                  else
                    {
                      inherit (suffix) action;
                      args = [ ];
                    };

                action = replacer "${prefix.action}-${actual-suffix.action}";
              in
              {
                name = "${prefix.key}+${suffix.key}";
                value.action.${action} = actual-suffix.args;
              };
            pairs =
              attrs: fn:
              concatMap (
                key:
                fn {
                  inherit key;
                  action = attrs.${key};
                }
              ) (attrNames attrs);
          in
          listToAttrs (pairs prefixes (prefix: pairs suffixes (suffix: [ (format prefix suffix) ])));

        xwayland-satellite-loop = pkgs.writeScriptBin "xwayland-satellite-loop" ''
          #!${pkgs.bash}/bin/bash

          while true; do
            ${pkgs.xwayland-satellite}/bin/xwayland-satellite
          done
        '';
      in
      {
        # Don't let the niri-flake fuck with our secret provider, kwallet.
        services.gnome-keyring.enable = lib.mkForce false;

        #
        # Core Niri settings.
        #
        programs.niri.settings = {

          # Use Wayland programs when possible.
          environment = {
            NIXOS_OZONE_WL = "1";
            SHELL_TYPE = "prompt_toolkit";
            XDG_DATA_DIRS = "/var/lib/flatpak/exports/share:/home/deprekated/.nix-profile/share:/nix/profile/share:/home/deprekated/.local/state/nix/profile/share:/etc/profiles/per-user/deprekated/share:/nix/var/nix/profiles/default/share:/run/current-system/sw/share";

            # Provided by xwayland-satellite below.
            DISPLAY = ":0";
          };

          # Avoid client-side decorations, when possible.
          prefer-no-csd = true;

          # Don't show our hotkeys on startup.
          hotkey-overlay.skip-at-startup = true;

          #
          # Adjust our layouts.
          #
          layout = {

            # No wasted space between windows.
            gaps = 0;

            # Default to half the screen.
            default-column-width = {
              proportion = 1.0 / 2.0;
            };

            # Set up the column widths for Meta+R.
            preset-column-widths = [
              { proportion = 1.0 / 2.0; }
              { proportion = 2.0 / 3.0; }
              { proportion = 1.0 / 3.0; }
            ];

            # Improve the color of focused windows.
            border = {
              active = {
                color = "#268BD2";
              };
            };
          };

          #
          # Keyboard, mouse, etc. settings.
          #
          input = {

            # Moving focus with the keyboard should move the mouse.
            warp-mouse-to-focus.enable = true;

            touchpad = {

              # No tap to click, but use the number of fingers to press
              # multiple buttons.
              tap = false;
              tap-button-map = "left-right-middle";
              click-method = "clickfinger";

              # Disable while typing.
              dwt = true;

              # Use natural scrolling.
              natural-scroll = true;
            };

            trackpoint = {
              accel-speed = 0.2;
              accel-profile = "flat";
            };

            keyboard.xkb = {
              # Make caps lock a second escape, and ralt a compose.
              options = "caps:escape,compose:ralt";
            };
          };

          #
          # Per-application settings.
          #
          window-rules = [

            # Wezterm workaround: wezterm gets sad when it
            # doesn't get to set its own width, so let it.
            {
              matches = [ { app-id = "^org.wezfurlong.wezterm$"; } ];
              default-column-width = { };
            }

            # Make anki flashcard windows modal floats.
            {
              matches = [
                {
                  app-id = "^anki$";
                  title = "Add";
                }
              ];

              open-floating = true;
              default-floating-position = {
                x = 0;
                y = 0;
                relative-to = "top-right";
              };
            }

            # Talon windows should show over other ones.
            {
              matches = [ { app-id = "talon"; } ];
            }

            # Bitwig needs to set its own width.
            {
              matches = [ { app-id = "^com.bitwig.BitwigStudio$"; } ];
              default-column-width = { };
            }

            # Let gamescope be fullscreen, by default.
            {
              matches = [ { app-id = "^gamescope$"; } ];
              default-column-width = {
                proportion = 1.0;
              };
            }
            {
              matches = [ { app-id = "^pcloud$"; } ];
              open-on-workspace = "2";
              open-focused = false;
            }

            # Keep our system monitor on the monitoring screen.
            {
              matches = [ { app-id = "io.missioncenter.MissionCenter"; } ];
              open-on-output = "Shenzhen Soogeen Electronics Co., LTD. L01N8A 0x01348C5C";
              open-fullscreen = true;
              open-focused = false;
            }
          ];

          #
          # Key bindings.
          #
          binds =
            with config.lib.niri.actions;
            let
              sh = spawn "sh" "-c";
            in
            lib.attrsets.mergeAttrsList [
              {
                # Kate keys.
                "Mod+O".action = sh "haxtype '⚪O>'";
                "Mod+T".action = sh "haxtype '🔵T>'";
                "Mod+K".action = sh "haxtype '🟣K>'";
                "Mod+W".action = sh "haxtype '🟢W>'";
                "Mod+S".action = sh "haxtype '🔴S>'";
                "Mod+E".action = sh "haxtype '⚫E>'";

                # Kate keys.
                "Mod+F1".action = sh "haxtype ⑴";
                "Mod+F2".action = sh "haxtype ⑵";
                "Mod+F3".action = sh "haxtype ⑶";

                "Print".action = screenshot;

                "Mod+Return".action = spawn "wezterm";
                "Mod+D".action = spawn "fuzzel";
                "Mod+Space".action = spawn "fuzzel";

                "XF86AudioRaiseVolume".action = sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 0.1+";
                "XF86AudioLowerVolume".action = sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 0.1-";
                "XF86Launch6".action = sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 0.1+";
                "XF86Tools".action = sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 0.1-";
                "XF86AudioMute".action = sh "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
                "XF86AudioMicMute".action = sh "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle";

                "XF86MonBrightnessDown".action = sh "brightnessctl set 10%-";
                "XF86MonBrightnessUp".action = sh "brightnessctl set 10%+";

                "XF86Messenger".action = sh "rfkill toggle bluetooth";
                "XF86Go".action = sh "rfkill toggle wwan";
                "Cancel".action = sh "rfkill block nfc";
                "XF86Favorites".action = sh "mpc toggle";

                # Lenovo buttons.
                "0x47".action = sh "brightnessctl set 10%-";
                "0x48".action = sh "brightnessctl set 10%+";

                "Mod+Q".action = close-window;

                "XF86AudioPrev".action = sh "playerctl previous";
                "XF86AudioNext".action = sh "playerctl next";
                "XF86AudioPlay".action = sh "playerctl play-pause";

                "XF86Launch5".action = sh "playerctl play-pause";

              }
              (binds {
                suffixes."Left" = "column-left";
                suffixes."Down" = "window-down";
                suffixes."Up" = "window-up";
                suffixes."Right" = "column-right";
                prefixes."Mod" = "focus";
                prefixes."Mod+Ctrl" = "move";
                prefixes."Mod+Shift" = "focus-monitor";
                prefixes."Mod+Shift+Ctrl" = "move-window-to-monitor";
                substitutions."monitor-column" = "monitor";
                substitutions."monitor-window" = "monitor";
              })
              (binds {
                suffixes."Home" = "first";
                suffixes."End" = "last";
                prefixes."Mod" = "focus-column";
                prefixes."Mod+Ctrl" = "move-column-to";
              })
              (binds {
                suffixes."U" = "workspace-down";
                suffixes."I" = "workspace-up";
                prefixes."Mod" = "focus";
                prefixes."Mod+Ctrl" = "move-window-to";
                prefixes."Mod+Shift" = "move";
              })
              (binds {
                suffixes = builtins.listToAttrs (
                  map (n: {
                    name = toString n;
                    value = [
                      "workspace"
                      n
                    ];
                  }) (range 1 9)
                );
                prefixes."Mod" = "focus";
                prefixes."Mod+Ctrl" = "move-window-to";
              })
              {
                "Mod+Comma".action = consume-window-into-column;
                "Mod+Period".action = expel-window-from-column;

                "Mod+R".action = switch-preset-column-width;
                "Mod+F".action = maximize-column;
                "Mod+Shift+F".action = fullscreen-window;
                "Mod+C".action = center-column;

                "Mod+L".action = switch-focus-between-floating-and-tiling;
                "Mod+Shift+L".action = toggle-window-floating;

                "Mod+Minus".action = set-column-width "-10%";
                "Mod+Plus".action = set-column-width "+10%";
                "Mod+Shift+Minus".action = set-window-height "-10%";
                "Mod+Shift+Plus".action = set-window-height "+10%";

                "Mod+Print".action = screenshot-window;

                "Mod+Shift+E".action = quit;
                "Mod+Shift+P".action = power-off-monitors;

                "Mod+Shift+Ctrl+T".action = toggle-debug-tint;
              }
            ];

          # Startup our wallpaper and our credential manager.
          spawn-at-startup =
            let
              niri-init = pkgs.writeScriptBin "niri-init" ''
                #!${pkgs.bash}/bin/bash
                systemctl --user restart swaybg
                sleep 2

                systemctl --user stop waybar
                niri msg action spawn -- waybar

                sleep 2

                niri msg action spawn -- ${xwayland-satellite-loop}/bin/xwayland-satellite-loop
                niri msg action spawn -- home-assistant-desktop
                ${
                  if (sysConfig.networking.hostName != "trailblazer") then "#" else ""
                }niri msg action spawn -- ${pkgs.mission-center}/bin/missioncenter
                niri msg action spawn -- ${pkgs.pcloud}/bin/pcloud

                ${cfg.extraStartupCommands}

                sleep 10

                niri msg action spawn -- kdeconnect-cli -l
              '';
            in
            [ { command = [ "${niri-init}/bin/niri-init" ]; } ];
        };
      };
  };
}
