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

let
  plainmacs =
    emacs_package:
    pkgs.writeShellScriptBin "plainmacs" ''
      INIT_SCRIPT=$(cat <<EOF
      (progn
        (setq make-backup-files nil auto-save-default nil create-lockfiles nil)
        (load-theme 'tango-dark t)
        (set-face-attribute 'default nil :background "black")
        ;; Bright yellow highlighting for selected region
        (set-face-attribute 'region nil :background "#ffff50" :foreground "black")
        ;; Bright green cursor to distinguish from yellow region
        (set-cursor-color "#ccff66")
        ;; Hightlight the current line
        (set-face-attribute 'line-number-current-line nil :foreground "white")
        ;; Set default font
        (set-face-attribute 'default nil :height 100 :width 'regular :weight 'regular :family "Cascadia Mono")
        ;; Set fallback font for unicode glyphs
        (when (display-graphic-p)
          (set-fontset-font "fontset-default" nil (font-spec :name "Noto Color Emoji")))
        (menu-bar-mode -1)
        (when (fboundp 'tool-bar-mode)
          (tool-bar-mode -1))
        (when (  fboundp 'scroll-bar-mode)
          (scroll-bar-mode -1))
        (pixel-scroll-precision-mode)
        (setq frame-resize-pixelwise t)
        )
      EOF
      )

      exec ${emacs_package}/bin/emacs -q --eval "$INIT_SCRIPT" "''${@}"
    '';
  e_shorthand =
    emacs_package:
    pkgs.writeShellScriptBin "e" ''
      exec ${emacs_package}/bin/emacs "''${@}"
    '';
in
{
  imports = [ ];

  options.me.emacs_flavor = lib.mkOption {
    type = lib.types.nullOr (
      lib.types.enum [
        "full"
        "plainmacs"
      ]
    );
    default = null;
    example = "full";
    description = "What flavor of emacs to set up.";
  };

  config = lib.mkIf (config.me.emacs_flavor != null) (
    lib.mkMerge [
      {
        environment.systemPackages = with pkgs; [
          my_emacs
          (plainmacs my_emacs)
          (e_shorthand my_emacs)
        ];

        environment.persistence."/state" = lib.mkIf (config.me.mountPersistence) {
          hideMounts = true;
          users.talexander = {
            directories = [
              ".config/emacs/eln-cache" # Installed packages
              ".config/emacs/elpa" # Installed packages
              ".config/emacs/private" # For recentf
              ".config/emacs/tree-sitter" # Compiled tree-sitter grammars
            ];
            files = [
              ".config/emacs/history" # For savehist
              ".config/emacs/.last-package-update-day" # For use-package
            ];
          };
        };

        environment.variables.EDITOR = "plainmacs";
      }
      (lib.mkIf (config.me.graphical) {
        nixpkgs.overlays = [
          (final: prev: {
            my_emacs = final.emacs-pgtk;
          })
        ];
      })
      (lib.mkIf (!config.me.graphical) {
        nixpkgs.overlays = [
          (final: prev: {
            my_emacs = final.emacs-nox;
          })
        ];
      })
      (lib.mkIf (config.me.emacs_flavor == "full") {
        nixpkgs.overlays = [
          (final: prev: {
            my_emacs = pkgs.buildEnv {
              name = prev.my_emacs.name;
              paths = with prev; [
                my_emacs
              ];
              extraOutputsToInstall = [
                "man"
                "doc"
                "info"
              ];
              nativeBuildInputs = [ final.makeWrapper ];
              postBuild = ''
                wrapProgram $out/bin/emacs --prefix PATH : ${
                  lib.makeBinPath [
                    (final.aspellWithDicts (
                      dicts: with dicts; [
                        en
                        en-computers
                        # en-science # TODO: Why is en-science non-free?
                      ]
                    ))
                    final.nixd # nix language server
                    final.nixfmt # auto-formatting nix files through nixd
                    final.clang # To compile tree-sitter grammars
                    final.shellcheck
                    final.cmake-language-server
                    final.cmake # Used by cmake-language-server
                    final.rust-analyzer
                    final.prettier # Format yaml, json, and JS
                    final.terraform-ls
                    final.typescript-language-server
                    final.tex
                  ]
                }
              '';
            };
          })
        ];

        me.install.user.talexander.file = {
          ".config/emacs" = {
            source = ./files/emacs;
            recursive = true;
          };
        };
      })
      (lib.mkIf (config.me.emacs_flavor == "plainmacs") {
        nixpkgs.overlays = [
          (final: prev: {
            my_emacs = pkgs.buildEnv {
              name = prev.my_emacs.name;
              paths = with prev; [
                my_emacs
              ];
              extraOutputsToInstall = [
                "man"
                "doc"
                "info"
              ];
            };
          })
        ];
      })
    ]
  );
}
