{
  config,
  lib,
  pkgs,
  ...
}:

let
  patchScriptBin =
    {
      filename,
      contents,
      path ? [ ],
    }:
    ((pkgs.writeScriptBin filename contents).overrideAttrs (old: {
      buildCommand = "${old.buildCommand}\n patchShebangs $out";
      postBuild = ''
        wrapProgram $out/bin/${filename} --prefix PATH : ${lib.makeBinPath path}
      '';
    }));
  workspace_helper = (
    patchScriptBin {
      filename = "workspace_helper";
      contents = (builtins.readFile ./files/workspace_helper.bash);
      path = with pkgs; [ jq ];
    }
  );
  sway-config = pkgs.writeTextFile {
    name = "config";
    text = ''
      # Default config for sway
      #
      # Copy this to ~/.config/sway/config and edit it to your liking.
      #
      # Read `man 5 sway` for a complete reference.

      ### Variables
      #
      # Logo key. Use Mod1 for Alt.
      set $mod Mod4
      # set $mod Mod1
      # Home row direction keys, like vim
      set $left h
      set $down j
      set $up k
      set $right l
      # Your preferred terminal emulator
      set $term ${pkgs.alacritty}/bin/alacritty
      # Your preferred application launcher
      # Note: it's recommended that you pass the final command to sway
      # set $menu dmenu_path | dmenu | xargs swaymsg exec
      set $menu ${pkgs.tofi}/bin/tofi-drun | xargs swaymsg exec --
      #set $menu ${pkgs.wofi}/bin/wofi --show drun --gtk-dark

      # Do not show a title bar on windows
      default_border pixel 2
      hide_edge_borders smart_no_gaps

      bindsym $mod+grave exec $term

      ${lib.concatMapStringsSep "\n" (item: "include ${item}") config.me.swayIncludes}
    '';
  };
  base-hotkeys = pkgs.writeTextFile {
    name = "base-hotkeys.conf";
    text = ''
      ### Key bindings
      #
      # Basics:
      #
          # kill focused window
          bindsym $mod+Shift+q kill

          # start your launcher
          bindsym $mod+Return exec $menu

          # Drag floating windows by holding down $mod and left mouse button.
          # Resize them with right mouse button + $mod.
          # Despite the name, also works for non-floating windows.
          # Change normal to inverse to use left mouse button for resizing and right
          # mouse button for dragging.
          floating_modifier $mod normal

          # reload the configuration file
          bindsym $mod+Shift+c reload

          # exit sway (logs you out of your Wayland session)
          bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
    '';
  };
  display-configs = pkgs.writeTextFile {
    name = "display-configs.conf";
    text = ''
      output 'Unknown 0x095F 0x00000000' scale 1.5
      output 'BOE 0x095F Unknown' scale 1.5
      output 'BOE 0x0BCA Unknown' scale 1.5

      # Breaks screen sharing:
      # output 'Dell Inc. DELL U3014 P1V6N35M329L' render_bit_depth 10
    '';
  };
  window-management = pkgs.writeTextFile {
    name = "window-management.conf";
    text = ''
      #
      # Layout stuff:
      #
          # You can "split" the current object of your focus with
          # $mod+b or $mod+v, for horizontal and vertical splits
          # respectively.
          bindsym $mod+h splith
          bindsym $mod+v splitv

          # Switch the current container between different layout styles
          bindsym $mod+s layout stacking
          bindsym $mod+w layout tabbed
          bindsym $mod+e layout toggle split

          # Make the current focus fullscreen
          bindsym $mod+f fullscreen

          # Toggle the current focus between tiling and floating mode
          bindsym $mod+Shift+space floating toggle

          # Swap focus between the tiling area and the floating area
          bindsym $mod+space focus mode_toggle

          # move focus to the parent container
          bindsym $mod+a focus parent
      #
      # Scratchpad:
      #
          # Sway has a "scratchpad", which is a bag of holding for windows.
          # You can send windows there and get them back later.

          # Move the currently focused window to the scratchpad
          bindsym $mod+Shift+minus move scratchpad

          # Show the next scratchpad window or hide the focused scratchpad window.
          # If there are multiple scratchpad windows, this command cycles through them.
          bindsym $mod+minus scratchpad show
      #
      # Resizing containers:
      #
      mode "resize" {
          # left will shrink the containers width
          # right will grow the containers width
          # up will shrink the containers height
          # down will grow the containers height
          bindsym $left resize shrink width 10px
          bindsym $down resize grow height 10px
          bindsym $up resize shrink height 10px
          bindsym $right resize grow width 10px

          # ditto, with arrow keys
          bindsym Left resize shrink width 10px
          bindsym Down resize grow height 10px
          bindsym Up resize shrink height 10px
          bindsym Right resize grow width 10px

          # return to default mode
          bindsym Return mode "default"
          bindsym Escape mode "default"
      }
      bindsym $mod+r mode "resize"
    '';
  };
  movement = pkgs.writeTextFile {
    name = "movement.conf";
    text = ''
      #
      # Moving around:
      #
          # Move your focus around
          # bindsym $mod+$left focus left
          # bindsym $mod+$down focus down
          # bindsym $mod+$up focus up
          # bindsym $mod+$right focus right
          # or use $mod+[up|down|left|right]
          bindsym $mod+Left focus left
          bindsym $mod+Down focus down
          bindsym $mod+Up focus up
          bindsym $mod+Right focus right

          # _move_ the focused window with the same, but add Shift
          bindsym $mod+Shift+$left move left
          bindsym $mod+Shift+$down move down
          bindsym $mod+Shift+$up move up
          bindsym $mod+Shift+$right move right
          # ditto, with arrow keys
          bindsym $mod+Shift+Left move left
          bindsym $mod+Shift+Down move down
          bindsym $mod+Shift+Up move up
          bindsym $mod+Shift+Right move right
      #
      # Workspaces:
      #
          # switch to workspace
          bindsym $mod+1 exec ${workspace_helper}/bin/workspace_helper go_to_workspace 1
          bindsym $mod+2 exec ${workspace_helper}/bin/workspace_helper go_to_workspace 2
          bindsym $mod+3 exec ${workspace_helper}/bin/workspace_helper go_to_workspace 3
          bindsym $mod+4 exec ${workspace_helper}/bin/workspace_helper go_to_workspace 4
          bindsym $mod+5 exec ${workspace_helper}/bin/workspace_helper go_to_workspace 5
          bindsym $mod+6 exec ${workspace_helper}/bin/workspace_helper go_to_workspace 6
          bindsym $mod+7 exec ${workspace_helper}/bin/workspace_helper go_to_workspace 7
          bindsym $mod+8 exec ${workspace_helper}/bin/workspace_helper go_to_workspace 8
          bindsym $mod+9 exec ${workspace_helper}/bin/workspace_helper go_to_workspace 9
          bindsym $mod+0 exec ${workspace_helper}/bin/workspace_helper go_to_workspace 10
          # move focused container to workspace
          bindsym $mod+Shift+1 exec ${workspace_helper}/bin/workspace_helper send_to_workspace 1
          bindsym $mod+Shift+2 exec ${workspace_helper}/bin/workspace_helper send_to_workspace 2
          bindsym $mod+Shift+3 exec ${workspace_helper}/bin/workspace_helper send_to_workspace 3
          bindsym $mod+Shift+4 exec ${workspace_helper}/bin/workspace_helper send_to_workspace 4
          bindsym $mod+Shift+5 exec ${workspace_helper}/bin/workspace_helper send_to_workspace 5
          bindsym $mod+Shift+6 exec ${workspace_helper}/bin/workspace_helper send_to_workspace 6
          bindsym $mod+Shift+7 exec ${workspace_helper}/bin/workspace_helper send_to_workspace 7
          bindsym $mod+Shift+8 exec ${workspace_helper}/bin/workspace_helper send_to_workspace 8
          bindsym $mod+Shift+9 exec ${workspace_helper}/bin/workspace_helper send_to_workspace 9
          bindsym $mod+Shift+0 exec ${workspace_helper}/bin/workspace_helper send_to_workspace 10
          # Note: workspaces can have any name you want, not just numbers.
          # We just use 1-10 as the default.

          bindsym $mod+Ctrl+Left exec ${workspace_helper}/bin/workspace_helper go_to_previous_monitor
          bindsym $mod+Ctrl+Shift+Left exec ${workspace_helper}/bin/workspace_helper send_to_previous_monitor
          bindsym $mod+Ctrl+Right exec ${workspace_helper}/bin/workspace_helper go_to_next_monitor
          bindsym $mod+Ctrl+Shift+Right exec ${workspace_helper}/bin/workspace_helper send_to_next_monitor
    '';
  };
  disable-focus-follows-mouse = pkgs.writeTextFile {
    name = "disable-focus-follows-mouse.conf";
    text = ''
      # Disable focus following mouse
      focus_follows_mouse no
    '';
  };
  background = pkgs.writeTextFile {
    name = "background.conf";
    text = ''
      output * bg ${./files/bliss.jpg} fill
    '';
  };
  touchpad_input = pkgs.writeTextFile {
    name = "touchpad_input.conf";
    text = ''
      input * xkb_rules "evdev"

      # All touchpads
      input type:touchpad {
            dwt enabled
            click_method clickfinger
            tap enabled
      }
    '';
  };
  announce_sway_start = pkgs.writeTextFile {
    name = "announce_sway_start.conf";
    text = ''
      # announce a running sway session to systemd
      exec systemctl --user import-environment WAYLAND_DISPLAY DISPLAY XDG_CURRENT_DESKTOP SWAYSOCK I3SOCK XCURSOR_SIZE XCURSOR_THEME
      exec dbus-update-activation-environment WAYLAND_DISPLAY DISPLAY XDG_CURRENT_DESKTOP SWAYSOCK I3SOCK XCURSOR_SIZE XCURSOR_THEME
    '';
  };

  start_screen_share = pkgs.writeShellScriptBin "start_screen_share" ''
    # Disable displaying notifications. This is useful for video conference screen sharing.
    set -euo pipefail
    IFS=$'\n\t'
    DIR="$( cd "$( dirname "''${BASH_SOURCE[0]}" )" && pwd )"

    ${pkgs.mako}/bin/makoctl mode -s do-not-disturb

    ${pkgs.sway}/bin/swaymsg output "'Dell Inc. DELL U3014 P1V6N35M329L'" scale 2
  '';
  stop_screen_share = pkgs.writeShellScriptBin "stop_screen_share" ''
    # Allow mako to show notifications again.
    set -euo pipefail
    IFS=$'\n\t'
    DIR="$( cd "$( dirname "''${BASH_SOURCE[0]}" )" && pwd )"

    ${pkgs.mako}/bin/makoctl mode -s default

    ${pkgs.sway}/bin/swaymsg output "'Dell Inc. DELL U3014 P1V6N35M329L'" scale 1
  '';
