;;; early-init.el --- Early initialization file for Emacs  -*- lexical-binding: t; -*-

;;; Commentary:
;; Emacs 27.1 introduced early-init.el, which is run before init.el,
;; before package and UI initialization happens, and before site files
;; are loaded.

;; uses some from minimal-emacs
;; https://github.com/jamescherti/minimal-emacs.d

;; not using `use-package' in here `early-init.el'

;;; Code:


;;; Internal variables

(defvar my--gc-backup-cons-threshold gc-cons-threshold
  "Backup of the original value of `gc-cons-threshold' before startup.")

(setq gc-cons-threshold most-positive-fixnum)                         ;; will be further defined below


;;; Variables

(defvar my-debug (bound-and-true-p init-file-debug)
  "Non-nil to enable debug and verbose output.")

(defvar my-ui-features '()
  "List of user interface features to enable.
This variable holds a list of Emacs UI features that can be enabled:
- context-menu (Enables the context menu in graphical environments.)
- tool-bar (Enables the tool bar in graphical environments.)
- menu-bar (Enables the menu bar in graphical environments.)
- dialogs (Enables both file dialogs and dialog boxes.)
- tooltips (Enables tooltips.)")


;;; emacs-conf location .cache, lisp, settings

(defvar config-dir (if load-file-name
		       (file-name-directory (file-truename load-file-name))
		     user-emacs-directory)
  "Location of the distributed configuration, e.g. /opt/emacs-conf/.
Fallbacks to `user-emacs-directory' if this file is not loaded.")

(when user-init-file
  (setq user-emacs-directory (file-name-directory user-init-file)))   ;; for cache etc.
(defconst user-cache-directory
  (file-name-as-directory (concat user-emacs-directory ".cache"))
  "My Emacs storage area for persistent files.")
(make-directory user-cache-directory t)                               ;; create the `user-cache-directory' if not exists

(progn
  ;; path where settings files are kept
  (add-to-list 'load-path (concat config-dir "settings"))
  ;; add personal elisp lib dir, for manually installed packages
  (defun add-to-load-path-with-subdirs (base &optional exclude-list include-list)
    "This will add all first level dirs from BASE and exclude the ones in
  EXCLUDE-LIST, while for the dirs in INCLUDE-LIST, it will add all the
  first level dirs of that dir too.
  Example: (add-to-load-path-with-subdirs \"~/.emacs.d\" '(\".\" \"..\" \"backup\") '(\"vendor\" \"my-lisp\"))"
    (add-to-list 'load-path base nil)  ;; no append so shipped packages are loaded first
    (dolist (f (directory-files base))
      (let ((name (concat base "/" f)))
        (when (and (file-directory-p name)
                   (not (member f exclude-list)))
          (add-to-list 'load-path name nil)  ;; no append so shipped packages are loaded first
          (when (member f include-list)
            (add-to-load-path-with-subdirs name exclude-list include-list))))))
  (add-to-load-path-with-subdirs (concat config-dir "lisp") '("." ".." "0patches") nil)
  ;; load autoloads, not combined with function above because we need
  ;; load-path filled first and then we can load all autoloads, to
  ;; prevent loading parts from emacs buit-in packages. (for example
  ;; one custom packages refences to org features but custom org is
  ;; not yet in the load-path and therefore would load built-in org
  ;; instead)
  (defun load-autoloads (base &optional exclude-list include-list)
    "This will load all autoloads from all first level dirs from BASE and
  exclude the ones in EXCLUDE-LIST, while for the dirs in INCLUDE-LIST,
  it will load all the first level dirs of that dir too.
  Example: (load-autoloads \"~/.emacs.d\" '(\".\" \"..\" \"backup\") '(\"vendor\" \"my-lisp\"))"
    (dolist (f (directory-files base))
      (let ((name (concat base "/" f)))
        (when (and (file-directory-p name)
                   (not (member f exclude-list)))
          (let ((fileal (concat name "/" (file-name-base name) "-autoloads.el"))
                (nameal (concat (file-name-base name) "-autoloads")))
            (when (file-exists-p fileal)
              (require (intern nameal) fileal) ))
          (when (member f include-list)
            (add-to-load-path-with-subdirs-autoloads name exclude-list include-list))))))
  (load-autoloads (concat config-dir "lisp") '("." ".." "0patches") nil)
  )


;;; Load pre-early-init.el

(setq load-prefer-newer t)                                            ;; Prefer loading newer compiled files
(setq debug-on-error my-debug)
;; (load (expand-file-name "pre-early-init.el" user-emacs-directory) :no-error :no-message)
(setq custom-theme-directory (expand-file-name "themes/" config-dir))
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))


;;; Garbage collection
;; Garbage collection significantly affects startup times. This
;; setting delays garbage collection during startup but will be reset
;; later.

(setq garbage-collection-messages my-debug)

(defvar my-gc-optimize-startup t
  "If non-nil, increase `gc-cons-threshold' during startup to reduce pauses.
After Emacs finishes loading, `gc-cons-threshold' is restored to the
value stored in `my-gc-cons-threshold'.")

(defvar my-gc-cons-threshold (* 32 1024 1024)
  "Value to which `gc-cons-threshold' is set after Emacs startup.
Ignored if `my-gc-optimize-startup' is nil.")

(defvar my-gc-cons-threshold-restore-delay nil
  "Number of seconds to wait before restoring `gc-cons-threshold'.")

(defun my--gc-restore-cons-threshold ()
  "Restore `gc-cons-threshold' to `my-gc-cons-threshold'."
  (if (bound-and-true-p my-gc-cons-threshold-restore-delay)
      ;; Defer garbage collection during initialization to avoid 2 collections.
      (run-at-time
       my-gc-cons-threshold-restore-delay nil
       (lambda () (setq gc-cons-threshold my-gc-cons-threshold)))
    (setq gc-cons-threshold my-gc-cons-threshold)))

(if my-gc-optimize-startup
    ;; `gc-cons-threshold' is managed by early-init
    (add-hook 'emacs-startup-hook #'my--gc-restore-cons-threshold 105)
  ;; `gc-cons-threshold' is not managed by early-init.
  ;; If it is equal to `most-positive-fixnum', this indicates that the user has
  ;; not overridden the value in their `pre-early-init.el' configuration.
  (when (= gc-cons-threshold most-positive-fixnum)
    (setq gc-cons-threshold my--gc-backup-cons-threshold)))


;;; Native compilation and Byte compilation

(defvar my-setup-native-compilation t
  "Controls whether native compilation settings are enabled during setup.
When non-nil, the following variables are set to non-nil to enable
native compilation features:
- `native-comp-deferred-compilation'
- `native-comp-jit-compilation'
- `package-native-compile'
If nil, these variables are left at their default values and are not
modified during setup.")

(if (and (featurep 'native-compile)
         (fboundp 'native-comp-available-p)
         (native-comp-available-p))
    (when my-setup-native-compilation
      ;; Activate `native-compile'
      (setq native-comp-deferred-compilation t
            native-comp-jit-compilation t
            package-native-compile t))
  ;; Deactivate the `native-compile' feature if it is not available
  (setq features (delq 'native-compile features)))

(setq native-comp-warning-on-missing-source my-debug
      native-comp-async-report-warnings-errors (or my-debug 'silent)
      native-comp-verbose (if my-debug 1 0))

(setq jka-compr-verbose my-debug)
(setq byte-compile-warnings my-debug
      byte-compile-verbose my-debug)


;;; Miscellaneous

(set-language-environment "UTF-8")                                    ;; make UTF-8 the default coding system
(setq default-input-method nil)                                       ;; `set-language-enviornment' sets `default-input-method', which is unwanted
(setq read-process-output-max (* 1 1024 1024))                        ;; (1 MB) Increase how much is read from processes in a single chunk
(setq process-adaptive-read-buffering nil)
(setq ffap-machine-p-known 'reject)                                   ;; Don't ping things that look like domain names.
(setq warning-minimum-level (if my-debug :warning :error))
(setq warning-suppress-types '((lexical-binding)))
(when my-debug (setq message-log-max (expt 2 14)))


;;; Performance: Miscellaneous options

;; Font compacting can be very resource-intensive, especially when
;; rendering icon fonts on Windows. This will increase memory usage.
(setq inhibit-compacting-font-caches t)

(when (and (not (daemonp)) (not noninteractive))
  ;; Resizing the Emacs frame can be costly when changing the
  ;; font. Disable this to improve startup times with fonts larger
  ;; than the system default.
  (setq frame-resize-pixelwise t)

  ;; Without this, Emacs will try to resize itself to a specific
  ;; column size
  (setq frame-inhibit-implied-resize t)

  ;; A second, case-insensitive pass over `auto-mode-alist' is time
  ;; wasted.  No second pass of case-insensitive search over
  ;; auto-mode-alist.
  (setq auto-mode-case-fold nil)

  ;; Reduce *Message* noise at startup. An empty scratch buffer (or
  ;; the dashboard) is more than enough, and faster to display.
  (setq inhibit-startup-screen t)                                     ;; don't show the startup screen
  (setq inhibit-startup-echo-area-message user-login-name)
  (setq initial-buffer-choice nil)
  (setq inhibit-startup-buffer-menu t)
  (setq inhibit-x-resources t)

  ;; Disable bidirectional text scanning for a modest performance boost.
  (setq-default bidi-display-reordering 'left-to-right)
  (setq-default bidi-paragraph-direction 'left-to-right)

  ;; Give up some bidirectional functionality for slightly faster
  ;; re-display.
  (setq bidi-inhibit-bpa t)

  ;; Remove "For information about GNU Emacs..." message at startup
  (advice-add 'display-startup-echo-area-message :override #'ignore)

  ;; Suppress the vanilla startup screen completely. We've disabled it
  ;; with `inhibit-startup-screen', but it would still initialize
  ;; anyway.
  (advice-add 'display-startup-screen :override #'ignore)

  ;; The initial buffer is created during startup even in
  ;; non-interactive sessions, and its major mode is fully
  ;; initialized. Modes like `text-mode', `org-mode', or even the
  ;; default `lisp-interaction-mode' load extra packages and run
  ;; hooks, which can slow down startup.
  ;;
  ;; Using `fundamental-mode' for the initial buffer to avoid
  ;; unnecessary startup overhead.
  (setq initial-major-mode 'fundamental-mode)
  ;; (setq initial-scratch-message nil)
  (setq initial-scratch-message "# This buffer is for text that is not saved.

")

  (unless my-debug
    ;; Unset command line options irrelevant to the current OS. These
    ;; options are still processed by `command-line-1` but have no
    ;; effect.
    (unless (eq system-type 'darwin)
      (setq command-line-ns-option-alist nil))
    (unless (memq initial-window-system '(x pgtk))
      (setq command-line-x-option-alist nil))))


;;; Performance: File-name-handler-alist

(defvar my-optimize-file-name-handler-alist t
  "Enable optimization of `file-name-handler-alist'.
When non-nil, this variable activates optimizations to reduce file name handler
lookups during Emacs startup.")

(defvar my--old-file-name-handler-alist (default-toplevel-value
                                         'file-name-handler-alist))

(defun my--respect-file-handlers (fn args-left)
  "Respect file handlers.
FN is the function and ARGS-LEFT is the same argument as
`command-line-1'.  Emacs processes command-line files very early in
startup. These files may include special paths like TRAMP paths, so
restore `file-name-handler-alist' for this stage of initialization."
  (let ((file-name-handler-alist (if args-left
                                     my--old-file-name-handler-alist
                                   file-name-handler-alist)))
    (funcall fn args-left)))

(defun my--restore-file-name-handler-alist ()
  "Restore `file-name-handler-alist'."
  (set-default-toplevel-value
   'file-name-handler-alist
   ;; Merge instead of overwrite to preserve any changes made since
   ;; startup.
   (delete-dups (append file-name-handler-alist
                        my--old-file-name-handler-alist))))

(when (and my-optimize-file-name-handler-alist (not (daemonp))
           (not my-debug))
  ;; Determine the state of bundled libraries using
  ;; calc-loaddefs.el. If compressed, retain the gzip handler in
  ;; `file-name-handler-alist`. If compiled or neither, omit the gzip
  ;; handler during startup for improved startup and package load
  ;; time.
  (set-default-toplevel-value
   'file-name-handler-alist
   (if (locate-file-internal "calc-loaddefs.el" load-path)
       nil
     (list (rassq 'jka-compr-handler
                  my--old-file-name-handler-alist))))

  ;; Ensure the new value persists through any current let-binding.
  (put 'file-name-handler-alist 'initial-value
       my--old-file-name-handler-alist)

  ;; Emacs processes command-line files very early in startup. These
  ;; files may include special paths TRAMP. Restore
  ;; `file-name-handler-alist'.
  (advice-add 'command-line-1 :around #'my--respect-file-handlers)

  (add-hook 'emacs-startup-hook #'my--restore-file-name-handler-alist
            101))


;;; Performance: Inhibit redisplay
;; see restore below

(defvar my-inhibit-redisplay-during-startup nil
  "Suppress redisplay during startup to improve performance.
This prevents visual updates while Emacs initializes. The tradeoff is
that you won't see the progress or activities during the startup
process.")

(defun my--reset-inhibit-redisplay ()
  "Reset inhibit redisplay."
  (setq-default inhibit-redisplay nil)
  (remove-hook 'post-command-hook #'my--reset-inhibit-redisplay))

(when (and my-inhibit-redisplay-during-startup (not (daemonp))
           (not noninteractive) (not my-debug))
  ;; Suppress redisplay and redraw during startup to avoid delays and
  ;; prevent flashing an unstyled Emacs frame.
  (setq-default inhibit-redisplay t)
  (add-hook 'post-command-hook #'my--reset-inhibit-redisplay -100))


;;; Performance: Inhibit message
;; see restore below

(defvar my-inhibit-message-during-startup nil
  "Suppress startup messages for a cleaner experience.
This slightly enhances performance. The tradeoff is that you won't be
informed of the progress or any relevant activities during startup.")

(defun my--reset-inhibit-message ()
  "Reset inhibit message."
  (setq-default inhibit-message nil)
  (remove-hook 'post-command-hook #'my--reset-inhibit-message))

(when (and my-inhibit-message-during-startup (not (daemonp))
           (not noninteractive) (not my-debug))
  (setq-default inhibit-message t)
  (add-hook 'post-command-hook #'my--reset-inhibit-message -100))


;;; Performance: Disable mode-line during startup
;; see restore below

(defvar my-disable-mode-line-during-startup t
  "Disable the mode line during startup.
This reduces visual clutter and slightly enhances startup
performance. The tradeoff is that the mode line is hidden during the
startup phase.")

(when (and my-disable-mode-line-during-startup (not (daemonp))
           (not noninteractive) (not my-debug))
  (put 'mode-line-format
       'initial-value (default-toplevel-value 'mode-line-format))
  (setq-default mode-line-format nil)
  (dolist (buf (buffer-list))
    (with-current-buffer buf
      (setq mode-line-format nil))))


;;; Restore values
;; `inhibit-message' `inhibit-redisplay' `mode-line-format'

(defun my--startup-load-user-init-file (fn &rest args)
  "Advice to reset `mode-line-format'. FN and ARGS are the function and args."
  (unwind-protect
      ;; Start up as normal
      (apply fn args)
    ;; If we don't undo inhibit-{message, redisplay} and there's an error, we'll
    ;; see nothing but a blank Emacs frame.
    (when my-inhibit-message-during-startup
      (setq-default inhibit-message nil))
    (when my-inhibit-redisplay-during-startup
      (setq-default inhibit-redisplay nil))
    ;; Restore the mode-line
    (when my-disable-mode-line-during-startup
      (unless (default-toplevel-value 'mode-line-format)
        (setq-default mode-line-format (get 'mode-line-format
                                            'initial-value))))))

(advice-add 'startup--load-user-init-file :around
            #'my--startup-load-user-init-file)


;;; UI elements / frame

(defvar my-frame-title-format "%b – Emacs"
  "Template for displaying the title bar of visible and iconified frame.")

(setq frame-title-format my-frame-title-format)
(setq icon-title-format my-frame-title-format)

;; Intentionally avoid calling `menu-bar-mode', `tool-bar-mode', and
;; `scroll-bar-mode' because manipulating frame parameters can trigger
;; or queue a superfluous and potentially expensive frame redraw at
;; startup, depending on the window system. The variables must also be
;; set to `nil' so users don't have to call the functions twice to
;; re-enable them.

;; menu-bar
;; menu displayed inside `tab-bar' and via `tmm-menubar'
(unless (memq 'menu-bar my-ui-features)
  (push '(menu-bar-lines . 0) default-frame-alist)
  (unless (memq window-system '(mac ns))
    (setq menu-bar-mode nil)))

;; tool-bar
(defun my--setup-toolbar (&rest _)
  "Setup the toolbar."
  (when (fboundp 'tool-bar-setup)
    (advice-remove 'tool-bar-setup #'ignore)
    (when (bound-and-true-p tool-bar-mode)
      (funcall 'tool-bar-setup))))
(when (and (not (daemonp))
           (not noninteractive))
  (when (fboundp 'tool-bar-setup)
    ;; Temporarily override the tool-bar-setup function to prevent it
    ;; from running during the initial stages of startup
    (advice-add 'tool-bar-setup :override #'ignore)
    (advice-add 'startup--load-user-init-file :after
                #'my--setup-toolbar)))
(unless (memq 'tool-bar my-ui-features)
  (push '(tool-bar-lines . 0) default-frame-alist)
  (setq tool-bar-mode nil))

;; scroll-bars
(push '(vertical-scroll-bars) default-frame-alist)
(push '(horizontal-scroll-bars) default-frame-alist)
(setq scroll-bar-mode nil)

;; tooltips
(unless (memq 'tooltips my-ui-features)
  (when (bound-and-true-p tooltip-mode)
    (tooltip-mode -1)))

;; Disable GUIs because they are inconsistent across systems, desktop
;; environments, and themes, and they don't match the look of Emacs.
(unless (memq 'dialogs my-ui-features)
  (setq use-file-dialog nil)
  (setq use-dialog-box nil))

;; 'initial frame' is created between early-init.el and the main init.
(add-to-list 'initial-frame-alist '(alpha . (95 . 95)))             ;; transparency VALUE: '(<active> . <inactive>) / <both>
(add-to-list 'initial-frame-alist '(background-mode . dark))
(add-to-list 'initial-frame-alist '(background-color . "#1e1e1e"))
(add-to-list 'initial-frame-alist '(foreground-color . "#b2b2b2"))

(when (featurep 'frame)
  ;; Custom functions/hooks for persisting/loading frame geometry upon save/load
  (defvar my-frame-geometry-file (concat user-cache-directory "frame-geometry.el"))
  (defun my-frame-geometry-save ()
    "Gets the current frame's geometry and save it to `my-frame-geometry-file'."
    (let (;; (frameg-font (frame-parameter (selected-frame) 'font))
          (frameg-top (frame-parameter (selected-frame) 'top))
          (frameg-left (frame-parameter (selected-frame) 'left))
          (frameg-width (frame-parameter (selected-frame) 'width))
          (frameg-height (frame-parameter (selected-frame) 'height))
          ;; (frameg-alpha (frame-parameter (selected-frame) 'alpha))
          (frameg-file my-frame-geometry-file))
      (with-temp-buffer
        ;; Turn off backup for this file
        (make-local-variable 'make-backup-files)
        (setq make-backup-files nil)
        (when (featurep 'scroll-bar)  ;; for terminal scroll-bar is not loaded
          (scroll-bar-mode -1))
        (insert
         ";;; " (file-name-nondirectory frameg-file) " --- Frame configuration  -*- no-byte-compile: t; lexical-binding: t; -*-"
         ";;; This file stores the previous emacs frame's geometry.\n"
         ";;; Last generated " (current-time-string) ".\n"
         (format "(add-to-list 'initial-frame-alist '(top . %d))\n" (max frameg-top 0))
         (format "(add-to-list 'initial-frame-alist '(left . %d))\n" (max frameg-left 0))
         (format "(add-to-list 'initial-frame-alist '(width . %d))\n" (max frameg-width 0))
         (format "(add-to-list 'initial-frame-alist '(height . %d))\n" (max frameg-height 0))
         )
        (when (file-writable-p frameg-file)
          (write-file frameg-file)))))
  (defun my-frame-geometry-load ()
    "Load `my-frame-geometry-file' which should load the previous frame's geometry."
    (let ((frameg-file my-frame-geometry-file))
      (when (file-readable-p frameg-file)
        ;; (load-file frameg-file)
        (load (expand-file-name frameg-file) nil (not my-debug) t))))
  (my-frame-geometry-load)
  (add-hook 'kill-emacs-hook 'my-frame-geometry-save))


;;; Security
(setq gnutls-verify-error t)                                          ;; Prompts user if there are certificate issues
(setq tls-checktrust t)                                               ;; Ensure SSL/TLS connections undergo trust verification
(setq gnutls-min-prime-bits 3072)                                     ;; Stronger GnuTLS encryption


;;; package.el and use-package

(progn
  (setq package-enable-at-startup nil)                                ;; Let the init.el file handle this
  (add-to-list 'package-directory-list (concat config-dir "lisp"))
  (setq package-archives '(("melpa" . "https://melpa.org/packages/")
                           ("gnu" . "https://elpa.gnu.org/packages/")
                           ("nongnu" . "https://elpa.nongnu.org/nongnu/")))
  (setq package-archive-priorities '(("gnu"    . 99)
                                     ("nongnu" . 80)
                                     ("melpa"  . 70))))

(setq use-package-compute-statistics my-debug)
(setq use-package-expand-minimally (not my-debug))                    ;; (t) results in a more compact output that emphasizes performance over clarity.
(setq use-package-minimum-reported-time (if my-debug 0 0.1))
(setq use-package-verbose my-debug)


;;; Load post-early-init.el
;; (load (expand-file-name "post-early-init.el" user-emacs-directory) :no-error :no-message)

;; (setq use-default-font-for-symbols nil)
;; (defvar my-fontset
;;   (create-fontset-from-fontset-spec standard-fontset-spec)
;;   "Standard fontset for user.")
;; ;;(add-to-list 'default-frame-alist (cons 'font my-fontset))
;; ;;(add-to-list 'initial-frame-alist (cons 'font my-fontset))
;; (set-fontset-font my-fontset 'latin
;;                   (font-spec :family "NotoSansM Nerd Font Mono")
;;                   nil 'prepend)
;; (set-fontset-font my-fontset 'unicode
;;                   (font-spec :family "NotoSansM Nerd Font Mono")
;;                   nil 'prepend)
;; (dolist (charset '(kana han cjk-misc hangul kanbun bopomofo))
;;     (set-fontset-font my-fontset charset
;;                       (font-spec :family "Noto Sans Mono CJK SC")
;;                       nil 'prepend))
;; (set-fontset-font my-fontset ?✿ (font-spec :family "Symbols Nerd Font Mono") nil 'prepend)

;; (add-to-list 'default-frame-alist '(font . my-fontset))



(provide 'early-init)

;; Local variables:
;; byte-compile-warnings: (not obsolete free-vars)
;; End:

;;; early-init.el ends here
