;; syd-strategies-lookup.el -*- lexical-binding: t; -*-

(require 'syd-strategies)
(use-package better-jumper)
(require 'better-jumper)

(defun syd-strat--run-lookup-strategy (strategy identifier origin)
  "Safely call a lookup STRATEGY.  A lookup strategy, if it is not an
interactive command, will be called with the lone argument IDENTIFIER.
Interactive or not, the procedure is expected to return nil on failure.  Returns
a marker if STRATEGY returns a buffer or marker, or nil on failure.
Modifications to the window configuration will be discarded if STRATEGY fails to
return a buffer or marker."
  (condition-case-unless-debug e
      (let ((wconf (current-window-configuration))
            (result (condition-case-unless-debug e
                        (if (commandp strategy)
                            (call-interactively strategy)
                          (funcall strategy identifier))
                      (error
                       (message "Lookup strategy %S threw an error: %s" strategy e)
                       'fail))))
        (cond ((eq result 'fail)
               (set-window-configuration wconf)
               nil)
              ((bufferp result)
               (with-current-buffer result
                 (point-marker)))
              ((or result
                   (null origin)
                   (/= (point-marker) origin))
               (prog1 (point-marker)
                 (set-window-configuration wconf)))))
    ((error user-error)
     (message "Lookup strategy %S: %s" strategy e)
     nil)))

(cl-defun syd-strat--lookup-and-jump-to
    (strategy-category identifier &key (display-fn #'switch-to-buffer))
  (let* ((origin (point-marker))
         (strategies (alist-get strategy-category syd-strat--strategies))
         ;; TODO: If called with a prefix argument, prompt the user to select a
         ;; strategy.
         (result (syd-strat-try-functions-wrapped
                  #'syd-strat--run-lookup-strategy
                  strategies identifier origin)))
    (unwind-protect
        (when (cond ((null result)
                     (message "No lookup strategy could find %S" identifier)
                     nil)
                    ((markerp result)
                     (funcall display-fn (marker-buffer result))
                     (goto-char result)
                     result)
                    (result))
          (with-current-buffer (marker-buffer origin)
            (better-jumper-set-jump (marker-position origin)))
          result))
    (set-marker origin nil)))

(defun syd-strat-lookup-documentation (identifier)
  (interactive (list (syd-thing-at-point-or-region)))
  (syd-strat--lookup-and-jump-to :documentation identifier
                                 :display-fn #'pop-to-buffer))

(defun syd-strat-lookup-definition (identifier)
  (interactive (list (syd-thing-at-point-or-region)))
  (syd-strat--lookup-and-jump-to :definition identifier))

(provide 'syd-strategies-lookup)
