;;; gui-settings --- Summary

;;; Commentary:

;;; Requirements:
;; restart-emacs https://melpa.org/#/restart-emacs
;; dashboard https://melpa.org/#/dashboard
;;   page-break-lines https://melpa.org/#/page-break-lines
;;   all-the-icons https://melpa.org/#/all-the-icons
;;     memoize https://melpa.org/#/memoize
;; indent-guide https://melpa.org/#/indent-guide
;; not highlight-indent-guides
;; rainbow-mode https://elpa.gnu.org/packages/rainbow-mode.html
;; focus https://melpa.org/#/focus
;; virtual-auto-fill https://melpa.org/#/virtual-auto-fill

;;; Code:

(use-package emacs
  :delight
  (auto-fill-function "↵")
  (compilation-shell-minor-mode "Ⓒｓ")
  (eldoc-mode "Ⓔ")  ;; Ⓔ e
  (visual-line-mode "↩")  ;; " Wrap"
  (whitespace-mode "␣")   ;; "␣ _ ws"
  ;; major modes
  (calendar-mode "🅒ａｌ")  ;; "Calendar" "📆"
  (css-mode "🅒SS")  ;; "CSS"
  (emacs-lisp-mode "🅛ｅ")  ;; "EL 🅛⒠"
  (eshell-mode "🅢ｅ")  ;; "Esh 🅔⒮"
  (fundamental-mode "🅕")  ;; "F "  ;; not working
  (help-mode "🅗")  ;; "H "
  (Info-mode "🅘")  ;; "I "  ;; not working
  (latex-mode "🅛ａ")  ;; "LaTeX "
  (lisp-interaction-mode "🅛ｉ")  ;; "LI 🅛⒤"
  (messages-mode "🅜")  ;; "M "  ;; not working
  (messages-buffer-mode "🅜")  ;; "M "  ;; not working
  (inferior-python-mode "🅟ｉ")  ;; "IPy 🅟⒤"
  :hook
  (prog-mode . display-fill-column-indicator-mode)
  (help-mode . visual-line-mode)
  (messages-buffer-mode . visual-line-mode)
  :config
  (set-face-attribute 'fill-column-indicator nil :foreground "DarkSlateGray")  ;; inherit shadow

  ;; `scroll-bar-mode' sets for all frames and all windows
  ;; use `set-window-scroll-bars' for windows only
  (scroll-bar-mode 0)  ;; 1st deactivate scrolling
  (defun my-window-scroll-function (window display-start)
    "This function
- is listed in `window-scroll-functions' which is called by
  `set-window-buffer' before running `window-configuration-change-hook'
  and therefore will be run on every vertical scroll event
- will activate vertical scoll bar if not whole buffer height is
  visible

WINDOW: `window' object, its string will look like #<window 3 on gui-settings.el> and
DISPLAY-START: `integer', e.g. 3820"
    ;; (message "%s" window)
    (let ((vertical-type nil)
          (horizontal-type nil))
      (unless (string-equal (format-mode-line "%p") "All")
        (setq vertical-type 'right))
      ;; `scroll-bar-mode' not used bc/ it's global, therefore:
      (set-window-scroll-bars
       window nil vertical-type nil horizontal-type nil)))
  (add-to-list 'window-scroll-functions #'my-window-scroll-function)
  )  ;; end of use-package emacs

(use-package tab-bar
  :defer 0.5
  :config
  (tab-bar-mode)
  (tab-bar-rename-tab "Default")
  (defun my-tab-view-elisp ()
    (if (tab-bar--tab-index-by-name "ELisp IDE")
        (tab-bar-switch-to-tab "ELisp IDE")
      (tab-bar-new-tab)
      (tab-bar-rename-tab "ELisp IDE")
      (switch-to-buffer "*scratch*")
      (my-view-elisp)))
  (defun my-tab-view-python ()
    (if (tab-bar--tab-index-by-name "Python IDE")
        (tab-bar-switch-to-tab "Python IDE")
      (tab-bar-new-tab)
      (tab-bar-rename-tab "Python IDE")
      (if (get-buffer "*scratch-python*")
          (switch-to-buffer "*scratch-python*")
        (switch-to-buffer "*scratch-python*")
        (insert "#!/usr/bin/env python\n")
        (insert "\"\"\"\n")
        (insert "\"\"\"\n"))
      (python-mode)
      (my-view-python)))
  (defun my-tab-view-shell ()
    (if (tab-bar--tab-index-by-name "Shell IDE")
        (tab-bar-switch-to-tab "Shell IDE")
      (tab-bar-new-tab)
      (tab-bar-rename-tab "Shell IDE")
      (switch-to-buffer "*scratch-shell*")
      (shell-script-mode)
      (my-view-shell)))
  (defun my-tab-view-org-pdf ()
    (if (tab-bar--tab-index-by-name "Org PDF")
        (tab-bar-switch-to-tab "Org PDF")
      (tab-bar-new-tab)
      (tab-bar-rename-tab "Org PDF")
      (switch-to-buffer "*scratch-org*")
      (org-mode)
      (my-view-org-pdf)))
  (defun my-tab-view-gnuplot ()
    (if (tab-bar--tab-index-by-name "Gnuplot IDE")
        (tab-bar-switch-to-tab "Gnuplot IDE")
      (tab-bar-new-tab)
      (tab-bar-rename-tab "Gnuplot IDE")
      (switch-to-buffer "*scratch-gnuplot*")
      (gnuplot-mode)
      (my-view-gnuplot))))

(use-package awesome-tray
  :defer 0.2
  :config
  (setq awesome-tray-info-padding-right 1)
  (setq awesome-tray-buffer-name-buffer-changed t)
  (setq awesome-tray-mode-line-active-color "#008b8b")
  (setq awesome-tray-mode-line-inactive-color "#333333")
  ;; see available modules in `awesome-tray-module-alist'
  (defun my-awesome-tray-module-flycheck-info ()
    (string-trim (flycheck-mode-line-status-text)))
  (defface my-awesome-tray-module-flycheck-face
    (list (list t (list :foreground (face-foreground 'font-lock-warning-face))))
    "Flycheck module face."
    :group 'awesome-tray)
  (add-to-list 'awesome-tray-module-alist
    '("flycheck" . (my-awesome-tray-module-flycheck-info my-awesome-tray-module-flycheck-face)))
  (setq awesome-tray-active-modules  ;; circe parent-dir battery
    '("file-path"
      "buffer-name"
      "buffer-read-only"
      "location"
      "mode-name"
      "flycheck"
      "git"))
  (set-face-foreground 'awesome-tray-module-file-path-face (face-foreground 'font-lock-keyword-face))
  (set-face-foreground 'awesome-tray-module-buffer-name-face (face-foreground 'font-lock-constant-face))
  (set-face-foreground 'awesome-tray-module-location-face (face-foreground 'vertical-border))
  (set-face-foreground 'awesome-tray-module-mode-name-face (face-foreground 'font-lock-string-face))
  (defun my-awesome-tray-module-file-path-info-advice (&rest args)
    "If file path is same as buffer name omit output."
    (if (string= (car args) (awesome-tray-module-buffer-name-info))
        ""
      (car args)))
  (advice-add 'awesome-tray-module-file-path-info :filter-return #'my-awesome-tray-module-file-path-info-advice)
  (awesome-tray-mode 1))

(use-package doc-view
  :defer t
  :delight (doc-view-mode "🅓ｏｃ")  ;; "DocView"
  :config
  (setq doc-view-continuous t))

(use-package restart-emacs)

(use-package page-break-lines
  :delight (page-break-lines-mode "Ⓟｂ")
  :config
  (add-to-list 'page-break-lines-modes 'ledger-mode)
  (global-page-break-lines-mode))

(use-package memoize
  :defer t)

(use-package all-the-icons
  :load-path "lisp/all-the-icons"
  :defer t
  :config
  (when (and (not (my-font-installed-p "all-the-icons"))
             (window-system))
    (all-the-icons-install-fonts t)
    ;;(restart-emacs)  ;; infinity loop?
    ))

(use-package dashboard
  :load-path "lisp/dashboard"
  :delight (dashboard-mode "🅓")
  :config
  ;; because we use `use-package' include `use-package-statistics' if computed
  (defun my-dashboard-init-info ()
    (let ((package-count 0) (time (emacs-init-time)))
      (when (bound-and-true-p package-alist)
        (setq package-count (length package-activated-list)))
      (when (boundp 'straight--profile-cache)
        (setq package-count (+ (hash-table-size straight--profile-cache) package-count)))
      ;; added case
      (when use-package-compute-statistics
        (setq package-count (+ (hash-table-size use-package-statistics) package-count)))
      (if (zerop package-count)
          (format "Emacs started in %s" time)
        (format "%d packages loaded in %s" package-count time))))
  (setq dashboard-init-info (my-dashboard-init-info))
  ;; overwrite bc/ to recompute `dashboard-init-info' on refresh
  (defun dashboard-insert-init-info ()
    "Insert init info when `dashboard-set-init-info' is t."
    (when dashboard-set-init-info
      (setq dashboard-init-info (my-dashboard-init-info))  ;; ADDED
      (dashboard-center-line dashboard-init-info)
      (insert (propertize dashboard-init-info 'face 'font-lock-comment-face))))
  "Init info with packages loaded and init time."
  (setq dashboard-startup-banner 'logo)
  (setq dashboard-set-navigator t)
  ;; (setq dashboard-navigator-buttons  ;; Format: "(icon title help action face prefix suffix)"
  ;;       (list (list  ;; line1
  ;;              ;; "☆" "Star" "Show stars" (lambda (&rest _) (show-stars)) warning "[" "]")
  ;;              (list "?" "" "?/h" (lambda (&rest _) (describe-mode)) nil "<" ">")  ;;#'show-help
  ;;              )))
  (require 'all-the-icons)
  (defun dashboard-navigator-buttons-func ()
        ;; Format: "(icon title help action face prefix suffix)"
        `(( ;; views
           ("" "Custom Views:" "custom views" nil default "" "")
           (,(all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.1)
            "ELisp" "my-view-elisp" (lambda (&rest _) (my-tab-view-elisp)))
           (,(all-the-icons-alltheicon "python" :height 1.0 :v-adjust 0.0)
            "Python" "my-view-python" (lambda (&rest _) (my-tab-view-python)))
           (,(all-the-icons-alltheicon "script" :height 1.0 :v-adjust 0.0)
            "Shell" "my-view-shell" (lambda (&rest _) (my-tab-view-shell)))
           (,(all-the-icons-octicon "file-media" :height 1.0 :v-adjust 0.0)
            "Gnuplot" "my-view-gnuplot" (lambda (&rest _) (my-tab-view-gnuplot)))
           (,(all-the-icons-octicon "file-pdf" :height 1.0 :v-adjust 0.0)
            "Org PDF" "my-view-org-pdf" (lambda (&rest _) (my-tab-view-org-pdf)))
           )
          ( ;; major modes first line
           ("" "Major Modes:" "major modes" nil default "" "")
           (""
            "Deft" "deft" (lambda (&rest _) (deft)))
           (""
            "EShell" "eshell-mode" (lambda (&rest _) (eshell)))
           (""
            "Magit" "magit" (lambda (&rest _) (magit)))
           (,(all-the-icons-octicon "mail" :height 1.0 :v-adjust 0.0)
            "Mu4e" "mu4e" (lambda (&rest _) (mu4e)))
           (,(all-the-icons-octicon "mail" :height 1.0 :v-adjust 0.0)
            "Notmuch" "notmuch" (lambda (&rest _) (notmuch)))
           (""
            "Org-Brain" "org-brain-visualize" (lambda (&rest _) (call-interactively 'org-brain-visualize)))
           )
          ( ;; major modes second line
           (""
            "Org-Drill" "org-drill" (lambda (&rest _) (org-drill)))
           (""
            "Powershell" "powershell" (lambda (&rest _) (powershell)))
           (""
            "Shell" "shell" (lambda (&rest _) (shell)))
           (""
            "Treemacs" "treemacs" (lambda (&rest _) (treemacs)))
           )
          ( ;; line1
           ;; "☆" "Star" "Show stars" (lambda (&rest _) (show-stars)) warning "[" "]")
           (,(all-the-icons-material "help_outline" :height 1.1 :v-adjust -0.15)  ;; all-the-icons-octicon "question"
            "Help" "?/h" (lambda (&rest _) (describe-mode)) nil)  ;; #'show-help
           (,(all-the-icons-material "refresh" :height 1.1 :v-adjust -0.15)   ;; all-the-icons-octicon "sync"
            "Restart" "restart-emacs" (lambda (&rest _) (restart-emacs)) nil)
           ("" ,(concat "Config: " (my-dashboard-config-update)) "config" (lambda (&rest _) (progn (my-dashboard-config-update) (setq dashboard-navigator-buttons (dashboard-navigator-buttons-func)) (dashboard-refresh-buffer))) default "" "")
           )))
  (setq dashboard-navigator-buttons (dashboard-navigator-buttons-func))
  (setq dashboard-items '((recents  . 10)
                          (bookmarks . 5)
                          ;;(projects . 5)
                          ;;(agenda . 5)
                          (registers . 5)))
  
  ;;custom widget
  (defun my-widget-item (icon icon-face title title-face help action)
    (let ((action (or action #'ignore)))
      (widget-create 'item
                     :tag (concat
                           (propertize icon 'face `(:inherit
                                                    ,(get-text-property 0 'face icon)
                                                    :inherit
                                                    ,icon-face))
                           (propertize " " 'face 'variable-pitch)
                           (propertize title 'face title-face))
                     :help-echo help
                     :action action
                     :button-face `(:underline nil)
                     :mouse-face 'highlight
                     ;;:button-prefix (propertize "" 'face 'dashboard-navigator)
                     ;;:button-suffix (propertize "" 'face 'dashboard-navigator)
                     :format "%[%t%]")))
  ;; (defun dashboard-insert-custom (list-size)
  ;;   (dashboard-insert-shortcut "v" "Views" t)
  ;;   (when (display-graphic-p)
  ;;     (insert (all-the-icons-material "view_quilt" :height 1.6 :v-adjust -0.25
  ;;                                     :face 'dashboard-heading))
  ;;     (insert " "))
  ;;   (insert (propertize "Custom views:" 'face 'dashboard-heading))
  ;;   (insert " (v)")
  ;;   (insert "  ")
  ;;   (my-widget-item (all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.1)
  ;;                   'all-the-icons-purple "ELisp" 'default "my-view-elisp"
  ;;                   (lambda (&rest _) (my-view-elisp)))
  ;;   (insert " ")
  ;;   (my-widget-item (all-the-icons-alltheicon "python" :height 1.0 :v-adjust 0.0)
  ;;                   'all-the-icons-dblue "Python" 'default "my-view-python"
  ;;                   (lambda (&rest _) (my-view-python)))
  ;;   (insert " ")
  ;;   (my-widget-item (all-the-icons-octicon "mail" :height 1.0 :v-adjust 0.0)
  ;;                   'all-the-icons-dblue "Notmuch" 'default "notmuch"
  ;;                   (lambda (&rest _) (notmuch)))
  ;;   )
  ;; (add-to-list 'dashboard-item-generators  '(custom . dashboard-insert-custom) t)
  ;; see below add-to-list to dashboard-items

  ;; `clean-buffer-list'
  (defun my-buffer-name-list (&optional special internal)
    "If SPECIAL non-nil then include special buffers.
If INTERNAL non-nil then include internal buffers."
    (let ((b-list (mapcar (function buffer-name) (buffer-list)))
          new-list head)
      (while b-list
        (setq head (car b-list))
        (when (or internal   ;; check if no space is in the fron
                  (not (string= " " (substring head 0 1))))
          (when (or special  ;; check if no star are in the front and back
                    (and (not (string= "*" (substring head 0 1)))
                         (not (string= "*" (cl-subseq head -1)))))
            (push head new-list)))
        (setq b-list (cdr b-list)))
      (nreverse new-list)))
  (defun my-buffer-name-list-special ()
    "List special buffers."
    (let ((b-list (mapcar (function buffer-name) (buffer-list)))
          new-list head)
      (while b-list
        (setq head (car b-list))
        (when (and (string= "*" (substring head 0 1))
                   (string= "*" (cl-subseq head -1)))
          (push head new-list))
        (setq b-list (cdr b-list)))
      (nreverse new-list)))
  ;; overwrite with addition parameter to supress the logic to insert
  ;; a pre-defined heading icon. This is used to include own icon.
  (defun dashboard-insert-heading (heading &optional shortcut suppress-icon)
    "Insert a widget HEADING in dashboard buffer, adding SHORTCUT if provided."
    (when (and (display-graphic-p)
               dashboard-set-heading-icons)
      ;; Try loading `all-the-icons'
      (unless (or (fboundp 'all-the-icons-octicon)
                  (require 'all-the-icons nil 'noerror))
        (error "Package `all-the-icons' isn't installed"))

      (unless suppress-icon  ;; ADDED
      (insert (cond
               ((string-equal heading "Recent Files:")
                (all-the-icons-octicon (cdr (assoc 'recents dashboard-heading-icons))
                                       :height 1.2 :v-adjust 0.0 :face 'dashboard-heading))
               ((string-equal heading "Bookmarks:")
                (all-the-icons-octicon (cdr (assoc 'bookmarks dashboard-heading-icons))
                                       :height 1.2 :v-adjust 0.0 :face 'dashboard-heading))
               ((or (string-equal heading "Agenda for today:")
                    (string-equal heading "Agenda for the coming week:"))
                (all-the-icons-octicon (cdr (assoc 'agenda dashboard-heading-icons))
                                       :height 1.2 :v-adjust 0.0 :face 'dashboard-heading))
               ((string-equal heading "Registers:")
                (all-the-icons-octicon (cdr (assoc 'registers dashboard-heading-icons))
                                       :height 1.2 :v-adjust 0.0 :face 'dashboard-heading))
               ((string-equal heading "Projects:")
                (all-the-icons-octicon (cdr (assoc 'projects dashboard-heading-icons))
                                       :height 1.2 :v-adjust 0.0 :face 'dashboard-heading))
               (t " ")))
      )  ;; ADDED
      (insert " "))

    (insert (propertize heading 'face 'dashboard-heading))
    (if shortcut (insert (format " (%s)" shortcut))))
  ;; overwrite to supress the logic to insert a pre-defined heading
  ;; icon. This is used to include own icon.
  (defmacro my-dashboard-insert-section (section-name list list-size shortcut action &rest widget-params)
    "Add a section with SECTION-NAME and LIST of LIST-SIZE items to the dashboard.
SHORTCUT is the keyboard shortcut used to access the section.
ACTION is theaction taken when the user activates the widget button.
WIDGET-PARAMS are passed to the \"widget-create\" function."
    `(progn
       (dashboard-insert-heading ,section-name
                                 (if (and ,list dashboard-show-shortcuts) ,shortcut)
                                 t)  ;; ADDED
       (if ,list
           (when (dashboard-insert-section-list
                  ,section-name
                  (dashboard-subseq ,list 0 ,list-size)
                  ,action
                  ,@widget-params)
             (dashboard-insert-shortcut ,shortcut ,section-name))
         (insert "\n    --- No items ---"))))
  (defun dashboard-insert-buffers (list-size)
    "Add the list of LIST-SIZE items from buffers list.

See also `dashboard-insert-section'."
    (when (display-graphic-p)
      (insert (all-the-icons-octicon
               "versions"
               :height 1.2 :v-adjust 0.0 :face 'dashboard-heading)))
    (my-dashboard-insert-section
     "Special Buffers:"
     ;;(my-buffer-name-list)
     (my-buffer-name-list-special)
     list-size
     "b"
     `(lambda (&rest ignore) (switch-to-buffer ,el))
     (abbreviate-file-name el)))
  (add-to-list 'dashboard-item-generators  '(buffers . dashboard-insert-buffers) t)
  ;; see below add-to-list to dashboard-items
  
  (add-to-list 'dashboard-items '(buffers . 5) t)
  ;; (add-to-list 'dashboard-items '(custom) t)
  
  ;; needs package ‘all-the-icons’
  (setq dashboard-set-heading-icons t)
  (setq dashboard-set-file-icons t)
  (dashboard-setup-startup-hook))

(use-package indent-guide
  :delight (indent-guide-mode "Ⓘｇ")
  :hook (prog-mode . indent-guide-mode)  ;; problem if used in notmuch
  :config
  (set-face-attribute 'indent-guide-face nil :foreground "DarkSlateGray")  ;; foreground #535353
  ;;(setq indent-guide-char ":")
  (setq indent-guide-char "\u2502")
  (setq indent-guide-recursive t)  ;; NOT RECOMMENDED: To show not only one guide line but all guide lines recursively, set indent-guide-recursive non-nil.
  )

;; problem when using 'character in elisp it inserts the guide characters when inserting text before guide characters
;; (use-package highlight-indent-guides
;;   :delight (highlight-indent-guides-mode "Ⓘｇ")
;;   :hook (prog-mode . highlight-indent-guides-mode)
;;   :config
;;   (setq highlight-indent-guides-method 'character)  ;; 'fill 'character 'bitmap
;;   (setq highlight-indent-guides-character ?:)       ;; ?:
;;   (setq highlight-indent-guides-auto-enabled nil)   ;; deactivate auto colors
;;   (set-face-foreground 'highlight-indent-guides-character-face "gray30")
;;   )

(use-package rainbow-mode
  :delight (rainbow-mode "Ⓡ")  ;; " Rbow"
  :commands rainbow-mode)

(use-package focus
  :commands focus-mode
  :custom-face (focus-unfocused ((t :inherit shadow))))

(use-package iscroll
  :commands iscroll-mode
  ;; :hook ((special-mode text-mode) . iscroll-mode)
  )

(use-package virtual-auto-fill
  :delight (virtual-auto-fill-mode "Ⓥｆ")
  :commands virtual-auto-fill-mode
  ;;:hook (help-mode . virtual-auto-fill-mode)
  )

;; Custom functions/hooks for persisting/loading frame geometry upon save/load
(setq 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-left (frame-parameter (selected-frame) 'left))
        (frameg-top (frame-parameter (selected-frame) 'top))
        (frameg-width (frame-parameter (selected-frame) 'width))
        (frameg-height (frame-parameter (selected-frame) 'height))
        (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)
      (insert
       ";;; This file stores the previous emacs frame's geometry.\n"
       ";;; Last generated " (current-time-string) ".\n"
       "(setq initial-frame-alist\n"
       ;; " '((font . \"" frameg-font "\")\n"
       " '("
       (format " (top . %d)\n" (max frameg-top 0))
       (format " (left . %d)\n" (max frameg-left 0))
       (format " (width . %d)\n" (max frameg-width 0))
       (format " (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))))
;; Special work to do ONLY when there is a window system being used
(if (display-graphic-p)
    (progn
      (add-hook 'after-init-hook 'my-frame-geometry-load)
      (add-hook 'kill-emacs-hook 'my-frame-geometry-save)))

(provide 'gui-settings)
;;; gui-settings.el ends here
