;; -*- lexical-binding: t; -*-

(require 'syd/base)
(require 'syd/evil)

;; Optional Org-mode dependency used for highlighting source code in HTML
;; exports.
(use-package htmlize)

(defun syd-org-init-theme ()
  (require 'syd/kanagawa-palette)
  (let* ((hl `(:weight bold))
         (fg (lambda (c) `(:foreground ,(syd-kanagawa-get c))))
         (bg (lambda (c) `(:background ,(syd-kanagawa-get c))))
         (block-delim `(:foreground unspecified
                        :inherit font-lock-comment-face
                        :extend t
                        ,@(funcall bg 'sumi-ink-0)
                        :height 0.75))
         (keyword '(:background unspecified :foreground unspecified
                    :inherit (fixed-pitch font-lock-comment-face)
                    :height 0.9)))
    (setq org-src-block-faces
          `(("jupyter-apl" syd-apl)))
    (custom-theme-set-faces
     'user
     `(org-document-title ((t (,@hl :height 1.60))))
     `(org-document-info ((t (,@hl :height 1.0))))
     `(org-document-info-keyword ((t ,keyword)))
     `(org-meta-line ((t ,keyword)))
     `(org-table ((t (:inherit fixed-pitch))))
     `(org-level-1 ((t (,@hl :height 1.40 ,@(funcall fg 'oni-violet)))))
     `(org-level-2 ((t (,@hl :height 1.35 ,@(funcall fg 'crystal-blue)))))
     `(org-level-3 ((t (,@hl :height 1.30 ,@(funcall fg 'spring-violet-2)))))
     `(org-level-4 ((t (,@hl :height 1.25 ,@(funcall fg 'light-blue)))))
     `(org-level-5 ((t (,@hl :height 1.20 ,@(funcall fg 'wave-aqua-2)))))
     `(org-level-6 ((t (,@hl :height 1.15 ,@(funcall fg 'spring-green)))))
     `(org-level-7 ((t (,@hl :height 1.10 ,@(funcall fg 'boat-yellow-1)))))
     `(org-level-8 ((t (,@hl :height 1.05 ,@(funcall fg 'boat-yellow-2)))))
     `(org-block-begin-line ((t ,block-delim)))
     `(org-block-end-line ((t ,block-delim)))
     '(org-ellipsis ((t (:height 1.0))))
     ;; It is important that the `org-indent' face uses a fixed-pitch font, lest
     ;; e.g. multi-line bullets appear misaligned.
     '(org-indent ((t (:inherit (org-hide syd-alt-fixed-pitch)))))
     ;; Must be fixed-pitch; `[ ]` and `[X]' should be the same width.
     '(org-checkbox ((t (:inherit fixed-pitch))))
     `(org-drawer ((t ,block-delim)))
     '(org-property-value ((t (:inherit fixed-pitch))))
     '(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch)))))
     `(org-block ((t (:inherit fixed-pitch
                               ,@(funcall bg 'sumi-ink-2)))))
     `(org-quote ((t (:inherit (variable-pitch org-block)))))
     '(org-code ((t (:inherit (shadow fixed-pitch))))))))

(evil-define-command syd-org-yank-link (register)
  (interactive "<x>")
  (if-let* ((url (thing-at-point 'url)))
      (progn (evil-set-register (or register ?\") url)
             (message "Yanked link: %s" url))
    (message "No URL at point")))

(defun syd-org-init-keybinds ()
  (general-def
    :keymaps 'org-mode-map
    :states '(insert emacs)
    [C-M-return] #'org-insert-subheading
    "TAB" nil
    "<tab>" nil)
  (general-def
    :prefix-map 'syd-org-mode-links-map
    "l" #'org-insert-link
    "y" #'syd-org-yank-link)
  (general-define-key
   :keymaps 'org-mode-map
   :states '(normal visual motion emacs insert)
   :major-modes t
   :prefix syd-localleader-key
   :non-normal-prefix syd-alt-localleader-key
   "." #'consult-org-heading
   "/" #'consult-org-agenda
   "@" #'org-cite-insert
   "e" #'org-export-dispatch
   "f" #'org-footnote-action
   "h" #'org-toggle-heading
   "i" #'org-toggle-item
   "I" #'org-id-get-create
   "k" #'org-babel-remove-result
   "l" `("Links" . ,syd-org-mode-links-map)
   "t" #'org-todo
   "L L" #'syd-org-latex-preview-buffer
   "L r" #'syd-org-latex-preview-recompile-preamble
   "L c" #'org-latex-preview-clear-cache
   "s t" #'org-set-tags-command
   "s p" #'org-set-property
   "s d" #'org-deadline
   "s s" #'org-schedule
   "d t" #'org-timestamp
   "d T" #'org-timestamp-inactive
   "x" #'org-toggle-checkbox))

(use-package hide-mode-line
  :hook (org-mode . hide-mode-line-mode))

(defvar syd-org-default-css "
<style>
  html
  { height: 100%;
  }
  
  body
  { height: 100%
  ; padding: 0 10px
  ; line-height: 1.6
  ; font-size: 18px
  ; margin: 40px auto
  ; max-width: 650px
  ; color: #444
  }
  
  h1, h2, h3
  { line-height: 1.2
  }
  
  pre
  { line-height: normal
  }

  .org-svg
  { max-height: 100%
  ; max-width: 100%
  }

  .figure img
  { max-height: 100%
  ; max-width: 100%
  }
</style>"
  "A default style for Org HTML exports.")

(use-package org
  :defer-incrementally
  calendar find-func format-spec org-macs org-compat org-faces org-entities
  org-list org-pcomplete org-src org-footnote org-macro ob org org-agenda
  org-capture
  ;; We are using Tecosaur's fork starring the shiny new async LaTeX preview
  ;; engine.  Learn more here:
  ;; https://abode.karthinks.com/org-latex-preview/#org9054eb6
  :straight `(org
              :fork (:host nil
                     :repo "https://git.tecosaur.net/tec/org-mode.git"
                     :branch "dev"
                     :remote "tecosaur")
              :files (:defaults "etc")
              :build t
              :pre-build
              (with-temp-file "org-version.el"
                (require 'lisp-mnt)
                (let ((version
                       (with-temp-buffer
                         (insert-file-contents "lisp/org.el")
                         (lm-header "version")))
                      (git-version
                       (string-trim
                        (with-temp-buffer
                          (call-process "git" nil t nil "rev-parse" "--short" "HEAD")
                          (buffer-string)))))
                  (insert
                   (format "(defun org-release () \"The release version of Org.\" %S)\n" version)
                   (format "(defun org-git-version () \"The truncate git commit hash of Org mode.\" %S)\n" git-version)
                   "(provide 'org-version)\n")))
              :pin nil)
  :init
  ;; HACK: Face specs fed directly to `org-todo-keyword-faces' don't respect
  ;; underlying faces like the `org-todo' face does, so we define our own
  ;; intermediary faces that extend from org-todo.
  (with-no-warnings
    (custom-declare-face
     'syd-org-todo-active
     '((t (:inherit (bold font-lock-constant-face org-todo)))) "")
    (custom-declare-face
     'syd-org-todo-project
     '((t (:inherit (bold font-lock-doc-face org-todo)))) "")
    (custom-declare-face
     'syd-org-todo-onhold
     '((t (:inherit (bold warning org-todo)))) "")
    (custom-declare-face
     'syd-org-todo-cancel
     '((t (:inherit (bold error org-todo)))) ""))
  :custom ((org-startup-folded 'content)
           (org-directory "~/org")
           (org-refile-use-outline-path t)
           (org-tag-persistent-alist `(("orgmode" . ?o)
                                       ("hrt" . ?h)))
           ;; Upon finishing a task, leave a timestamp.
           (org-log-done 'time)
           (org-indirect-buffer-display 'current-window)
           ;; Force a TeX-like syntax for {sub,super}-scripts.  x^{blah blah}
           (org-use-sub-superscripts '{})
           (org-fontify-quote-and-verse-blocks t)
           (org-enforce-todo-dependencies t)
           (org-image-actual-width nil)
           (org-imenu-depth 6)
           (org-M-RET-may-split-line '((default . nil)))
           ;; Include some sane default CSS declarations when exporting to HTML.
           (org-html-head syd-org-default-css)
           ;; Don't right-align tags.
           (org-tags-column 0)
           (org-priority-faces '((?A . error)
                                 (?B . warning)
                                 (?C . success)))
           ;; Stay out of my config dir!
           (org-id-locations-file (file-name-concat
                                   syd-cache-dir "org-id-locations"))
           (org-cite-global-bibliography
            (list (expand-file-name "~/etc/liography.bib")))
           ;; New headings should be inserted /after/ the heading's contents.
           (org-insert-heading-respect-content t)
           ;; Hide markup syntax and leave the markup.
           (org-hide-emphasis-markers t)
           (org-ellipsis " […]")
           (org-todo-keywords
            '((sequence
               "TODO(t)"    ; A task that needs doing & is ready to do
               "PROJ(p)"    ; A project, which usually contains other tasks
               "STRT(s)"    ; A task that is in progress
               "WAIT(w)"    ; Something external is holding up this task
               "HOLD(h)"    ; This task is paused/on hold because of me
               "IDEA(i)"    ; An unconfirmed and unapproved task or notion
               "|"
               "DONE(d)"    ; Task successfully completed
               "KILL(k)"))) ; Task was cancelled, aborted, or is no longer
                                        ; applicable
           (org-todo-keyword-faces
            '(("[-]"  . syd-org-todo-active)
              ("STRT" . syd-org-todo-active)
              ("WAIT" . syd-org-todo-onhold)
              ("HOLD" . syd-org-todo-onhold)
              ("PROJ" . syd-org-todo-project)
              ("KILL" . syd-org-todo-cancel))))
  :preface
  ;; Speed up initialisation by disabling modules we don't need.
  (defvar org-modules
    '(;; ol-w3m
      ;; ol-bbdb
      ol-bibtex
      ;; ol-docview
      ;; ol-gnus
      ;; ol-info
      ;; ol-irc
      ;; ol-mhe
      ;; ol-rmail
      ;; ol-eww
      ))
  :config
  (require 'syd/prose)
  (syd-add-hook 'org-mode-hook
    #'syd-prose-mode)
  (syd-org-init-theme)
  (syd-org-init-keybinds))

(with-eval-after-load 'org
  (syd-add-hook 'org-tab-first-hook
    (defun syd-org-cycle-only-current-subtree-h (&optional arg)
      "Toggle the local fold at the point, and no deeper.  `org-cycle's standard
behavior is to cycle between three levels: collapsed, subtree and whole
document. This is slow, especially in larger org buffer. Most of the time I just
want to peek into the current subtree — at most, expand *only* the current
subtree.

All my (performant) foldings needs are met between this and `org-show-subtree'
(on zO for evil users), and `org-cycle' on shift-TAB if I need it."
      (interactive "P")
      (unless (or (eq this-command 'org-shifttab)
                  (and (bound-and-true-p org-cdlatex-mode)
                       (or (org-inside-LaTeX-fragment-p)
                           (org-inside-latex-macro-p))))
        (save-excursion
          (org-beginning-of-line)
          (let (invisible-p)
            (when (and (org-at-heading-p)
                       (or org-cycle-open-archived-trees
                           (not (member org-archive-tag (org-get-tags))))
                       (or (not arg)
                           (setq invisible-p
                                 (memq (get-char-property (line-end-position)
                                                          'invisible)
                                       '(outline org-fold-outline)))))
              (unless invisible-p
                (setq org-cycle-subtree-status 'subtree))
              (org-cycle-internal-local)
              t))))))

  (with-eval-after-load 'evil-surround
    (syd-add-hook '(org-mode-hook)
      (defun syd-org--configure-evil-surround-h ()
	(setq-local evil-surround-pairs-alist
		    (append '((?* . ("*" . "*"))
			      (?/ . ("/" . "/"))
			      (?= . ("=" . "=")))
			    evil-surround-pairs-alist))))))

(use-package evil-org
  :hook ((org-mode . evil-org-mode)
         (org-capture-mode . evil-insert-state))
  :straight (:type git :host github :repo "doomelpa/evil-org-mode")
  :init
  (defvar evil-org-retain-visual-state-on-shift t)
  (defvar evil-org-special-o/O '(table-row))
  (defvar evil-org-use-additional-insert t)
  :config
  (add-hook 'evil-org-mode-hook #'evil-normalize-keymaps)
  (evil-org-set-key-theme))

(use-package org-appear
  :hook (org-mode . org-appear-mode)
  :custom ((org-appear-autoemphasis t)
           (org-appear-auto-submarkers t)))

(provide 'syd/org)
