;;; 05-custom-functions --- custom functions prefixed with "leo/"

;;; Commentary:

;;; Code:

(defun leo/compile-init ()
  "If the current buffer is 'init.el', compile it."
  (interactive)
  (when (equal (buffer-file-name)
               (expand-file-name (concat user-emacs-directory "init.el")))
    ;; Avoid running hooks when tangling.
    (let ((prog-mode-hook nil))
      (byte-compile-file (concat user-emacs-directory "init.el")))))

(defun leo/find-user-init-file ()
  "Edit the `~/.emacs.d/init.el' file."
  (interactive)
  (find-file "~/.emacs.d/init.el"))


(defun leo/tidy ()
  "Indent, untabify and unwhitespacify current buffer, or region if active."
  (interactive)
  (let ((beg (if (region-active-p) (region-beginning) (point-min)))
        (end (if (region-active-p) (region-end) (point-max))))
    (let ((inhibit-message t))
      (indent-region beg end))
    (whitespace-cleanup)
    (untabify beg (if (< end (point-max)) end (point-max)))
    (if (region-active-p) (message "Indenting Region...Done") (message "Indenting File...Done"))))

(defun leo/comment-or-uncomment-region-or-line ()
  "Comment or uncomment the region or the current line if there's no active region."
  (interactive)
  (let (beg end)
    (if (region-active-p)
        (setq beg (region-beginning) end (region-end))
      (setq beg (line-beginning-position) end (line-end-position)))
    (comment-or-uncomment-region beg end)))

(defun leo/duplicate-thing (comment)
  "Duplicates the current line, or the region if active.  If an argument (COMMENT) is given, the duplicated region will be commented out."
  (interactive "P")
  (save-excursion
    (let ((start (if (region-active-p) (region-beginning) (point-at-bol)))
          (end   (if (region-active-p) (region-end) (point-at-eol))))
      (goto-char end)
      (unless (region-active-p)
        (newline))
      (insert (buffer-substring start end))
      (when comment (comment-region start end)))))

(defun leo/kill-this-buffer-unless-scratch ()
  "Works like `kill-this-buffer' unless the current buffer is the *scratch* buffer.  In which case the buffer content is deleted and the buffer is buried."
  (interactive)
  (if (or (string= (buffer-name) "*dashboard*") (string= (buffer-name) "*scratch*"))
      (progn
        (bury-buffer (buffer-name))
        (switch-to-buffer (other-buffer)))
    (kill-this-buffer)))

