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

let
  cfg = config.wayland.windowManager.hyprland;
  apps = cfg.applications;

  # dynamically create a set of default app assignments
  defaultApps = mapAttrs (name: app: app.default) apps;

  # function to generate the attribute set for each application
  mkAppAttrs =
    {
      default,
      bind ? [ "" ],
      windowrule ? [ "" ],
    }:
    {
      default = mkOption {
        type = types.str;
        default = default;
        description = "The default application to use for the ${default}.";
      };
      bind = mkOption {
        type = types.listOf types.str;
        default = bind;
        description = "The keybinding to use for the ${default}.";
      };
      windowrule = mkOption {
        type = types.listOf types.str;
        default = windowrule;
        description = "The window rule to use for the ${default}.";
      };
    };

  # generate lists of all binds and window rules and remove empty strings
  binds = filter (s: s != "") (
    builtins.concatLists (map (app: app.bind or [ "" ]) (attrValues apps))
  );
  windowrules = filter (s: s != "") (
    builtins.concatLists (map (app: app.windowrule or [ "" ]) (attrValues apps))
  );

  inherit (lib)
    attrValues
    filter
    getExe
    mapAttrs
    mkOption
    types
    ;
in
{
  imports = [
    ./bemenu
    ./dmenu-bluetooth
    ./element-desktop
    ./feh
    ./kitty
    ./libreoffice
    ./librewolf
    ./mpv
    ./ncmpcpp
    ./networkmanager_dmenu
    ./newsboat
    ./passwordmanager
    ./powermenu-bemenu
    ./presentation-mode-bemenu
    ./qbittorrent
    ./screenshot
    ./thunderbird
    ./yazi
    ./zathura
    # add your application directories here
  ];

  options.wayland.windowManager.hyprland.applications = with defaultApps; {
    applauncher = mkAppAttrs {
      default = "bemenu";
      bind = [ "$mod, d, exec, ${applauncher}-run" ];
    };

    audiomixer = mkAppAttrs {
      default = "pulsemixer";
      bind = [ "$mod, a, exec, ${terminal} -T ${audiomixer} -e ${pkgs.pulsemixer}/bin/pulsemixer" ];
      windowrule = [
        "float, title:^${audiomixer}$"
        "size 50% 50%, title:^${audiomixer}$"
      ];
    };

    bluetoothsettings = mkAppAttrs {
      default = "dmenu-bluetooth";
      bind = [ "$mod SHIFT, b, exec, ${bluetoothsettings}" ];
    };

    browser = mkAppAttrs {
      default = "librewolf";
      bind = [ "$mod, b, exec, ${browser}" ];
    };

    calculator = mkAppAttrs {
      default = "octave";
      bind = [
        ", XF86Calculator, exec, ${terminal} -T ${calculator} -e ${pkgs.octave}/bin/octave"
      ];
    };

    emailclient = mkAppAttrs {
      default = "thunderbird";
      bind = [ "$mod, m, exec, ${emailclient}" ];
    };

    equalizer = mkAppAttrs {
      default = "easyeffects";
      bind = [ "$mod CTRL, e, exec, ${getExe pkgs.easyeffects}" ];
    };

    filemanager = mkAppAttrs {
      default = "yazi";
      bind = [ "$mod, e, exec, ${terminal} -T ${filemanager} -e ${filemanager}" ];
    };

    matrix-client = mkAppAttrs {
      default = "element-desktop";
      bind = [ "$mod SHIFT, e, exec, ${matrix-client}" ];
    };

    musicplayer = mkAppAttrs {
      default = "ncmpcpp";
      bind = [ "$mod SHIFT, m, exec, ${terminal} -T ${musicplayer} -e ${musicplayer}" ];
    };

    networksettings = mkAppAttrs {
      default = "networkmanager_dmenu";
      bind = [ "$mod SHIFT, n, exec, ${networksettings}" ];
    };

    notes = mkAppAttrs {
      default = "quicknote";
      bind = [ "$mod CTRL, n, exec, ${terminal} -T ${notes} -e ${getExe pkgs.synix.quicknote}" ];
    };

    office = mkAppAttrs {
      default = "libreoffice";
      bind = [ "$mod SHIFT, o, exec, ${office}" ];
    };

    password-manager = mkAppAttrs {
      default = "passmenu-bemenu";
      bind = [ "$mod, p, exec, ${password-manager}" ];
    };

    imageviewer = mkAppAttrs { default = "feh"; };

    pdfviewer = mkAppAttrs { default = "zathura"; };

    powermenu = mkAppAttrs {
      default = "powermenu-bemenu";
      bind = [ "$mod SHIFT, q, exec, ${powermenu}" ];
    };

    presentation-mode = mkAppAttrs {
      default = "presentation-mode-bemenu";
      bind = [ "$mod SHIFT, p, exec, ${presentation-mode}" ];
    };

    rssreader = mkAppAttrs {
      default = "newsboat";
      bind = [ "$mod, n, exec, ${terminal} -T ${rssreader} -e ${rssreader}" ];
    };

    screenshotter = mkAppAttrs {
      default = "screenshot";
      bind = [
        "$mod,       Print, exec, ${screenshotter} output" # select monitor
        "$mod SHIFT, Print, exec, ${screenshotter} region" # select region
        "$mod CTRL,  Print, exec, ${screenshotter} window" # select window
      ];
    };

    terminal = mkAppAttrs {
      default = "kitty";
      bind = [ "$mod, Return, exec, ${terminal}" ];
    };

    torrent-client = mkAppAttrs { default = "qbittorrent"; };

    videoplayer = mkAppAttrs { default = "mpv"; };
  };

  config = {
    wayland.windowManager.hyprland.settings = {
      bind = binds;
      windowrule = windowrules;
    };
  };
}
