;;; ycp-org.el -- My config for org -*- lexical-binding: t -*-

;; Copyright (C) 2023 Free Software Foundation.

;; Author: Yuchen Pei <id@ypei.org>
;; Package-Requires: ((emacs "28.2"))

;; This file is part of dotted.

;; dotted is free software: you can redistribute it and/or modify it under
;; the terms of the GNU Affero General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; dotted 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 Affero General
;; Public License for more details.

;; You should have received a copy of the GNU Affero General Public
;; License along with dotted.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; My config for org.

;;; Code:



;;; the glorious org mode
(my-package org
  (my-keybind global-map
    "M-u" #'org-store-link
    "C-c a" #'org-agenda
    "C-c c" #'org-capture
    )
  (setq org-startup-folded 'overview)
  (my-override org-next-link)
  (my-override org-previous-link)
  (my-override org--mouse-open-at-point)
  (my-keybind org-mode-map
    "C-a" #'my-org-beginning-of-line-or-indent
    "C-k" #'my-org-kill-line
    "M-k" #'my-org-kill-line-backwards
    "M-l" #'org-insert-last-stored-link
    "M-n" #'org-next-link
    "M-p" #'org-previous-link
    "C-c C-l" #'org-insert-link
    )
  (if (string< (org-version) "9.6")
      (my-keybind org-mode-map
        "C-j" #'org-return-and-maybe-indent)
    (my-keybind org-mode-map
      "C-j" #'default-indent-new-line))
  (my-keybind minibuffer-mode-map "M-l" #'org-insert-last-stored-link)
  (my-setq-from-local my-org-common-properties org-directory
                      my-org-doc-dir)
  ;; disable auto-indent on RET
  (add-hook 'org-mode-hook (lambda () (electric-indent-local-mode -1)))
  ;; tab-width 8 is needed for newer versions of org-mode, which I am
  ;; not using due to performance issues
  (add-hook 'org-mode-hook (lambda () (setq-local tab-width 2)))

  ;; The world does not end by 2038 (hopefully)
  (setq org-read-date-force-compatible-dates nil)
  (setq org-adapt-indentation 'headline-data)
  (setq org-special-ctrl-a/e t)
  (setq org-special-ctrl-k t)
  (setq org-M-RET-may-split-line '((default . t)))
  (setq org-catch-invisible-edits 'error)
  (setq org-modules '(ol-bbdb ol-gnus ol-info))
  (setq org-use-sub-superscripts '{})
  (setq org-todo-keywords
        '((sequence "TODO(t)" "DOIN(i)" "WAIT(w)" "|"
                    "DONE(d)" "OBSO(o)" "DUPL(u)")))
  (setq org-closed-keep-when-no-todo t)
  (setq org-enforce-todo-dependencies t
        org-enforce-todo-checkbox-dependencies nil)
  (setq org-use-fast-todo-selection 'expert)
  (setq org-fontify-quote-and-verse-blocks t)
  (setq org-highlight-latex-and-related nil) ; other options affect elisp regexp
                                        ; in src blocks
  (setq org-log-done 'time)
  (setq org-archive-location "%s_archive::")
  (setq org-file-apps
        '((auto-mode . emacs)
	        (directory . emacs)
	        ("\\.mm\\'" . default)
	        ("\\.x?html?\\'" . emacs)
	        ("\\.pdf\\'" . emacs)
          ("\\.mp4\\'" . "xdg-open %s")))
  (setq org-structure-template-alist
        '(("a" . "export ascii")
          ("c" . "center")
          ("C" . "comment")
          ("e" . "example")
          ("E" . "export")
          ("h" . "export html")
          ("l" . "export latex")
          ("m" . "src emacs-lisp")
          ("q" . "quote")
          ("s" . "src")
          ("v" . "verse")))
  (setq org-use-tag-inheritance nil)
  (my-setq-from-local org-default-notes-file)
  (setq org-export-backends '(ascii beamer html latex md odt org))
  (setq org-export-with-sub-superscripts '{})
  (setq org-image-actual-width '(400))
  (setq org-log-into-drawer t)
  (plist-put org-format-latex-options :scale 1.5)
  (setq org-reverse-note-order t)
  (add-hook 'before-save-hook #'my-org-update-updated)
  ;; Add org mode line string, including `org-clock-get-clock-string'
  (setq frame-title-format
        '(multiple-frames "%b"
                          ("" "%b - GNU Emacs at " system-name
                           " " org-mode-line-string)))
  )

(my-package org-duration
  (my-setq-from-local org-duration-units))

(my-package org-goto
  (setq org-goto-interface 'outline-path-completion)
  )

(my-package org-element
  ;; org-persist is buggy with encoding etc. let's disable it
  (setq org-element-cache-persistent nil)
  )

;;; ox - org export
(my-package ox
  (:delay 60)
  (setq org-export-headline-levels 8)
  (require 'ox-html)
  (setq org-html-prefer-user-labels t)
  (setq org-html-self-link-headlines t)
  (require 'ox-icalendar)
  (setq org-icalendar-include-bbdb-anniversaries t)
  (setq org-icalendar-include-todo t)
  (setq org-icalendar-use-scheduled '(event-if-todo todo-start))
  )

(my-package org-id
  (setq org-id-link-to-org-use-id t)
  )

;;; cite
(my-package oc
  (:delay 60)
  (my-setq-from-local org-cite-global-bibliography)
  )

(my-package org-src
  (:delay 10)
  (setq org-edit-src-persistent-message nil)
  (my-keybind org-src-mode-map "C-c C-c" #'org-edit-src-exit)
  (setq org-src-window-setup 'current-window)
  (setq org-src-preserve-indentation t) ;; useful for yaml and python
  )

(my-package org-agenda
  (:delay 10)
  ;; We want to prevent switching not-done to done for items with
  ;; not-done children or checkboxes (see
  ;; `org-enforce-todo-dependencies' and
  ;; `org-enforce-todo-checkbox-dependencies' but we don't want them
  ;; marked as blocked, because blocked should be marked explicitly
  ;; and dimmed when they are truly blocked by a task somewhere else.
  (my-override org-entry-blocked-p)
  (my-keybind global-map "C-c g" 'my-org-store-agenda-view-A)
  (setq org-agenda-confirm-kill t)
  (setq org-agenda-follow-indirect t)
  (setq org-agenda-time-leading-zero t)
  (setq org-agenda-todo-ignore-time-comparison-use-seconds t)
  (setq org-agenda-todo-ignore-deadlines 'all)
  (setq org-agenda-todo-ignore-scheduled 'all)
  (setq org-agenda-todo-ignore-with-date 'all)
  (setq org-agenda-todo-ignore-timestamp 'all)
  (setq org-agenda-tags-todo-honor-ignore-options t)
  (setq org-agenda-dim-blocked-tasks t)
  (setq org-agenda-sticky t)
  (setq org-agenda-inhibit-startup t)
  (my-setq-from-local org-agenda-files)
  (setq org-agenda-skip-deadline-if-done nil)
  (setq org-agenda-skip-scheduled-if-done nil)
  (setq org-agenda-skip-timestamp-if-done t)
  (setq org-agenda-start-on-weekday nil)
  (setq org-agenda-custom-commands
        `(("A" "Agenda and next"
	         ((agenda ""
		                ((org-agenda-span 32)
                     ;; only works when `org-agenda-start-on-weekday'
                     ;; is nil
                     (org-agenda-start-day "-2d")))
	          (tags-todo "PRIORITY=\"A\"" nil))
	         nil
           ,(my-get-from-local my-org-agenda-and-next-export-files))
          ("B" "Agenda and context"
	         ((agenda "" ((org-agenda-span 'week)))
            (tags-todo "{^@.*}"))
	         nil)
	        ("@" "context todos"
	         ((tags-todo "{^@.*}"))
	         nil)))
  (setq org-agenda-prefix-format
        '((agenda . "%?-12t% s")
          (todo . "")
          (tags . "")
          (search . "")))
  (setq org-agenda-use-tag-inheritance nil)
  (add-hook 'org-agenda-mode-hook #'hl-line-mode)
  (add-hook 'org-agenda-after-show-hook 'my-org-agenda-after-show)
  (org-defkey org-agenda-mode-map "d" #'org-agenda-deadline)
  (org-defkey org-agenda-mode-map "s" #'org-agenda-schedule)
  ;; For whatever reason, `org-agenda-quit' deletes window instead of
  ;; burying the buffer when `org-agenda-window-setup' is not
  ;; 'current-window and `(one-window-p)' returns nil
  (org-defkey org-agenda-mode-map "q" #'bury-buffer)
  (setq org-agenda-window-setup 'other-window)
  )

(my-package ob-core
  (setq org-babel-load-languages '((emacs-lisp . t) (shell . t)))
  (setq org-confirm-babel-evaluate nil)
  )

(my-package org-capture
  (my-override org-capture-set-plist)
  (setq org-capture-templates
        `(("w" "Today's work" checkitem
           (file+headline org-default-notes-file "Today's work")
           ;; the :priority link prop requires overloading
           ;; `org-id-store-link' below to work
           "- [ ] [#%:priority] %a%?"
           :prepend t)
          ("j" "Journal" entry
	         (file+olp+datetree ,(my-get-from-local my-org-journal-file))
	         "* %?
:PROPERTIES:
:CREATED:  %U
:END:
")
	        ("t" "Todo" entry
	         (file+headline org-default-notes-file "Inbox")
           "* TODO %?
:PROPERTIES:
:CREATED:  %U
:Referral: %a
:END:

%(unless (string-empty-p \"%i\")
         (format
          \"#+begin_example
%s
#+end_example\"
          (org-escape-code-in-string \"%i\")))
"
	         :prepend t)
	        ("ya" "Blank audio" entry
	         (file+headline org-default-notes-file "Audios")
           nil
	         :prepend t)
	        ("book" "Blank books and papers" entry
	         (file+headline org-default-notes-file "Books and papers")
           nil
	         :prepend t)
	        ("video" "Blank videos" entry
	         (file+headline org-default-notes-file "Videos")
           nil
	         :prepend t)
	        ("entity" "Blank entities" entry
	         (file+headline org-default-notes-file "Entities")
           nil
	         :prepend t)
	        ("videogame" "Blank video games" entry
	         (file+headline org-default-notes-file "Video games")
           nil
	         :prepend t)
	        ("software" "Blank software" entry
	         (file+headline org-default-notes-file "Software")
           nil
	         :prepend t)
	        ("organisation" "Blank organisation" entry
	         (file+headline org-default-notes-file "Organisations")
           nil
	         :prepend t)
	        ("people" "Blank people" entry
	         (file+headline org-default-notes-file "People")
           nil
	         :prepend t)
	        ("game" "Blank games" entry
	         (file+headline org-default-notes-file "Games")
           nil
	         :prepend t)
	        ("location" "Blank location" entry
	         (file+headline org-default-notes-file "Locations")
           nil
	         :prepend t))))

(my-package org-clock
  (setq org-clock-history-length 100)
  (setq org-clock-in-switch-to-state "DOIN")
  (setq org-clock-idle-time 15)
  (setq org-clock-mode-line-total 'auto)
  (setq org-clock-persist 'history)
  (org-clock-persistence-insinuate))

(my-package org-refile
  (setq org-outline-path-complete-in-steps nil)
  (setq org-refile-allow-creating-parent-nodes 'confirm)
  (setq org-refile-targets '((org-agenda-files :maxlevel . 5)))
  (setq org-refile-use-cache t)
  (setq org-refile-use-outline-path t)
  )

;;; todo: some of these commands will take a while to be ready
(my-package org-keys
  (setq org-return-follows-link t)
  (setq org-use-speed-commands t)
  (setq org-speed-commands
        '(("User commands")
          ("." . my-org-task-add-id)
          ("'" . my-org-task-associate)
          ("!" . my-org-task-remove-id)
          ("\"" . my-org-task-dissociate)
          ("T" . my-org-swap-referral-with-headline)
          ("D" . my-org-clean-up-entry)
          ("g" . org-delete-property)
          ("W" . my-org-refile-logbook)
          ("+" . my-org-vote-up)
          ("-" . my-org-vote-down)
          ("m" . my-magit-clone-org-source)
	        ("c" . my-org-copy-property-value)
	        ("x" . my-org-osm-goto)
	        ("X" . my-osm-org-add-properties)
	        ("Y" . my-org-logbook-yank)
	        ("y" . my-grok-update-properties)
	        ("z" . my-org-orgzly-merge-link)
	        ("A" . org-attach)
	        ("P" . my-org-set-common-property)
	        ("N" . my-org-jump-to-last-visible-child)
	        ("d" . org-deadline)
	        ("s" . org-schedule)
	        ("S" . org-toggle-narrow-to-subtree)
	        (";" . org-timer-set-timer)
	        ("," . org-timer-pause-or-continue)
	        ("h" . my-org-entry-toggle-drawer-visibility)
	        ("Outline Navigation")
	        ("n" org-speed-move-safe 'org-next-visible-heading)
	        ("p" org-speed-move-safe 'org-previous-visible-heading)
	        ("f" org-speed-move-safe 'org-forward-heading-same-level)
	        ("b" org-speed-move-safe 'org-backward-heading-same-level)
	        ("F" . org-next-block)
	        ("B" . org-previous-block)
	        ("u" org-speed-move-safe 'outline-up-heading)
	        ("j" . org-goto)
	        ("g" org-refile
	         '(4))
	        ("Outline Visibility")
	        ("C" . org-shifttab)
	        (" " . org-display-outline-path)
	        ("s" . org-toggle-narrow-to-subtree)
	        ("k" . org-cut-subtree)
	        ("=" . org-columns)
	        ("Outline Structure Editing")
	        ("U" . org-metaup)
	        ("D" . org-metadown)
	        ("r" . org-metaright)
	        ("l" . org-metaleft)
	        ("R" . org-shiftmetaright)
	        ("L" . org-shiftmetaleft)
	        ("i" . my-org-append-subheading)
	        ("^" . org-sort)
	        ("w" . org-refile)
	        ("a" . org-archive-subtree-default-with-confirmation)
	        ("@" . org-mark-subtree)
	        ("#" . org-toggle-comment)
	        ("Clock Commands")
	        ("I" . org-clock-in)
	        ("O" . org-clock-out)
	        ("Meta Data Editing")
	        ("t" . org-todo)
	        ("," org-priority)
	        ("0" org-priority 32)
	        ("1" org-priority 65)
	        ("2" org-priority 66)
	        ("3" org-priority 67)
	        (":" . org-set-tags-command)
	        ("e" . org-set-effort)
	        ("E" . org-inc-effort)
	        ("W" lambda
	         (m)
	         (interactive "sMinutes before warning: ")
	         (org-entry-put
	          (point)
	          "APPT_WARNTIME" m))
	        ("Agenda Views etc")
	        ("v" . org-agenda)
	        ("/" . org-sparse-tree)
	        ("Misc")
	        ("o" . org-open-at-point)
	        ("?" . org-speed-command-help)
	        ("<" org-agenda-set-restriction-lock 'subtree)
	        (">" org-agenda-remove-restriction-lock)))
  )

;;;;
;; org attach
;;;;
(my-package org-attach
  (:delay 15)
  (setq org-attach-store-link-p 'attached)
  (setq org-attach-sync-delete-empty-dir t)
  (require 'my-org)
  (my-setq-from-local my-org-attach-copy-attached-targets)
  (add-to-list 'org-attach-commands '((?k) my-org-attach-copy-attached-docs
				                              "Copy attached docs."))
  (require 'my-scihub)
  (add-to-list 'org-attach-commands '((?p) my-org-attach-scihub
				                              "Download document from scihub."))
  (require 'my-calibre)
  (add-to-list 'org-attach-commands '((?C) org-attach-calibre-book
				                              "Attach from local calibre libray."))
  (add-to-list 'org-attach-commands '((?V) my-org-attach-all-url-plaintext
                                      "Fetch all urls to plaintext file"))
  (my-override org-attach-url)
  (add-to-list 'org-attach-commands '((?U) my-org-attach-url-plaintext
                                      "Fetch url to plaintext file"))
  (add-to-list 'org-attach-commands '((?A) my-org-attach-url-plaintext-all-media
                                      "Fetch url to plaintext file, and all media
                                    files therein."))
  )

;; org-protocol
(my-package org-protocol
  (:delay 30)
  (require 'my-org)
  (add-to-list 'org-protocol-protocol-alist
               '("grok"
                 :protocol "grok"
                 :function my-org-protocol-grok))
  (add-to-list 'org-protocol-protocol-alist
               '("browse-url"
                 :protocol "browse-url"
                 :function my-org-protocol-browse-url)))

;; org man links
(my-package ol-man
  (:delay 30)
  (setq org-man-command 'man))

(my-package ol
  (:delay 10)
  (require 'my-buffer)
  (advice-add 'org-gnus-no-new-news :before 'my-org-gnus-other-window-advice)
  (add-to-list 'org-link-frame-setup
               (cons 'file 'my-find-file-maybe-other-window))
  )

(my-package my-org
  (:delay 10)
  (my-keybind global-map
    "<f6>" #'my-org-open-or-cycle-notes)
  (my-keybind org-mode-map
    "C-c 1" #'my-org-insert-date-range
    "C-c ns" #'my-org-substitute-gnus-link-after-archiving
    "C-x w" #'my-org-copy-link-at-point
    "C-'" #'my-org-store-link-and-return
    "C-c M-w" #'my-org-copy-dwim
    "C-c <f7>" #'my-org-open-shell-at-attach-dir
    "C-M-n" #'my-org-next-block-or-results
    "C-M-p" #'my-org-previous-block-or-results)
  (add-hook 'org-follow-link-hook 'my-org-follow-link-after)
  ;; When in prog-mode, use line number as search item.
  (add-to-list 'org-create-file-search-functions
               'my-link-to-line-number-in-prog-mode)
  (my-override org-insert-all-links)
  (my-override org-open-at-point-global)
  (my-override org-refile-get-targets)
  (my-override org-insert-last-stored-link)
  (my-override org-id-store-link)
  (org-link-set-parameters "info" :follow #'my-org-info-open-new-window)
  (org-link-set-parameters "rt" :follow #'my-org-rt-open-new-window)
  (my-override org-src--make-source-overlay)
  (my-server-timer my-org-clock-save-timer
	                 nil 600 'my-org-clock-maybe-save)
  (my-server-idle-timer my-org-refile-rebuild-cache-timer
                        600 t 'my-org-refile-cache-rebuild)
  (my-server-idle-timer my-org-agenda-redo-all-timer
                        1200 t 'my-org-agenda-redo-all)
  (org-defkey org-agenda-mode-map "0" #'my-org-agenda-priority-0)
  (org-defkey org-agenda-mode-map "1" #'my-org-agenda-priority-A)
  (org-defkey org-agenda-mode-map "2" #'my-org-agenda-priority-B)
  (org-defkey org-agenda-mode-map "3" #'my-org-agenda-priority-C)
  (with-eval-after-load "org-capture"
    (advice-add
     'org-capture-place-template
	   :around 'my-org-capture-place-template-dont-delete-windows))
  (advice-add 'org-insert-structure-template :after 'my-org-edit-special)
  (advice-add 'org-edit-src-exit :before 'my-org-edit-src-before-exit)
  (advice-add 'org-edit-src-exit :after 'my-org-edit-src-after-exit)
  (advice-add 'org-edit-special :after 'my-org-edit-special-after)
  (my-setq-from-local my-org-task-categories))

(my-package my-org
  (:delay 30)
  (require 'my-web)
  (org-link-set-parameters "http" :follow
 			                     (lambda (url arg)
			                       (browse-url (concat "http:" url) arg)))
  (org-link-set-parameters "https" :follow
			                     (lambda (url arg)
			                       (browse-url (concat "http:" url) arg)))
  (require 'eww)
  (define-key eww-mode-map (kbd "C-'") 'my-eww-org-protocol-grok)
  )

(my-package ox-jira
  (:delay 60)
  (require 'my-ox-jira))

(my-package org-remark
  (:install t)
  (:delay 60)
  (require 'my-org-remark)
  (setq org-remark-notes-display-buffer-action
        '(display-buffer-reuse-mode-window))
  (require 'nov)
  (my-keybind nov-mode-map
    "M-n" #'org-remark-next
    "M-p" #'org-remark-prev
    "M" #'my-org-remark-open-or-create
    "o" #'org-remark-view
    "d" #'org-remark-delete)
  (with-eval-after-load 'nov
    (org-remark-nov-mode +1)))

(provide 'ycp-org)
;;; ycp-org.el ends here