in
{
  imports = [
    ./graphical_session_target.nix
    ./iso.nix
    ./rofimoji.nix
    ./lockscreen.nix
    ./screenshot.nix
    ./force_focus.nix
    ./notification.nix
  ];

  options.me = {
    sway.enable = lib.mkOption {
      type = lib.types.bool;
      default = false;
      example = true;
      description = "Whether we want to install sway.";
    };
  };

  options.me.swayIncludes = lib.mkOption {
    type = lib.types.listOf lib.types.package;
    default = [ ];
    example = lib.literalExpression ''
      [ (pkgs.writeTextFile {
          name = "launch-kanshi.conf";
          text = "exec kanshi";
        }) ]'';
    description = "List of packages to import as sway configs.";
  };

  config = lib.mkIf (config.me.graphical && config.me.sway.enable) {
    environment.systemPackages = with pkgs; [
      alacritty
      pcmanfm
      start_screen_share
      stop_screen_share
    ];

    programs.sway.extraPackages = lib.mkForce [ ];

    me.swayIncludes = [
      base-hotkeys
      display-configs
      window-management
      movement
      disable-focus-follows-mouse
      background
      touchpad_input
      announce_sway_start
    ];

    services.seatd.enable = true;
    # TODO: Maybe use `seatd-launch -- sway` instead of running the seatd daemon
    users.users.talexander.extraGroups = [ "seat" ];

    # Probably would be cleaner to use environment.sessionVariables but programs.sway.extraSessionCommands is sway-specific.
    programs.sway.extraSessionCommands =
      if config.me.buildingPortable then
        ''
          export WLR_RENDERER_ALLOW_SOFTWARE=1
          export NIXOS_OZONE_WL=1 # Wayland support for chromium and electron
          export QT_QPA_PLATFORMTHEME=gtk3 # Use gtk theme in Qt applications
        ''
      else
        ''
          export WLR_RENDERER=vulkan
          export NIXOS_OZONE_WL=1 # Wayland support for chromium and electron
          export QT_QPA_PLATFORMTHEME=gtk3 # Use gtk theme in Qt applications
        '';

    programs.sway = {
      enable = true;
      wrapperFeatures.gtk = true;
      extraOptions =
        if config.me.buildingPortable then
          [
            "--config"
            "${sway-config}"
            "--unsupported-gpu"
          ]
        else
          [
            "--config"
            "${sway-config}"
          ];
    };

    environment.persistence."/state" = lib.mkIf (config.me.mountPersistence) {
      hideMounts = true;
      users.talexander = {
        files = [
          ".local/state/tofi-drun-history" # A cache of the desktop files for tofi
          ".cache/tofi-drun" # Execution history for tofi to sort results
        ];
      };
    };

    xdg = {
      portal = {
        enable = true;
        extraPortals = with pkgs; [
          xdg-desktop-portal-gtk
        ];
        wlr = {
          enable = true;
          settings = {
            # uninteresting for this problem, for completeness only
            screencast = {
              # output_name = "eDP-1";
              max_fps = 30;
              exec_before = "${start_screen_share}/bin/start_screen_share";
              exec_after = "${stop_screen_share}/bin/stop_screen_share";
              chooser_type = "simple";
              chooser_cmd = "${pkgs.slurp}/bin/slurp -f 'Monitor: %o' -or";
            };
          };
        };
      };
    };

    me.install.user.talexander.file = {
      ".config/mimeapps.list" = {
        # Configure default programs (for example, default browser)
        source = ./files/mimeapps.list;
      };
      ".config/gtk-3.0/settings.ini" = {
        source = ./files/settings.ini;
      };
      ".icons/default" = {
        source = "${pkgs.adwaita-icon-theme}/share/icons/Adwaita";
      };
    };

    # For mounting drives in pcmanfm
    services.gvfs.enable = true;

    # Auto-launch sway
    # Run sway as the absolute last command in the login shell init. mkBefore = 500, plain = 1000, mkAfter = 1500
    environment.loginShellInit = lib.mkOrder 2000 ''
      [ -z "$WAYLAND_DISPLAY" ] && [ -n "$XDG_VTNR" ] && [ "$XDG_VTNR" -eq 1 ] && [ "$(tty)" = "/dev/tty1" ] && exec ${pkgs.systemd}/bin/systemd-cat --identifier=sway sway
    '';
  };
}
