;;; +link-hint.el -*- lexical-binding: t; -*-

;;; Code:

(require 'cl-lib)
(require 'link-hint)

(defgroup +link-hint nil
  "Extra customizations for `link-hint'."
  :group 'link-hint)

(defcustom +link-hint-open-secondary-types '(gnus-w3m-image-url
                                             gnus-w3m-url
                                             markdown-link
                                             mu4e-attachment
                                             mu4e-url
                                             notmuch-hello
                                             nov-link
                                             org-link
                                             shr-url
                                             text-url
                                             w3m-link
                                             w3m-message-link)
  "Link types to define `:open-secondary' for.")

(defvar +link-hint-map (make-sparse-keymap)
  "Keymap for `link-hint' functionality.")

(cl-defmacro +link-hint-define-keyword (keyword handler docstring
                                                &optional (types 'link-hint-types)
                                                &rest rest
                                                &key multiple &allow-other-keys)
  "Set up a `link-hint' KEYWORD, with optional TYPES.
If TYPES is not present, use `link-hint-types'.

KEYWORD defines the link-hint type.  It will be used to create a
function for opening links of the form \"link-hint-openKEYWORD\".

HANDLER is the function to open a link with.

DOCSTRING is the macro's documentation.

Keyword arguments are passed to `link-hint-define-type' prefixed
with the KEYWORD."
  (declare (indent 2)
           (doc-string 3))
  (let ((types (symbol-value types))
        (func-sym (intern (format "+link-hint-open%s" keyword)))
        (mult-sym (intern (format "%s-multiple" keyword)))
        (expr))
    ;; Define the type
    (push `(dolist (type ',types)
             (link-hint-define-type type
               ,keyword ,handler
               ,@(mapcar (lambda (el)
                           (if (eq el :multiple)
                               mult-sym
                             el))
                         rest)))
          expr)
    ;; Define an opener
    (push `(defun ,func-sym ()
             ,(format "%s\n\nDefined by `+link-hint-define'." docstring)
             (interactive)
             (avy-with link-hint-open-link
               (link-hint--one ,keyword)))
          expr)
    ;; Handle `:multiple'
    (when multiple
      (push `(defun ,(intern (format "+link-hint-open-multiple%s" keyword)) ()
               ,(format "Open multiple links with `%s'.\n\nDefined by `+link-hint-define'."
                        func-sym)
               (avy-with link-hint-open-multiple-links
                 (link-hint--multiple ,keyword)))
            expr)
      (push `(defun ,(intern (format "+link-hint-open-all%s" keyword)) ()
                 ,(format "Open all visible links with `%s'.\n\nDefined by `+link-hint-define'."
                          func-sym)
               (avy-with link-hint-open-all-links
                 (link-hint--all ,keyword)))
            expr))
    ;; Return the built expression
    `(progn ,@(nreverse expr))))

(+link-hint-define-keyword :secondary browse-url-secondary-browser-function
  "Open a link in the secondary browser."
  +link-hint-open-secondary-types
  :multiple t)

(defun +link-hint-open-secondary-setup (&optional types)
  "Define the `:open-secondary' link-hint type for TYPES.
If TYPES is nil, define it for `+link-hint-open-secondary-types'."
  (dolist (type (or types +link-hint-open-secondary-types))
    (link-hint-define-type type
      :open-secondary browse-url-secondary-browser-function
      :open-secondary-multiple t)))

(defun +link-hint-open-secondary ()
  "Open a link in the secondary browser."
  (interactive)
  (avy-with link-hint-open-link
    (link-hint--one :open-secondary)))

(defun +link-hint-open-chrome-setup (&optional types)
  "Define the `:open-chrome' link-hint type for TYPES.
If TYPES is nil, define it for `+link-hint-open-secondary-types'."
  (dolist (type (or types +link-hint-open-secondary-types))
    (link-hint-define-type type
      :open-chrome #'browse-url-chrome
      :open-chrome-multiple t)))

(defun +link-hint-open-chrome ()
  "Open a link with chrome."
  (interactive)
  (avy-with link-hint-open-link
    (link-hint--one :open-chrome)))

;; (cl-defmacro +link-hint-add-type (keyword )
;;   "Define link-hint type KEYWORD to operate on TYPES.
;; If TYPES is nil or absent, define KEYWORD for all
;; `link-hint-types'."
;;   (let (forms)
;;     (dolist (type (or types link-hint-types))
;;       (push `(link-hint-define-type ,type ,keyword ,function) forms))
;;     (push `(defun ,(intern (format "+link-hint%s" ,keyword))
;;                ))))

(defun +link-hint-open-link (prefix)
  "Open a link.
Without a PREFIX, open using `browse-url-browser-function'; with
a PREFIX, use `browse-url-secondary-browser-function'."
  (interactive "P")
  (avy-with link-hint-open-link
    (link-hint--one (if prefix :open-secondary :open))))

(defun +link-hint-open-multiple-links (prefix)
  "Open multiple links.
Without a PREFIX, open using `browse-url-browser-function'; with
a PREFIX, use `browse-url-secondary-browser-function'."
  (interactive "P")
  (avy-with link-hint-open-multiple-links
    (link-hint--one (if prefix :open-secondary :open))))

(defun +link-hint-open-all-links (prefix)
  "Open all visible links.
Without a PREFIX, open using `browse-url-browser-function'; with
a PREFIX, use `browse-url-secondary-browser-function'."
  (interactive "P")
  (avy-with link-hint-open-all-links
    (link-hint--one (if prefix :open-secondary :open))))

;;; Pocket-reader.el integration

(defun +link-hint-pocket-add-setup (&optional types)
  "Define the `:pocket-add' link-hint type for TYPES.
If TYPES is nil, define it for `link-hint-types'."
  (dolist (type (or types link-hint-types))
    (link-hint-define-type type
      :pocket-add #'pocket-reader-generic-add-link
      :pocket-add-multiple t)))

(defun +link-hint-pocket-add ()
  "Add a link to the Pocket reader."
  (interactive)
  (avy-with link-hint-open-link
    (link-hint--one :pocket-add)))

(provide '+link-hint)
;;; +link-hint.el ends here