(defun leo/jump-to-symbol (&optional backwardp)
  "Jumps to the next symbol near the point if such a symbol exists.  If BACKWARDP is non-nil it jumps backward."
  (let* ((point (point))
         (bounds (find-tag-default-bounds))
         (beg (car bounds)) (end (cdr bounds))
         (str (isearch-symbol-regexp (find-tag-default)))
         (search (if backwardp 'search-backward-regexp
                   'search-forward-regexp)))
    (goto-char (if backwardp beg end))
    (funcall search str nil t)
    (cond ((<= beg (point) end) (goto-char point))
          (backwardp (forward-char (- point beg)))
          (t  (backward-char (- end point))))))

(defun leo/jump-to-prev-symbol ()
  "Jumps to the previous occurrence of the symbol at point."
  (interactive)
  (leo/jump-to-symbol t))

(defun leo/jump-to-next-symbol ()
  "Jumps to the next occurrence of the symbol at point."
  (interactive)
  (leo/jump-to-symbol))

(defun leo/last-car (list)
  "Return only the item in the last position of a LIST."
  (car (last list))
  )
(defun leo/get-package-from-url (url)
  "Accept a URL and return a stripped package name."
  (let ((mode
         (car
          (split-string
           (leo/last-car (split-string url "/" t))
           "\\." nil)
          )
         ))
    (message mode))
  )

(defun leo/org-global-props (&optional property buffer)
  "Get the plists of global org PROPERTY of current BUFFER."
  (unless property (setq property "PROPERTY"))
  (with-current-buffer (or buffer (current-buffer))
    (org-element-map (org-element-parse-buffer) 'keyword (lambda (el) (when (string-match property (org-element-property :key el)) el)))))

(defun leo/org-global-prop-value (key)
  "Get global org property KEY (case sensitive) of current buffer."
  (org-element-property :value (car (leo/org-global-props key))))

(defun leo/deft-insert-boilerplate ()
  "Insert boilerplate into newly create roam note."
  (interactive)
  (let ((title (leo/org-global-prop-value "TITLE"))
        (setupfile (leo/org-global-prop-value "SETUPFILE"))
        (startup (leo/org-global-prop-value "STARTUP")))
    ;; If we have neither SETUPFILE nor STARTUP
    (when (and title
               (not setupfile)
               (not startup))
      (kill-region (point-min) (point-max))
      (goto-char (point-min))
      (insert (format "#+TITLE:      %s\n" title))
      (insert "#+SETUPFILE:  setup.org\n")
      (insert "\n")
      (insert "* Metadata:\n")
      (insert "** Tags: ")
      (goto-char (point-max)))

    ;; If we only have STARTUP
    (when (and title
               startup
               (not setupfile))
      (goto-char (point-min))
      (search-forward "STARTUP")
      (beginning-of-line)
      (kill-line)
      (insert "#+SETUPFILE:  setup.org")
      (message "Updated Global Properties"))
    ))

(defun leo/org-narrow-prev-tree ()
  "When in a narrowed region, this will take you to the previous heading and narrow."
  (interactive)
  (goto-char (point-min))
  (widen)
  (org-backward-heading-same-level 1)
  (org-narrow-to-subtree))

(defun leo/org-narrow-next-tree ()
  "When in a narrowed region, this will take you to the next heading and narrow."
  (interactive)
  (goto-char (point-min))
  (widen)
  (org-forward-heading-same-level 1)
  (org-narrow-to-subtree))

(defun leo/org-present ()
  "Begin an `org-mode' presentation."
  (interactive)
  (defvar old-mlf nil "Temp storage of mode-line-format while in 'present mode'.")
  (if (buffer-narrowed-p)
      (progn
        (widen)
        (setq mode-line-format old-mlf)
        (setq cursor-type 'box)
        (read-only-mode -1)
        (text-scale-adjust 0)
        (visual-line-mode nil)
        (toggle-word-wrap nil)
        (message "No longer presenting")
        )
    (setq old-mlf mode-line-format)
    (setq mode-line-format nil)
    (org-narrow-to-subtree)
    (setq cursor-type 'hbar)
    (read-only-mode 1)
    (text-scale-adjust 5)
    (visual-line-mode 1)
    (toggle-word-wrap 1)
    (message "Presenting")
    ))

(defun leo/org-roam-reformat-tags ()
  "Search for Tags: in document and replace the old single-line format with a newline separated list."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (if (re-search-forward "Tags:")
        (progn
          (replace-regexp "Tags: \\[" "Tags:\n- [" nil (point-at-bol) (point-at-eol))
          (replace-regexp "\\] \\[" "] \n- [" nil (point-at-bol) (point-at-eol))
          (message "Tags formatted!")
          )
      (message "Tags not found, format aborted")
      )))

(defun leo/org-table-slurp ()
  "Slurp a line below the table into current cell."
  (interactive)
  (save-excursion
    (forward-word)
    (beginning-of-line)
    (kill-line))
  (yank))

(defun leo/auth-source-unfunc (secret)
  "Unfurl the SECRET."
  (if (functionp secret)
      (funcall secret)
    secret))

(defun leo/url-encode (str)
  "Poor man's url-encode of STR."
  (replace-regexp-in-string "@" '"%40" str nil t))

(defun leo/salesforce-login (un)
  "Login to Salesforce with UN and password from auth-source."
  (interactive)
  (let ((secret (plist-get (nth 0 (auth-source-search :max 1 :user un)) :secret))
        (host (plist-get (nth 0 (auth-source-search :max 1 :user un)) :host)))
    (shell-command (format "sfdx force:org:open -u %s" un))
    ;; (browse-url (concat "https://" host "/login.jsp?pw=" (leo/auth-source-unfunc secret) "&un=" (leo/url-encode un)))
    ))

(defun leo/exec-process (cmd name &optional comint)
  "Execute CMD as a process in a buffer NAME, optionally passing COMINT as non-nil to put buffer in `comint-mode'."
  (let ((compilation-buffer-name-function
         (lambda (mode)
           (format "*%s*" name))))
    (message (concat "Running " cmd))
    (compile cmd comint)))


(provide '05-custom-functions)
;;; 05-custom-functions.el ends here
