;;; init.el --- My Emacs Writing Studio init -*- lexical-binding: t; -*-
;; Time-stamp:
;;
;; Copyright (C) 2024-2025 Paul R. Jorgensen

;; Author: Paul R. Jorgensen <paul@prjorgensen.com>
;; Maintainer: Paul R. Jorgensen <paul@prjorgensen.com>
;; 
;; This file is NOT part of GNU Emacs.
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;
;; Based off of the Emacs Writing Studio init file:
;;    https://lucidmanager.org/tags/emacs, tangled from:
;;    documents/ews-book/99-appendix.org
;;
;;; Code:

;; Emacs 29 available?

(when (< emacs-major-version 29)
  (error "Emacs Writing Studio requires version 29 or later"))

;; Custom settings in a separate file and load the custom settings

(setq-default custom-file (expand-file-name
			     (concat system-name "-custom.el")
			     user-emacs-directory))

(load custom-file :no-error-if-file-is-missing)

;; Bind key for customising variables

(keymap-global-set "C-c w v" 'customize-variable)

;; Set package archives

(setq package-user-dir (concat user-emacs-directory emacs-version "/" system-configuration "/elpa") )

(use-package package
  :config
  (add-to-list 'package-archives
               '("melpa" . "https://melpa.org/packages/"))
  ;; (add-to-list 'package-archives
  ;; 	       '("ox-odt" . "https://kjambunathan.github.io/elpa/"))
  (package-initialize))

;; Fix upgrading in the built-in org-mode

(assq-delete-all 'org package--builtins)
(assq-delete-all 'org package--builtin-versions)

;; Package Management

(use-package use-package
  :custom
  (use-package-always-ensure t)
  (package-native-compile t)
  (warning-minimum-level :emergency)
  (package-install-upgrade-built-in t)
  )

;; Garbage collection; I don't obsess about this

(use-package gcmh)

;; get async all up in here

(use-package async
  :pin gnu
  :demand t
  :custom
  (message-send-mail-function 'async-smtpmail-send-it)
  (send-mail-function 'async-smtpmail-send-it)
  (async-bytecomp-package-mode t)
  (dired-async-mode t)
  )

;; I want smtpmail-async, but not until I do some email stuff

(with-eval-after-load 'smtpmail
  (require 'smtpmail-async)
)

;; and some authinfo

(use-package auth-source
  :ensure nil
  :custom
  (auth-source-debug t)
  (epg-gpg-program "gpg")
  (epg-pinentry-mode 'loopback)
  )

;; Fix upgrading in the built-in org-mode

(use-package org
  :pin gnu)

;; Fix upgrading in the built-in transient

(use-package transient
  :pin gnu)

;; Enable easymenu

(use-package easymenu
  :ensure nil)

;; Load EWS functions

(load-file (concat (file-name-as-directory user-emacs-directory)
		   "ews.el"))

;; Load the system environment
;; https://medium.com/really-learn-programming/emacs-on-macos-preserving-the-correct-environment-1735d2e8cb88
;; More efficient than exec-path-from-shell and similar

(maybe-refresh-env-file-based-on-system-name)

;; When emacs-mac updates to 30+ I can remove this

(unless (package-installed-p 'vc-use-package)
  (package-vc-install "https://github.com/slotThe/vc-use-package"))
(require 'vc-use-package)

;;; LOOK AND FEEL

(when (and (eq system-type 'darwin) (display-graphic-p))
  ;; (tool-bar-mode -1)
  (menu-bar-mode -1)
  (scroll-bar-mode -1)
  (setq
   tool-bar-style 'image
   )
)

;; Short answers only please
;; - moved to early-init.el

;; (setq-default use-short-answers t)

;; Spacious padding

(use-package spacious-padding
  :custom
  (line-spacing 3)
  :hook
  (after-init . spacious-padding-mode)
  )

;; Modus and EF Themes

(use-package modus-themes
  :custom
  (modus-themes-italic-constructs t)
  (modus-themes-bold-constructs t)
  (modus-themes-mixed-fonts t)
  (modus-themes-to-toggle '(modus-operandi-tinted
			    modus-vivendi-tinted))
  :bind
  (("C-c w t t" . modus-themes-toggle)
   ("C-c w t m" . modus-themes-select)
   ("C-c w t s" . consult-theme)))

(use-package ef-themes)

;; More font stuff
;; Mixed-pitch mode

(use-package mixed-pitch
  :hook
  (org-mode . mixed-pitch-mode))

;; set up filling in uncode characters. The order of the fallback font list
;;   is important

(use-package unicode-fonts
  :custom
  (unicode-fonts-fallback-font-list '("SF Pro Display" "Symbola" "Quivira"))
  :hook
  (after-init . unicode-fonts-setup))

;; from https://raw.githubusercontent.com/xenodium/dotsies/refs/heads/main/emacs/ar/sf.el

(use-package sf
  :load-path "site-lisp/sf/"
  :commands
  (
   sf-symbol-insert-name
   sf-symbol-insert
   )
  :bind
  ("C-x 8 s n" . sf-symbol-insert-name)
  ("C-x 8 s i" . sf-symbol-insert     )
  ("C-x 8 s s" . sf-symbol-insert     )
  (:map ctrl-x-map
	("8 s n" . sf-symbol-insert-name)
	("8 s i" . sf-symbol-insert     )
	("8 s s" . sf-symbol-insert     )
	)
  )

(with-eval-after-load 'unicode-fonts
  (set-fontset-font t nil "SF Pro Display" nil 'append)
)

(use-package hl-line)

;; Window management
;; Split windows sensibly

(setq split-width-threshold 120
      split-height-threshold nil)

;; Keep window sizes balanced

(use-package balanced-windows
  :config
  (balanced-windows-mode))

;; MINIBUFFER COMPLETION

;; Enable vertico

(use-package vertico
  :custom
  (vertico-sort-function 'vertico-sort-history-alpha)
  ;; (vertico-multiform-mode t)
  ;; (vertico-multiform-categories '(embark-keybinding grid))
  :hook
  (after-init . vertico-mode)
  )

(with-eval-after-load 'vertico
  (vertico-multiform-mode t)
  (add-to-list 'vertico-multiform-categories '(embark-keybinding grid))
 )

;; Persist history over Emacs restarts.

(use-package savehist
  :hook
  (after-init . savehist-mode)
  )

;; Search for partial matches in any order

(use-package orderless
  :custom
  (completion-styles '(orderless basic))
  (completion-category-defaults nil)
  (completion-category-overrides
   '((file (styles partial-completion)))))

;; Enable richer annotations using the Marginalia package

(use-package marginalia
  :after verico
  :custom
  (marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
  :hook
  (after-init . marginalia-mode)
  )

;; Improve keyboard shortcut discoverability

(use-package which-key
  :custom
  (which-key-max-description-length 40)
  (which-key-lighter nil)
  (which-key-sort-order 'which-key-description-order)
  :hook
  (after-init . which-key-mode)
  )

(use-package embark
  :after marginalia
  :bind
  (
   ("C-."   . embark-act     )  ;;pick some comfortable binding
   ("C-;"   . embark-dwim    )  ;; good alternative: M-.
   ("C-h B" . embark-bindings)  ;; alternative for `describe-bindings'
   )
  :hook
  (embark-collect-post-revert . resize-embark-collect-window)
  )

(with-eval-after-load 'marginalia
  ;; Optionally replace the key help with a completing-read interface
  (setq prefix-help-command #'embark-prefix-help-command)

  ;; Hide the mode line of the Embark live/completions buffers
  (add-to-list 'display-buffer-alist
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
                 nil
                 (window-parameters (mode-line-format . none))))
  )

(with-eval-after-load 'embark
  (setq embark-indicators
  '(embark-which-key-indicator
    embark-highlight-indicator
    embark-isearch-highlight-indicator))
  
  (advice-add #'embark-completing-read-prompter
            :around #'embark-hide-which-key-indicator)
  )

(use-package embark-consult
  :hook
  (embark-collect-mode . consult-preview-at-point-mode)
  )

(use-package emacs
  :custom
  ;; Support opening new minibuffers from inside existing minibuffers.
  (enable-recursive-minibuffers t)
  ;; Hide commands in M-x which do not work in the current mode.  Vertico
  ;; commands are hidden in normal buffers. This setting is useful beyond
  ;; Vertico.
  (read-extended-command-predicate #'command-completion-default-include-p)
  ;; Do not allow the cursor in the minibuffer prompt
  (minibuffer-prompt-properties
   '(read-only t cursor-intangible t face minibuffer-prompt))
  )

(use-package avy
  :bind
  (
   ("H-a"     . avy-goto-word-1)
   ("H-s"     . avy-goto-char-timer)
   ("M-g M-g" . avy-goto-line)
   ("M-g g"   . avy-goto-line)
   ("H-A"     . avy-goto-char)
   )
  :custom
  (avy-all-windows nil)
  )

(use-package casual
  :custom
  (casual-lib-use-unicode t)
  )

(use-package casual-suite
  :bind
  ("<f10>" . casual-editkit-main-tmenu)
  )

(use-package casual-avy
  :bind
  (
   ("M-g" . casual-avy-tmenu)
   )
  )

(use-package symbol-overlay)

(use-package casual-symbol-overlay)

;; Contextual menu with right mouse button

(when (display-graphic-p)
  (context-menu-mode))

;; Improved help buffers

(use-package helpful
  :bind
  (
   ("C-h f" . helpful-function)
   ("C-h x" . helpful-command)
   ("C-h k" . helpful-key)
   ("C-h v" . helpful-variable)
   )
  )

;;; Text mode settings

(use-package text-mode
  :ensure
  nil
  :hook
  (text-mode . visual-line-mode)
  :init
  (delete-selection-mode t)
  :custom
  (sentence-end-double-space nil)
  (scroll-error-top-bottom t)
  (save-interprogram-paste-before-kill t))

;; Check spelling with flyspell and hunspell

(use-package flyspell
  :defer t
  :custom
  (ispell-program-name "hunspell")
  (ispell-dictionary ews-hunspell-dictionaries)
  (flyspell-mark-duplications-flag nil) ;; Writegood mode does this
  (org-fold-core-style 'overlays) ;; Fix Org mode bug
  (flyspell-prog-text-faces '(font-lock-comment-face font-lock-doc-face))
  ;; :config
  ;; (ispell-set-spellchecker-params)
  ;; (ispell-hunspell-add-multi-dic ews-hunspell-dictionaries)
  :hook
  (text-mode . flyspell-mode)
  :bind
  (
   ("C-c w s s" . ispell)
   ("C-;"       . flyspell-auto-correct-previous-word)
   :map ctl-x-map
   ("C-i" . endless/ispell-word-then-abbrev) ; see
   )
  )

;;; Ricing Org mode

(use-package org
  :custom
  (org-fold-catch-invisible-edits 'error)
  (org-fold-catch-invisible-edits 'show)
  (org-hide-emphasis-markers       t)
  (org-id-link-to-org-use-id       t)
  (org-image-actual-width         '(450))
  (org-pretty-entities             t)
  (org-return-follows-links        t)
  (org-startup-indented            t)
  (org-startup-with-inline-images  t)
  (org-support-shift-select        t)
  (org-use-sub-superscripts       "{}")
  (org-modules
   '(ol-bbdb ol-bibtex org-crypt ol-docview ol-doi ol-eww ol-gnus org-habit ol-info org-inlinetask ol-irc ol-mhe org-mouse ol-rmail org-tempo ol-w3m ol-eshell))
  :hook
  (auto-save . org-save-all-org-buffers)
  :mode ("\\.txt\\'" . org-mode)
  )

(use-package org-protocol
  :ensure nil)

(use-package server
  :ensure nil
  :defer 1
  :custom
  (server-use-tcp t)
  (server-auth-dir (concat user-emacs-directory system-name "/server") )
  :config
  (server-start)
  )

;; Show hidden emphasis markers

(use-package org-appear
  :hook
  (org-mode . org-appear-mode))

;; LaTeX previews

(use-package org-fragtog
  :after org
  :hook
  (org-mode . org-fragtog-mode)
  :custom
  (org-startup-with-latex-preview nil)
  (org-format-latex-options
   (plist-put org-format-latex-options :scale 2)
   (plist-put org-format-latex-options :foreground 'auto)
   (plist-put org-format-latex-options :background 'auto)))

;; Org modern: Most features are disabled for beginning users

(use-package org-modern
  :hook
  (org-mode . org-modern-mode)
  :custom
  (org-modern-table nil)
  (org-modern-keyword nil)
  (org-modern-timestamp nil)
  (org-modern-priority nil)
  (org-modern-checkbox nil)
  (org-modern-tag nil)
  (org-modern-block-name nil)
  (org-modern-keyword nil)
  (org-modern-footnote nil)
  (org-modern-internal-target nil)
  (org-modern-radio-target nil)
  (org-modern-statistics nil)
  (org-modern-progress nil))

;; INSPIRATION

;; Doc-View

(use-package doc-view
  :custom
  (doc-view-resolution 300)
  (large-file-warning-threshold (* 50 (expt 2 20))))

;; Read ePub files

(use-package nov
  ;; :init
  ;; (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))
  :custom
  (nov-text-width t)
  :hook
  (nov-mode-hook . visual-line-mode)
  (nov-mode-hook . visual-fill-column-mode)
  :mode
  ("\\.epub\\'" . nov-mode)
  )

;; Managing Bibliographies

(use-package bibtex
  :custom
  (bibtex-user-optional-fields
   '(("keywords" "Keywords to describe the entry" "")
     ("file"     "Relative or absolute path to attachments" "" )))
  (bibtex-align-at-equal-sign t)
  :config
  (ews-bibtex-register)
  :bind
  (("C-c w b r" . ews-bibtex-register)))

;; Biblio package for adding BibTeX records

(use-package biblio
  :bind
  (("C-c w b b" . ews-bibtex-biblio-lookup)))

;; Citar to access bibliographies

(use-package citar
  :defer t
  :custom
  (citar-bibliography ews-bibtex-files)
  :bind
  (("C-c w b o" . citar-open)))

;; Read RSS feeds with Elfeed

(use-package elfeed
  :custom
  (elfeed-db-directory
   (expand-file-name "elfeed" user-emacs-directory))
  (elfeed-show-entry-switch 'display-buffer)
  :bind
  ("C-c w e" . elfeed))

;; Configure Elfeed with org mode

(use-package elfeed-org
  :config
  (elfeed-org)
  :custom
  (rmh-elfeed-org-files
   (list (concat (file-name-as-directory (getenv "HOME"))
		 "elfeed.org"))))

;; Easy insertion of weblinks

(use-package org-web-tools
  :bind
  (("C-c w w" . org-web-tools-insert-link-for-url)))

;; Emacs Multimedia System

(use-package emms
  :config
  (require 'emms-setup)
  (require 'emms-mpris)
  (emms-all)
  (emms-default-players)
  (emms-mpris-enable)
  :custom
  (emms-browser-covers #'emms-browser-cache-thumbnail-async)
  :bind
  (
   ("C-c w m b"       . emms-browser      )
   ("C-c w m e"       . emms              )
   ("C-c w m p"       . emms-play-playlist)
   ("<XF86AudioPrev>" . emms-previous     )
   ("<XF86AudioNext>" . emms-next         )
   ("<XF86AudioPlay>" . emms-pause        )
   )
  )

;; Open files with external applications

(use-package openwith
  :hook
  (after-init . openwith-mode)
  :custom
  (openwith-associations nil))

;; Fleeting notes

(use-package org-cliplink)

(use-package org
  :bind
  (("C-c c"   . org-capture)
   ("C-c C-l" . org-store-link)
   ("C-c l"   . org-cliplink)
   )
  :custom
  (org-goto-interface 'outline-path-completion)
  (org-capture-templates
   '(
     ("f" "Fleeting note"
      item
      (file+headline org-default-notes-file "Notes")
      "- %?")
     ("p" "Permanent note" plain
      (file denote-last-path)
      #'denote-org-capture
      :no-save t
      :immediate-finish nil
      :kill-buffer t
      :jump-to-captured t)
     ("t" "New task" entry
      (file+headline org-default-notes-file "Tasks")
      "* TODO %i%?")
     ("K" "Cliplink capture task" entry (file "")
      "* TODO %(org-cliplink-capture) \n  SCHEDULED: %t\n" :empty-lines 1)
     ("j" "Journelly" entry (file "/Users/paul/Library/Mobile Documents/iCloud~com~xenodium~Journelly/Documents/Journelly.org")
      "* %U @ %(journelly-generate-metadata)\n%?" :prepend t :eval t)
     ("a" "Captee Capture" entry
         (file+headline "~/org/captee.org" "Captee Captures")
         "* %:description\n%:annotation\n%i\n%?" :empty-lines 1)
     )))

(use-package journelly
  :load-path "site-lisp/journelly/"
  :commands
  (
   journelly-generate-metadata
   journelly-get-location
   journelly-fetch-weather
   journelly-resolve-metno-to-sf-symbol
   )
  )

(use-package separedit
  :custom
  (separedit-default-mode 'org-mode)
  :bind
  (
   :map prog-mode-map
	(("C-c '" . separedit))
   :map minibuffer-local-map
	(("C-c '" . separedit))
   :map help-mode-map
	(("C-c '" . separedit))
   :map helpful-mode-map
	(("C-c '" . separedit))
	)
  )

(use-package org
  :bind
  (
   :map ctl-x-map
        (("n"       . narrow-or-widen-dwim))
        :map org-mode-map
        (("A-|"     . narrow-or-widen-dwim))
        :map org-src-mode-map
        (
	 ("C-x C-s" . org-edit-src-exit   )
         ("A-|"     . narrow-or-widen-dwim)
         )
        )
  )

;; Denote

(use-package denote
  :defer t
  :custom
  (denote-sort-keywords t)
  (denote-link-description-function #'ews-denote-link-description-title-case)
  :hook
  (dired-mode . denote-dired-mode)
  :custom-face
  (denote-faces-link ((t (:slant italic))))
  :bind
  (
   ("C-c w d b" . denote-find-backlink)
   ("C-c w d d" . denote-date)
   ("C-c w d l" . denote-find-link)
   ("C-c w d i" . denote-link-or-create)
   ("C-c w d k" . denote-rename-file-keywords)
   ("C-c w d n" . denote)
   ("C-c w d r" . denote-rename-file)
   ("C-c w d R" . denote-rename-file-using-front-matter)
   )
  )

(use-package denote-org
  :after (org denote)
  :bind
  (("C-c w d h" . denote-org-link-to-heading)
   ("C-c w d s" . denote-org-extract-subtree)))

;; Consult convenience functions

(use-package consult
  :bind
  (
   ("s-f"     . consult-line-multi)
   ("C-c w h" . consult-org-heading)
   ("C-c w g" . consult-grep)
   )
  :config
  (add-to-list 'consult-preview-allowed-hooks 'visual-line-mode)
  )

;; Consult-Notes for easy access to notes

(use-package consult-notes
  :custom
  (consult-notes-denote-display-keywords-indicator "_")
  :bind
  (("C-c w d f" . consult-notes)
   ("C-c w d g" . consult-notes-search-in-all-notes))
  ;; :init
  ;; (consult-notes-denote-mode)
  :hook
  (after-init . consult-notes-denote-mode)
  )

;; Citar-Denote to manage literature notes

(use-package citar-denote
  :custom
  (citar-open-always-create-notes t)
  :hook
  (after-init . citar-denote-mode)
  :bind
  (("C-c w b c" . citar-create-note)
   ("C-c w b n" . citar-denote-open-note)
   ("C-c w b x" . citar-denote-nocite)
   :map org-mode-map
   ("C-c w b k" . citar-denote-add-citekey)
   ("C-c w b K" . citar-denote-remove-citekey)
   ("C-c w b d" . citar-denote-dwim)
   ("C-c w b e" . citar-denote-open-reference-entry)))

;; Explore and manage your Denote collection

(use-package denote-explore
  :bind
  (;; Statistics
   ("C-c w x c" . denote-explore-count-notes)
   ("C-c w x C" . denote-explore-count-keywords)
   ("C-c w x b" . denote-explore-barchart-keywords)
   ("C-c w x e" . denote-explore-barchart-filetypes)
   ;; Random walks
   ("C-c w x r" . denote-explore-random-note)
   ("C-c w x l" . denote-explore-random-link)
   ("C-c w x k" . denote-explore-random-keyword)
   ("C-c w x x" . denote-explore-random-regex)
   ;; Denote Janitor
   ("C-c w x d" . denote-explore-identify-duplicate-notes)
   ("C-c w x z" . denote-explore-zero-keywords)
   ("C-c w x s" . denote-explore-single-keywords)
   ("C-c w x o" . denote-explore-sort-keywords)
   ("C-c w x w" . denote-explore-rename-keyword)
   ;; Visualise denote
   ("C-c w x n" . denote-explore-network)
   ("C-c w x v" . denote-explore-network-regenerate)
   ("C-c w x D" . denote-explore-barchart-degree)))

;; Set some Org mode shortcuts

(use-package org
  :bind
  (:map org-mode-map
        ("C-c w n" . ews-org-insert-notes-drawer)
        ("C-c w p" . ews-org-insert-screenshot)
        ("C-c w c" . ews-org-count-words)))

;; Distraction-free writing

(use-package olivetti
  :demand t
  :bind
  (("C-c w o" . ews-olivetti)))

;; Undo Tree

(use-package undo-tree
  :hook
  (after-init . global-undo-tree-mode)
  :custom
  (undo-tree-auto-save-history nil)
  :bind
  (("C-c w u" . undo-tree-visualise)))

;; Export citations with Org Mode

(require 'oc-natbib)
(require 'oc-csl)

(setq org-cite-global-bibliography ews-bibtex-files
      org-cite-insert-processor 'citar
      org-cite-follow-processor 'citar
      org-cite-activate-processor 'citar)

;; Lookup words in online dictionaries

(use-package dictionary
  :custom
  (dictionary-server "dict.org")
  :bind
  (("C-c w s d" . dictionary-lookup-definition)))

(use-package osx-dictionary
  :if (memq window-system '(mac ns))
  :bind
  (("C-c d l" . osx-dictionary-search-word-at-point)
   ("C-c d i" . osx-dictionary-search-input)
   )
  )

(use-package powerthesaurus
  :bind
  (("C-c w s p" . powerthesaurus-transient)))

;; Lookup things on the internet with Duck Duck Go

(use-package google-this
  :custom
  (google-this-keybind nil)
  (google-this-browse-url-function 'eww-browse-url)
  (google-this-base-url "https://www.duckduckgo.")
  :bind
  (("C-x / g" . google-this))
  )

;; Writegood-Mode for weasel words, passive writing and repeated word detection

(use-package writegood-mode
  :bind
  (("C-c w s r" . writegood-reading-ease))
  :hook
  (text-mode . writegood-mode))

;; Titlecasing

(use-package titlecase
  :bind
  (("C-c w s t" . titlecase-dwim)
   ("C-c w s c" . ews-org-headings-titlecase)))

;; Abbreviations

(add-hook 'text-mode-hook 'abbrev-mode)

;; Lorem Ipsum generator

(use-package lorem-ipsum
  :custom
  (lorem-ipsum-list-bullet "- ") ;; Org mode bullets
  :init
  (setq lorem-ipsum-sentence-separator
        (if sentence-end-double-space "  " " "))
  :bind
  (("C-c w s i" . lorem-ipsum-insert-paragraphs)))

;; ediff

(use-package ediff
  :ensure nil
  :custom
  (ediff-keep-variants nil)
  (ediff-split-window-function 'split-window-horizontally)
  (ediff-window-setup-function 'ediff-setup-windows-plain))

;; Enable Other text modes

;; Fountain mode for writing scripts

(use-package fountain-mode)

;; Markdown mode

(use-package markdown-mode)

;; Quick ability to access accents without having to C-x 8

(use-package accent
  :bind
  (("C-x C-a" . accent-menu))
  )

;; PUBLICATION

;; Generic Org Export Settings

(use-package org
  :custom
  (org-export-with-drawers nil)
  (org-export-with-todo-keywords nil)
  (org-export-with-toc nil)
  (org-export-with-smart-quotes t)
  (org-export-date-timestamp-format "%e %B %Y"))

;; LibreOffice export

(use-package ox-odt
  :ensure nil
  :vc (:fetcher github :repo kjambunathan/org-mode-ox-odt)
  :custom
  (org-odt-preferred-output-format "docx")
  )

;; epub export

(use-package ox-epub
  :demand t
  :init
  (require 'ox-org))

;; Github Flavored Markdown exporter for Org Mode

(use-package ox-gfm
  :after org
  )

;; Slack export

(use-package ox-slack
  :after ox-gfm
  )

;; LaTeX PDF Export settings

(use-package ox-latex
  :ensure nil
  :demand t
  :custom
  ;; Multiple LaTeX passes for bibliographies
  (org-latex-pdf-process
   '("pdflatex -interaction nonstopmode -output-directory %o %f"
     "bibtex %b"
     "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
     "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
  ;; Clean temporary files after export
  (org-latex-logfiles-extensions
   (quote ("lof" "lot" "tex~" "aux" "idx" "log" "out"
           "toc" "nav" "snm" "vrb" "dvi" "fdb_latexmk"
           "blg" "brf" "fls" "entoc" "ps" "spl" "bbl"
           "tex" "bcf"))))

;; EWS paperback configuration

(with-eval-after-load 'ox-latex
  (add-to-list
   'org-latex-classes
   '("ews"
     "\\documentclass[11pt, twoside, hidelinks]{memoir}
      \\setstocksize{9.25in}{7.5in}
      \\settrimmedsize{\\stockheight}{\\stockwidth}{*}
      \\setlrmarginsandblock{1.5in}{1in}{*}
      \\setulmarginsandblock{1in}{1.5in}{*}
      \\checkandfixthelayout
      \\layout
      \\setcounter{tocdepth}{0}
      \\setsecnumdepth{subsection}
      \\renewcommand{\\baselinestretch}{1.2}
      \\setheadfoot{0.5in}{0.75in}
      \\setlength{\\footskip}{0.8in}
      \\chapterstyle{bianchi}
      \\renewcommand{\\beforechapskip}{-30pt}
      \\setsecheadstyle{\\normalfont \\raggedright \\textbf}
      \\setsubsecheadstyle{\\normalfont \\raggedright \\emph}
      \\setsubsubsecheadstyle{\\normalfont\\centering}
      \\pagestyle{myheadings}
      \\usepackage[font={small, it}]{caption}
      \\usepackage{ccicons}
      \\usepackage{ebgaramond}
      \\usepackage[authoryear]{natbib}
      \\bibliographystyle{apalike}
      \\usepackage{svg}
      \\hyphenation{mini-buffer}
      \\renewcommand{\\LaTeX}{LaTeX}
      \\renewcommand{\\TeX}{TeX}"
     ("\\chapter{%s}" . "\\chapter*{%s}")
     ("\\section{%s}" . "\\section*{%s}")
     ("\\subsection{%s}" . "\\subsection*{%s}")
     ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))))

;; htmlize stuff

(use-package htmlize)

(with-eval-after-load 'htmlize
  (setq xah-html-lang-mode-list (mapcar (lambda (x) (aref (cdr x) 0)) xah-html-lang-name-map))
  (setq htmlize-convert-nonascii-to-entities nil)
  (setq htmlize-html-charset "utf-8")
  (setq htmlize-untabify nil)
  )

;;; ADMINISTRATION

;; Bind org agenda command and custom agenda

(use-package org
  :custom
  (org-agenda-custom-commands
   '(("e" "Agenda, next actions and waiting"
      ((agenda "" ((org-agenda-overriding-header "Next three days:")
                   (org-agenda-span 3)
                   (org-agenda-start-on-weekday nil)))
       (todo "NEXT" ((org-agenda-overriding-header "Next Actions:")))
       (todo "WAIT" ((org-agenda-overriding-header "Waiting:")))))))
  :bind
  (
   ("C-c a" . org-agenda)
   :map org-agenda-mode-map
   (
    ("<f10>" . casual-agenda-tmenu)
    ("M-j"   . org-agenda-clock-goto)
    ("J"     . bookmark-jump)
    )
   )
  )

(use-package calendar
    :ensure nil
    :bind
    (:map calendar-mode-map
          ("<f10>" . casual-calendar)
	  )
    )

;; File MANAGEMENT

(use-package emacs
  :if (memq window-system '(mac ns))
  :custom
  (mac-system-move-file-to-trash-use-finder t)
  )

(use-package reveal-in-folder)		; works in multiple OS

(use-package dired
  :ensure nil
  :commands
  (dired dired-jump)
  :bind
  (
   :map dired-mode-map
	("<f10>" . casual-dired-tmenu)
	("s"     . casual-dired-sort-by-tmenu)
	("/"     . casual-dired-search-replace-tmenu)
	)
  :custom
  (dired-listing-switches
   "-goah --group-directories-first --time-style=long-iso")
  (dired-dwim-target t)
  (delete-by-moving-to-trash t)
  (dired-auto-revert-buffer #'dired-directory-changed-p)
  :init
  (put 'dired-find-alternate-file 'disabled nil)
  (when (and (eq system-type 'darwin) (executable-find "gls"))
    (setq insert-directory-program "gls"))
  :hook
  (context-menu-functions . casual-dired-context-menu-addons)
  (dired-mode             . context-menu-mode)
  (dired-mode             . hl-line-mode)
  (dired-mode             . dired-async-mode)
  (dired-mode             . (lambda ()
			      (setq-local mouse-1-click-follows-link 'double)))
)

(use-package dired-x
  :ensure nil)

(use-package wdired
  :ensure nil
  :custom
  (wdired-allow-to-change-permissions t)
  (wdired-create-parent-directories   t)
  )

;; Hide or display hidden files; also casual-dired

(use-package dired
  :ensure nil
  :hook (dired-mode . dired-omit-mode)
  :bind
  (:map dired-mode-map
        ( "."    . dired-omit-mode)
	)
  :custom (dired-omit-files "^\\.[a-zA-Z0-9]+"))

;; Backup files

(setq-default backup-directory-alist
              `(("." . ,(expand-file-name "backups/" user-emacs-directory)))
              version-control t
              delete-old-versions t
              create-lockfiles nil)

;; Recent files

(use-package recentf
  :hook
  (after-init . recentf-mode)
  :custom
  (recentf-max-saved-items 50)
  :bind
  (("C-c w r" . recentf-open)))

;; Bookmarks

(use-package bookmark
  :custom
  (bookmark-save-flag 1)
  :bind
  (
   ("C-x r d" . bookmark-delete)
   :map bookmark-bmenu-mode-map
   (
    ("<f10>" . casual-bookmarks-tmenu)
    ("J"     . bookmark-jump)
    )
   )
  :hook
  (bookmark-bmenu-mode    . hl-line-mode)
  (context-menu-functions . casual-bookmark-context-menu-addons)
  (bookmark-mode          . context-menu-mode)
  )

;; Image viewer

(use-package emacs
  :custom
  (image-dired-external-viewer "gimp")
  ;; Enable converting external formats (ie. webp) to internal ones.
  (image-use-external-converter t)
  :bind
  ((:map image-mode-map
         ("k" . image-kill-buffer)
         ("<right>" . image-next-file)
         ("<left>"  . image-previous-file)
	 ("<f10>"   . casual-image-tmenu)
	 )
   (:map dired-mode-map
         ("C-<return>" . image-dired-dired-display-external)
	 )
   )
  )

(use-package image-dired
  :bind
  (("C-c w I" . image-dired))
  (:map image-dired-thumbnail-mode-map
        ("C-<right>" . image-dired-display-next)
        ("C-<left>"  . image-dired-display-previous)
	)
  )

;; ADVANCED UNDOCUMENTED EXPORT SETTINGS FOR EWS

;; GraphViz for flow diagrams
;; requires GraphViz software
(org-babel-do-load-languages
 'org-babel-load-languages
 '((dot . t)))

;; My additional configuration

;; My few overriding default behavior keybindings
(use-package emacs
  :bind
  ("M-;"   . xah-comment-dwim)		; orig. comment-region
  ("C-z"   . undo            )		; orig. suspend-emacs
  ("C-x b" . consult-buffer  )		; orig. switch-to-buffer
  ("C-g"   . er-keyboard-quit) 		; orig. keyboard-quit
  )

;; Additional convenience mappings

(use-package emacs
  :bind
  ("A-s-c" . xah-copy-file-path)	; mimics ⌥-⌘-c MacOS function to pop path into the kill-ring
  ("M-o"   . other-window      )
  )

;; My Super (s-) maps
(use-package emacs
  :bind
  (
   ("C-s-SPC"   . nil                        ) ; blocked by Alfred for me; ns-do-show-character-palette in NS port
   ("M-s-h"     . nil                        ) ; ns-do-hide-others in NS port
   ("s-&"       . kill-current-buffer        )
   ("s-'"       . next-window-any-frame      )
   ("s-'"       . other-frame                )
   ("s-,"       . customize                  )
   ("s-0"       . text-scale-reset           )
   ("s-:"       . ispell                     )
   ("s-<"       . text-scale-decrease        )
   ("s-<down>"  . end-of-buffer              )
   ("s-<left>"  . backward-sentence          ) ; move-beginning-of-line in NS port
   ("s-<right>" . forward-sentence           ) ; move-end-of-line in NS port
   ("s-<up>"    . beginning-of-buffer        )
   ("s->"       . text-scale-increase        )
   ("s-?"       . info                       ) ; conflicts w/ MacOS
   ("s-C"       . nil                        ) ; conflicts w/ Named Keyboard Switcher; ns-popup-color-panel in NS port
   ("s-D"       . dired                      )
   ("s-E"       . edit-abbrevs               )
   ("s-H"       . nil                        ) ; ns-do-hide-others in NS port
   ("s-L"       . shell-command              )
   ("s-M"       . manual-entry               )
   ("s-S"       . write-file                 ) ; ns-write-file-using-panel in NS port
   ("s-Z"       . redo                       )
   ("s-a"       . mark-whole-buffer          )
   ("s-c"       . my-smart-copy-dwim         ) ; ns-copy-including-secondary in NS port
   ("s-d"       . nil                        ) ; isearch-repeat-backward in NS port
   ("s-e"       . isearch-yank-kill          ) ; blocked by my Keyboard Maestro macro top open Finder window
   ("s-f"       . consult-line-multi         ) ; isearch-forward in NS port
   ("s-g"       . er-keyboard-quit           )
   ("s-h"       . nil                        ) ; ns-do-hide-emacs in NS port
   ("s-j"       . exchange-point-and-mark    )
   ("s-k"       . kill-current-buffer        ) ; Xah has a better function for this
   ("s-l"       . goto-line                  )
   ("s-m"       . iconify-frame              )
   ("s-n"       . make-frame                 )
   ("s-o"       . mac-open-file-using-panel  ) ; ns-open-file-using-panel in NS port
   ("s-p"       . print-buffer               ) ; conflicts w/ MacOS print
   ("s-q"       . save-buffers-kill-emacs    ) ; TODO make this safer
   ("s-s"       . save-buffer                )
   ("s-t"       . menu-set-font              )
   ("s-u"       . revert-buffer              )
   ("s-v"       . xah-paste-or-paste-previous) ; yank in NS port
   ("s-w"       . xah-close-current-buffer   ) ; delete-frame in NS port
   ("s-x"       . my-smart-cut-dwim          )
   ("s-y"       . nil                        ) ; ns-paste-secondary on NS port
   ("s-z"       . undo                       )
   ("s-|"       . nil                        ) ; shell-command-on-region in NS port
   ("s-~"       . previous-frame             )
   :map org-mode-map
   (
    ("s-'"      . org-single-quote-region-or-point)
    ("s-+"      . cc/emphasize-strike-through     )
    ("s--"      . org-subscript-region-or-point   ) ; center-line in NS port
    ("s-<tab>"  . completion-at-point             )
    ("s-="      . cc/emphasize-verbatim           )
    ("s-C"      . cc/emphasize-code               )
    ("s-\""     . org-double-quote-region-or-point)
    ("s-^"      . org-superscript-region-or-point ) ; orig. kill-some-buffer
    ("s-_"      . cc/emphasize-underline          )
    ("s-b"      . cc/emphasize-bold               )
    ("s-e"      . cc/emphasize-dwim               )
    ("s-i"      . cc/emphasize-italic             )
    ("s-r"      . cc/emphasize-remove             )
    ("s-l"      . org-goto-line                   )
    )
   :map markdown-mode-map
   (
    ("s-<tab>"  . completion-at-point        )
    ("s-="      . cc/emphasize-verbatim      )
    ("s-C"      . cc/emphasize-code          )
    ("s-_"      . cc/emphasize-underline     )
    ("s-b"      . cc/emphasize-bold          )
    ("s-e"      . cc/emphasize-dwim          )
    ("s-i"      . cc/emphasize-italic        )
    ("s-s"      . cc/emphasize-strike-through)
    )
   )
  )

;; My Hyper (H-) maps
(use-package emacs
  :bind
  ("H-<backspace>" . delete-forward-char           )
  ("H-<down>"      . xah-end-of-line-or-block      ) ; page down
  ("H-<left>"      . move-beginning-of-line        ) ; home
  ("H-<right>"     . move-end-of-line              ) ; end
  ("H-<up>"        . xah-beginning-of-line-or-block) ; page up
  )

;; My Alt (A-) maps
;; These are exclusively org-mode and denote related
(use-package org
  :bind (
         ("A-a"     . org-agenda)
         ("A-c"     . org-capture)
         ("A-l"     . org-cliplink)
         ("A-S-l"   . org-insert-link-global)
         ("A-o"     . org-open-at-point-global)
         ("A-."     . org-time-stamp-inactive)
         ("M-<SPC>" . org-mark-ring-goto)
         :map org-mode-map
         (
	  ("<return>"    . scimax/org-return)
          ;; ("A-'"         . org-single-quote-region-or-point)
          ;; ("A-+"         . org-strikethrough-region-or-point)
          ;; ("A--"         . org-subscript-region-or-point)
          ("A-<return>"  . org-return) ; be able to do a standard org-return
          ;; ("A-="         . org-superscript-region-or-point)
          ;; ("A-\""        . org-double-quote-region-or-point)
          ("A-*"         . org-toggle-heading)
          ("A-<"         . org-insert-structure-template)
          ("A-."         . org-time-stamp-inactive)
          ("A-,"         . org-time-stamp)
          ("A-/"         . org-sparse-tree)
          ("A-<down>"    . org-move-subtree-down)
          ("A-<left>"    . org-promote-subtree)
          ("A-<right>"   . org-demote-subtree)
          ("A-<up>"      . org-move-subtree-up)
          ("A-@"         . org-mark-subtree)
          ("A-RET"       . org-return)	; when using Scimax's version
          ("A-S-<left>"  . org-do-promote)
          ("A-S-<right>" . org-do-demote)
          ("A-S-b"       . org-backward-heading-same-level)
          ("A-S-i"       . org-time-stamp-inactive)
          ("A-S-r"       . org-refile)
          ("A-S-s"       . org-save-all-org-buffers)
          ("A-S-u"       . outline-up-heading)
          ("A-^"         . org-sort)
          ("A-4"         . org-latex-math-region-or-point)
          ;; ("A-b"         . org-bold-region-or-point)
          ;; ("A-c"         . org-code-region-or-point)
          ("A-d"         . org-deadline)
          ;; ("A-e"         . ivy-insert-org-entity)
          ("A-f"         . org-forward-heading-same-level)
          ;; ("A-g"         . org-mac-grab-link)
          ;; ("A-i"         . org-italics-region-or-point)
          ("A-j"         . org-goto)
	  ("A-k"         . scimax-org-headline-to-inlinetask)
          ("A-N"         . org-add-note) ; add a note to the current entry's logbook
          ("A-n"         . org-next-visible-heading)
          ("A-p"         . org-previous-visible-heading)
          ;; ("A-q"         . counsel-org-tag)
          ("A-s"         . org-schedule)
          ("A-t"         . org-toggle-narrow-to-subtree)
          ;; ("A-u"         . org-underline-region-or-point)
          ;; ("A-v"         . org-verbatim-region-or-point)
          ("A-z"         . org-archive-subtree)
          ("A-¥"         . (lambda ()
                             "Enter a backslash on the MBA JP keyboard."
                             (interactive)
                             (insert "\\")
                             ))
          )
         )
  )

;; MacOS keyboard stuff
;; I have <CAPS> set up to send A-C-s for quick launching my-applications
(use-package emacs
  :if (memq window-system '(mac ns))
  :config
  ;; Left & right command ⌘ is (s)uper
  ;; Left & right option ⌥ is (A)lt
  ;; Esc ␛ is (M)eta (sticky-key)
  ;; Caps Lock ⇪ is F13 when pressed alone (sticky-key), s-M-A-S when in combination (set externally using karabiner-elements)
  ;; fn 🌐 is (H)yper unless the hardware or OS takes it
  ;; (C)ontrol ⌃, (S)hift ⇧ are as default
  (setq
   mac-command-modifier       'super
   mac-control-modifier       'control
   mac-function-modifier      'hyper
   mac-option-modifier        'alt
   mac-pass-command-to-system  nil	; default is t
   mac-pass-control-to-system  nil	; default is t; I want to keep C- maps to Emacs, so nil
   mac-right-command-modifier 'super
   mac-right-control-modifier 'left
   mac-right-option-modifier  'alt
   )

  ;; My MacOS app launcher
  
  (defvar my-applications
    '(("a" "DEVONagent.app"       "activate-devonagent")
      ("d" "DEVONthink 3.app"     "activate-devonthink")
      ("f" "Firefox.app"          "activate-firefox")
      ("g" "Beeper Desktop.app"   "activate-signal")
      ("i" "Messages.app"         "activate-imessage" "/System/Applications/")
      ("m" "Proton Mail.app"      "activate-protonmail")
      ("p" "Bitwarden.app"        "activate-bitwarden")
      ("s" "Safari.app"           "activate-safari")
      ("t" "iTerm.app"            "activate-iterm")
      ("v" "Vivaldi.app"          "activate-vivaldi")
      ("r" "Fiery Feeds.app"      "activate-rss"))
    "List of (key app-name function-name [optional app-path]).")

  (defun my-open-app (app-path)
    "Open application at APP-PATH using `open -a`."
    (shell-command (concat "/usr/bin/open -a \"" app-path "\"")))

  (dolist (app my-applications)
    (let* ((key (car app))
           (app-name (cadr app))
           (func-name (intern (nth 2 app)))
           (app-dir (or (nth 3 app) "/Applications/"))
           (full-path (concat app-dir app-name))
           (docstring (format "Launches %s from %s." app-name full-path)))
      (defalias func-name
        `(lambda ()
           ,docstring
           (interactive)
           (my-open-app ,full-path)))
      (global-set-key (kbd (format "A-C-s-%s" key)) func-name)))

  (defun describe-launch-keys ()
    "Display a list of custom app launch keybindings."
    (interactive)
    (with-help-window "*Launch Keybindings*"
      (princ "Custom Application Launch Keybindings (A-C-s-<key>):\n\n")
      (dolist (app my-applications)
        (let* ((key (car app))
               (app-name (cadr app)))
          (princ (format "  A-C-s-%s → %s\n" key app-name))))))
  )

;; MacOS Specific stuff

(use-package org-mac-link
  :if (memq window-system '(mac ns))
  ;; :demand t
  :load-path "~/plrjorg/config/emacs/elisp/"
  :bind (
         :map org-mode-map
              ("A-g" . org-mac-link-get-link)
              )
  )

(use-package org-devonthink
  :if (memq window-system '(mac ns))
  ;; :if (eq system-type 'darwin)
  :ensure nil
  ;; :defer t
  :load-path "~/plrjorg/config/emacs/elisp/"
  :commands (org-devonthink-open org-devonthink-get-link org-devonthink-set-link org-devonthink-insert-link org-devonthink-store-link org-devonthink-uuid-to-path org-devonthink-message-open)
  :bind
  ("A-x" . org-export-to-devonthink)
  )

(use-package mac-win
  :ensure nil
  )

;; Mouse scrolling

(use-package ultra-scroll
  :ensure nil
  :vc (:fetcher github :repo jdtsmith/ultra-scroll)
  :custom
  ;; (ultra-scroll-mode t)
  (scroll-conservatively 101)
  (scroll-margin 0)
  ;; :config
  ;; (setq scroll-conservatively 101 ; important!
  ;;       scroll-margin 0)
  :hook
  (after-init . ultra-scroll-mode)
  )

;; Casual stuff

(use-package calc
  :ensure nil
  :bind
  (:map calc-mode-map
        ("<f10>" . casual-calc-tmenu)
	)
  )

(use-package ibuffer
  :ensure nil
  :bind
  (:map ibuffer-mode-map
        ("<f10>" . casual-ibuffer-tmenu)
        ("F"     . casual-ibuffer-filter-tmenu)
        ("s"     . casual-ibuffer-sortby-tmenu)
        ("{"     . ibuffer-backwards-next-marked)
        ("}"     . ibuffer-forward-next-marked)
        ("["     . ibuffer-backward-filter-group)
        ("]"     . ibuffer-forward-filter-group)
        ("$"     . ibuffer-toggle-filter-group)
	("<double-mouse-1>" . ibuffer-visit-buffer)
	("M-<double-mouse-1>" . ibuffer-visit-buffer-other-window)
	)
  :hook
  (ibuffer-mode . hl-line-mode)
  (ibuffer-mode . ibuffer-auto-mode)
  )

(use-package info
  :ensure nil
  :bind
  (:map Info-mode-map
        ("<f10>" . casual-info-tmenu)
        ("M-["   . Info-history-back)
        ("M-]"   . Info-history-forward)
        ("/"     . Info-search)
        ("B"     . bookmark-set)
	("p"     . casual-info-browse-backward-paragraph)
	("n"     . casual-info-browse-forward-paragraph)
	("h"     . Info-prev)
	("j"     . Info-next-reference)
	("k"     . Info-prev-reference)
	("l"     . Info-next)
	)
  :hook
  (Info-mode-hook . hl-line-mode)
  (Info-mode-hook . scroll-lock-mode)
  )

(use-package isearch
  :ensure nil
  :bind
  (:map isearch-mode-map
        ("<f10>" . casual-isearch-tmenu)
	)
  )

(use-package re-builder
  :ensure nil
  :bind
  (:map reb-mode-map
        ("<f10>". casual-re-builder-tmenu)
	)
  (:map reb-lisp-mode-map
        ("<f10>" . casual-re-builder-tmenu)
	)
  )

;; DWIM Shell Commands

(use-package dwim-shell-command
  :bind (([remap shell-command] . dwim-shell-command)
         :map dired-mode-map
         ([remap dired-do-async-shell-command] . dwim-shell-command)
         ([remap dired-do-shell-command] . dwim-shell-command)
         ([remap dired-smart-shell-command] . dwim-shell-command))
  ;; :hook
  ;; (after-init . (require 'dwim-shell-commands))
)

(set-fontset-font t nil "SF Pro Display" nil 'append)