   alist)
 
 
-(defun bbj:request (method &rest pairs)
+(defun bbj-request (method &rest pairs)
   "Poke netcat to poke the server who will hopefully poke us back"
   ;; json-false/json-nil are bound as nil here to stop them from being silly keywords
   (let (json message json-false json-null
         (data (list
-          (cons 'user bbj:user)
-          (cons 'auth_hash bbj:hash)
+          (cons 'user bbj-user)
+          (cons 'auth_hash bbj-hash)
           (cons 'method method))))
     ;; populate a query with our hash and username, then the func arguments
     (while pairs

        (match-beginning group)))))


(defun bbj:next-prop (prop &optional backward bound)
  "Like the `bbj:next-pos', but doesnt care about strings and
just hunts for a specific text property."
  (bbj:next-pos "." t prop backward nil bound))


(defun bbj:post-prop (prop &optional id)
  "retrieve PROP from the current post. needs ID-seeking support"
  (save-excursion
    (bbj:assert-post-start)
    (get-char-property (point) prop)))


;; returns positions of the next head and ending seps, respectively
(defun bbj:head-pos (&optional backward)
  (bbj:next-prop 'head backward))
(defun bbj:sep-pos (&optional backward)
  (bbj:next-prop 'end backward))


(defun bbj:assert-post-start ()
  (unless (eql 'head (get-char-property (point) 'type))
    (goto-char (bbj:head-pos t))))


(defun bbj:point-to-post (dir &optional nocenter)
  "Move the cursor from the head of one post to another, in (symbol) DIR"
  (let ((check (case dir
          (prev (bbj:head-pos t))
          (next (save-excursion ;; or else point will stick
                   (while (eq 'head (get-char-property (point) 'type))
                     (goto-char (next-property-change (point))))
                   (bbj:head-pos))))))
    (when check
      (goto-char check)
      (back-to-indentation)
      (unless nocenter (recenter 1)))))


(defun bbj:next-post ()
  (interactive)
  (bbj:point-to-post 'next))


(defun bbj:prev-post ()
  (interactive)
  (bbj:point-to-post 'prev))


(defun bbj:first-post ()
  ;; does interactive work like this? i never checked tbh
  (interactive (push-mark))
  (goto-char (+ 1 bbj:width (point-min))))


(defun bbj:aux ()
  "just some random lazy callback shitty thing for C-c C-c"
  (interactive)
  (funcall bbj:aux-callback))


(defun bbj:enter ()
  "Handles the RETURN key (and other similar binds) depending on
content type. Currently only opens threads."
  (interactive)
  (case bbj:buffer-type
    (index
     (bbj:enter-thread
      (alist-get 'thread_id (bbj:post-prop 'data))))))


(defun bbj:quote-current-post ()
  "Pop a composer, and insert the post number at point as a quote."
  (interactive)
  (case bbj:buffer-type
    (thread
     (let ((id (alist-get 'post_id (bbj:post-prop 'data))))
       (bbj:compose)
       (insert (format ">>%s\n\n" id))))
    (index
     ;; recursion haha yes
     (let ((buffer (current-buffer)))
       (bbj:enter)
       (unless (equal buffer (current-buffer))
         (bbj:quote-current-post))))))


(defun bbj:compose ()
  "Construct an appropriate callback to either create a thread or
reply to one. Pops a new window; window is killed and the message
is sent using C-c C-c."
  (interactive)
  (let ((params (case bbj:buffer-type
     (index
      `("Composing a new thread (C-c C-c to send)"
        (lambda ()
          (let* ((message (bbj:consume-window (current-buffer)))
                 (request (bbj:request "thread_create"
                   'body message
                   'title ,(read-from-minibuffer "(Thread Title)> ")
                   'tags ,(read-from-minibuffer "(Comma-seperated tags, if any)> "))))
            (if (numberp (bbj:descend request 'error 'code))
                (message "%s" request)
              (message "thread submitted")
              (bbj:browse-index))))))
     (thread
      `("Replying to thread (C-c C-c to send)"
        (lambda ()
          (let* ((message (bbj:consume-window (current-buffer)))
                 (request (bbj:request "thread_reply"
                            'body message 'thread_id ,thread-id)))
            (if (numberp (bbj:descend request 'error 'code))
                (message "%s" request)
              (message "reply submitted")
              (bbj:enter-thread ,thread-id)
              (goto-char (point-max))
              (bbj:point-to-post 'prev)
              (recenter nil)))))))))

    (apply #'bbj:compose-in-window params)))


(defun bbj:compose-in-window (title callback &rest cbargs)
  "Create a new buffer, pop it, set TITLE as the header line, and
assign CALLBACK to C-c C-c."
  (let ((buffer (get-buffer-create "*BBJ: Compose*")))

    (insert (propertize
      (concat "~" (alist-get 'name userdata) " ")
      'face 'font-lock-keyword-face))
    (insert (if (eq bbj-buffer-type 'index)
        (propertize (format "@ %s\n%s replies; last active %s\n"
            (bbj-timestring (alist-get 'created object))
            (alist-get 'reply_count object)
            (bbj-timestring (alist-get 'lastmod object)))
          'face 'font-lock-comment-face)
      (propertize (format "@ %s\n\n" (bbj-timestring (alist-get 'created object)))
        'face 'font-lock-comment-face)))
    (when (eq bbj-buffer-type 'thread)
        (bbj-render-body (alist-get 'body object)))
    (bbj-insert-sep)))


;; (defun bbj-render-tag-span (dom)
;;   "A slightly modified version of `shr-tag-span' which handles quotes and stuff."
;;   (let ((class (if bbj-old-p
;;                    (assq :class (cdr dom))
;;                    (dom-attr dom 'class))))
;;     (dolist (sub (if (consp (car dom)) (cddr (car dom)) (cddr dom)))
