;;; jao-pdf.el --- utilities for pdf files           -*- lexical-binding: t; -*-

;; Copyright (C) 2022, 2025  jao

;; Author: jao <mail@jao.io>
;; Keywords: docs

;; 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/>.

;;; Commentary:

;; Some niceties for PDFs:
;;
;;   - Using mutools, we can extract the outline of PDFs, and tell back the
;;     section title of a given page.
;;   - Interoperability with zathura.

(require 'jao-doc-session)

;;; PDF info

(declare-function 'pdf-info-outline "pdf-info")

(defvar-local jao-pdf--outline nil)

(defun jao-pdf-is-pdf-file (file)
  "Simply checks the FILE extension."
  (string-match-p ".*\\.pdf$" file))

(defun jao-pdf-title-to-file-name (title)
  "Convert a title, possibly with embedded spaces, to a PDF filename."
  (concat (mapconcat 'downcase (split-string title nil t) "-") ".pdf"))

(defun jao-pdf-title (&optional fname)
  (if (or fname (not (derived-mode-p 'doc-view-mode 'pdf-view-mode)))
      (let ((base (file-name-base (or fname (buffer-file-name)))))
        (capitalize (replace-regexp-in-string "-" " " base)))
    (or (jao-pdf-section-title)
        (when buffer-file-name (jao-pdf-title buffer-file-name)))))

(defvar-local jao-pdf--outline nil)

(defun jao-pdf-section-title (&optional page file-name)
  (when (not jao-pdf--outline)
    (setq-local jao-pdf--outline (doc-view--pdf-outline file-name)))
  (let ((page (or page
                  (and (derived-mode-p 'doc-view-mode) (doc-view-current-page))
                  (and (derived-mode-p 'pdf-view) (pdf-view-current-page))))
        (outline jao-pdf--outline)
        (cur-page 0)
        (cur-title (jao-pdf-title (or file-name buffer-file-name "title"))))
    (while (and (car outline) page (< cur-page page))
      (setq cur-page (cdr (assoc 'page (car outline))))
      (when (<= cur-page page)
        (setq cur-title (cdr (assoc 'title (car outline)))))
      (setq outline (cdr outline)))
    (replace-regexp-in-string "[[:blank:]]+" " " cur-title)))

;;; zathura interop
(defun jao-pdf-zathura-open-cmd (file page &optional suffix)
  (let ((page (if page (format "-P %s" page) "")))
    (format "zathura %s %s %s" file page (or suffix ""))))

(defun jao-pdf-zathura-title-rx (file)
  (concat (file-name-nondirectory file) " \\[.+\\]"))

;; e.g.  "~/org/doc/write-yourself-a-scheme-in-48-hours.pdf [96 (96/138)]"
(defun jao-pdf-zathura-file-info (title)
  (when (string-match "\\(.+\\) \\[\\(.+\\) (\\([0-9]+\\)/\\([0-9]+\\))\\]"
                      title)
    (list (expand-file-name (match-string 1 title))
          (string-to-number (match-string 3 title))
          (string-to-number (match-string 4 title))
          (match-string 2 title))))

(defun jao-pdf--zathura-link (info)
  (when-let* ((file (car info))
              (page (cadr info))
              (no (or (car (last info)) page))
              (fn (file-name-nondirectory file))
              (lnk (format "doc:%s::%s" fn page))
              (desc (format "%s (p. %s)" (jao-pdf-section-title page file) no)))
    (org-make-link-string lnk desc)))

(defun jao-pdf-zathura-org-link (title)
  (jao-pdf--zathura-link (jao-pdf-zathura-file-info title)))

(defun jao-zathura-open (file page)
  (let ((id (jao-x11-search-window (jao-pdf-zathura-title-rx file))))
    (if (string-blank-p id)
        (progn
          (when jao-xmonad-enabled (jao-x11-goto-ws 2))
          (jao-shell-exec (jao-pdf-zathura-open-cmd file page)))
      (let* ((page (if page (format " && xdotool type %dg" page) ""))
             (cmd (format "xdotool windowactivate %s%s" id page)))
        (jao-shell-exec cmd t)))))

;;; Mac

(when (eq system-type 'darwin)
  (require 'jao-mac)
  (defun jao-pdf-skim-org-link (title)
    (when-let* ((fp (jao-skim-current-doc))
                (file (file-name-nondirectory (car fp)))
                (page (cadr fp))
                (lnk (format "doc:%s::%s" file page)))
      (org-make-link-string lnk title)))

  (defun jao-pdf-insert-skim-org-link ()
    (interactive)
    (if-let* ((title (read-string "Title: "))
              (lnk (jao-pdf-skim-org-link title)))
        (insert lnk)
      (user-error "Skim is not viewing any docs!"))))

;;; Open doc functions
(defvar jao-pdf-open-in-emacs t)

(defun jao-find-or-open (file &optional page height)
  (cond ((and jao-pdf-open-in-emacs window-system)
         (let* ((buffs (buffer-list))
                (b (catch 'done
                     (while buffs
                       (when (string-equal (buffer-file-name (car buffs)) file)
                         (throw 'done (car buffs)))
                       (setq buffs (cdr buffs))))))
           (jao-afio-goto-docs)
           (if b (pop-to-buffer b) (find-file file))
           (when page (jao-doc-view-goto-page page height))))
        (jao-river-enabled (jao-river-open-with-zathura file page))
        (jao-sway-enabled (jao-sway-open-with-zathura file page))
        ((eq system-type 'darwin) (jao-mac-open-in-skim file page))
        (t (jao-zathura-open file page))))

(defun jao-open-doc (&optional file page height)
  (interactive)
  (when-let* ((file (or file
                        (read-file-name "Document: "
                                        (concat jao-org-dir "/doc/")))))
    (funcall jao-open-doc-fun file page height)))

(defun jao-select-pdf ()
  (interactive)
  (jao-buffer-same-mode '(pdf-view-mode doc-view-mode org-mode)
                        #'jao-afio-goto-docs))

(defun jao-open-with-zathura ()
  (interactive)
  (when-let* ((f buffer-file-name))
    (let ((p (jao-doc-view-current-page)))
      (cond (jao-river-enabled (jao-river-open-with-zathura f p))
            (jao-sway-enabled (jao-sway-open-with-zathura f p))
            (t (jao-zathura-open f p))))))

;;; doc:// links for browse-url

(defun jao-open-doc-url (url &rest _)
  (when (string-match "doc://\\([^?]+\\)\\(\\?.*\\)?" url)
    (let ((file (match-string 1 url))
          (page (when-let* ((qs (match-string 2 url))
                            (long (> (length qs) 1))
                            (ps (url-parse-query-string (substring qs 1)))
                            (pn (cadr (assoc "page" ps))))
                  (string-to-number pn))))
      (jao-open-doc (expand-file-name (concat "doc/" file) jao-org-dir) page))))

(add-to-list 'browse-url-handlers (cons "^doc://.+" 'jao-open-doc-url))


(provide 'jao-pdf)
;;; jao-pdf.el ends here
