;;; org-ref-reftex.el --- org-ref completion setup with reftex  -*- lexical-binding: t; -*-

;; Copyright (C) 2016  John Kitchin

;; Author: John Kitchin <jkitchin@andrew.cmu.edu>
;; Keywords:

;; 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 <http://www.gnu.org/licenses/>.

;;; Commentary: This is a bare-bones completion engine using only org-mode and
;;; vanilla Emacs functions. It is not being further developed.

;;

;;; Code:
(require 'reftex)
(require 'reftex-cite)
(require 'org-ref-utils)

(declare-function 'org-ref-find-bibliography "org-ref-core.el")
(declare-function 'org-ref-get-bibtex-key-and-file "org-ref-core.el")
(declare-function 'org-ref-bib-citation "org-ref-core.el")

(defvar org-ref-cite-types)
(defvar org-ref-open-notes-function)
(defvar org-ref-get-pdf-filename-function)
(defvar org-ref-open-pdf-function)

;;;###autoload
(defun org-ref-reftex-completion ()
  "Use reftex and org-mode for completion."
  (interactive)
  ;; Define core functions for org-ref
  (setq org-ref-insert-link-function 'org-ref-insert-cite-link
	org-ref-insert-cite-function 'org-ref-insert-cite-link
	org-ref-insert-label-function 'org-insert-link
	org-ref-insert-ref-function 'org-insert-link
	org-ref-cite-onclick-function 'org-ref-cite-onclick-minibuffer-menu)
  (message "reftex completion in org-ref loaded."))

(org-ref-reftex-completion)
(define-key org-mode-map
  (kbd org-ref-insert-cite-key)
  org-ref-insert-link-function)

;; Messages in the minbuffer conflict with the minibuffer menu. So we turn them
;; off.
(setq org-ref-show-citation-on-enter nil)

;;* org-mode / reftex setup
(defun org-mode-reftex-setup ()
  "Setup `org-mode' and reftex for `org-ref'."
  (and (buffer-file-name)
       (file-exists-p (buffer-file-name))
       (global-auto-revert-mode t))
  (make-local-variable 'reftex-cite-format)
  (setq reftex-cite-format 'org))

(add-hook 'org-mode-hook 'org-mode-reftex-setup)


(eval-after-load 'reftex-vars
  '(progn
     (add-to-list 'reftex-cite-format-builtin
                  '(org "Org-mode citation"
                        ((?\C-m . "cite:%l")     ; default
                         (?d . ",%l")            ; for appending
                         (?a . "autocite:%l")
                         (?t . "citet:%l")
                         (?T . "citet*:%l")
                         (?p . "citep:%l")
                         (?P . "citep*:%l")
                         (?h . "citeauthor:%l")
                         (?H . "citeauthor*:%l")
                         (?y . "citeyear:%l")
                         (?x . "citetext:%l")
                         (?n . "nocite:%l"))))))


(defun org-ref-insert-cite-link (alternative-cite)
  "Insert a default citation link using reftex.
If you are on a link, it appends to the end of the link,
otherwise, a new link is inserted.  Use a prefix
arg (ALTERNATIVE-CITE) to get a menu of citation types."
  (interactive "P")
  (org-ref-find-bibliography)
  (let* ((object (org-element-context))
         (link-string-end (org-element-property :end object)))

    (if (not alternative-cite)

        (cond
         ;; case where we are in a link
         ((and (equal (org-element-type object) 'link)
               (-contains? org-ref-cite-types
			   (org-element-property :type object)))
          (goto-char link-string-end)
          ;; sometimes there are spaces at the end of the link
          ;; this code moves point pack until no spaces are there
          (skip-chars-backward " ")
          (insert (concat "," (mapconcat
			       'identity
			       (reftex-citation t ?a) ","))))

         ;; We are next to a link, and we want to append
         ((save-excursion
            (backward-char)
            (and (equal (org-element-type (org-element-context)) 'link)
                 (-contains? org-ref-cite-types
                             (org-element-property
			      :type (org-element-context)))))
          (skip-chars-backward " ")
          (insert (concat "," (mapconcat
			       'identity
			       (reftex-citation t ?a) ","))))

         ;; insert fresh link
         (t
          (insert
           (concat org-ref-default-citation-link
                   ":"
                   (mapconcat 'identity (reftex-citation t) ",")))))

      ;; you pressed a C-u so we run this code
      (reftex-citation))))


;;;###autoload
(defun org-ref-open-notes-from-reftex ()
  "Call reftex, and open notes for selected entry."
  (interactive)
  ;; now look for entry in the notes file
  (if  org-ref-bibliography-notes
      (find-file-other-window org-ref-bibliography-notes)
    (error "org-ref-bibliography-notes is not set to anything"))

  (org-open-link-from-string
   (format "[[#%s]]" (car (reftex-citation t))))
  (funcall org-ref-open-notes-function))

(defalias 'ornr 'org-ref-open-notes-from-reftex)

;;*** Minibuffer menu
;;;###autoload
(defun org-ref-cite-onclick-minibuffer-menu (&optional _link-string)
  "Action when a cite link is clicked on.
Provides a menu of context sensitive actions.  If the bibtex entry
has a pdf, you get an option to open it.  If there is a doi, you
get a lot of options.  LINK-STRING is used by the link function."
  (interactive)
  (let* ((results (org-ref-get-bibtex-key-and-file))
         (key (car results))
         (pdf-file (funcall org-ref-get-pdf-filename-function key))
         (bibfile (cdr results))
         (url (save-excursion
                (with-temp-buffer
                  (insert-file-contents bibfile)
                  (bibtex-set-dialect (parsebib-find-bibtex-dialect) t)
                  (bibtex-search-entry key)
                  (bibtex-autokey-get-field "url"))))
         (doi (save-excursion
                (with-temp-buffer
                  (insert-file-contents bibfile)
                  (bibtex-set-dialect (parsebib-find-bibtex-dialect) t)
                  (bibtex-search-entry key)
                  ;; I like this better than bibtex-url which does not always find
                  ;; the urls
                  (bibtex-autokey-get-field "doi")))))

    (when (string= "" doi) (setq doi nil))
    (when (string= "" url) (setq url nil))
    (setq org-ref-cite-menu-funcs '())

    ;; open action
    (when
        bibfile
      (add-to-list
       'org-ref-cite-menu-funcs
       '("o" "pen" org-ref-open-citation-at-point)))

    ;; pdf
    (when (file-exists-p pdf-file)
      (add-to-list
       'org-ref-cite-menu-funcs
       `("p" "df" ,org-ref-open-pdf-function) t))

    ;; notes
    (add-to-list
     'org-ref-cite-menu-funcs
     '("n" "otes" org-ref-open-notes-at-point) t)

    ;; url
    (when (or url doi)
      (add-to-list
       'org-ref-cite-menu-funcs
       '("u" "rl" org-ref-open-url-at-point) t))

    ;; doi funcs
    (when doi
      (add-to-list
       'org-ref-cite-menu-funcs
       '("w" "os" org-ref-wos-at-point) t)

      (add-to-list
       'org-ref-cite-menu-funcs
       '("c" "iting" org-ref-wos-citing-at-point) t)

      (add-to-list
       'org-ref-cite-menu-funcs
       '("r" "elated" org-ref-wos-related-at-point) t)

      (add-to-list
       'org-ref-cite-menu-funcs
       '("g" "oogle scholar" org-ref-google-scholar-at-point) t)

      (add-to-list
       'org-ref-cite-menu-funcs
       '("P" "ubmed" org-ref-pubmed-at-point) t))

    ;; add user functions
    (dolist (tup org-ref-user-cite-menu-funcs)
      (add-to-list
       'org-ref-cite-menu-funcs
       tup t))

    ;; finally quit
    (add-to-list
     'org-ref-cite-menu-funcs
     '("q" "uit" (lambda ())) t)

    ;; now we make a menu
    ;; construct menu string as a message
    (message
     (concat
      (let* ((results (org-ref-get-bibtex-key-and-file))
             (key (car results))
             (bibfile (cdr results)))
        (save-excursion
          (with-temp-buffer
            (insert-file-contents bibfile)
            (bibtex-set-dialect (parsebib-find-bibtex-dialect) t)
            (bibtex-search-entry key)
            (org-ref-bib-citation))))
      "\n"
      (mapconcat
       (lambda (tup)
         (concat "[" (elt tup 0) "]"
                 (elt tup 1) " "))
       org-ref-cite-menu-funcs "")))
    ;; get the input
    (let* ((input (read-char-exclusive))
           (choice (assoc
                    (char-to-string input) org-ref-cite-menu-funcs)))
      ;; now run the function (2nd element in choice)
      (when choice
        (funcall
         (elt
          choice
          2))))))


(provide 'org-ref-reftex)
;;; org-ref-reftex.el ends here
