* Create cache structure containing stop information
#+BEGIN_SRC emacs-lisp
(defclass imalison:muni-stop ()
  ((id :initarg :id)
   (name :initarg :name)
   (lat :initarg :lat)
   (long :initarg :long)))

(defmethod imalison:muni-stop-get-lat-long ((stop imalison:muni-stop))
  (list (oref stop :lat) (oref stop :long)))

(defvar imalison:muni-stop-cache (pcache-repository :object-name "imalison-muni-stop"))

(defun imalison:muni-get-gtfs-stops (&optional filepath)
  (unless filepath
    (setq filepath
          (concat (file-name-directory load-file-name) "stops.txt")))
  (let ((data (imalison:get-string-from-file filepath)))
    (parse-csv-string-rows data ?\, ?\" "\n")))

(defun imalison:build-muni-stop-from-gtfs-stop (gtfs-stop)
  (when (stringp (nth 3 gtfs-stop))
    (make-instance imalison:muni-stop
                   :id (nth 0 gtfs-stop)
                   :name (nth 1 gtfs-stop)
                   :lat (string-to-number (nth 3 gtfs-stop))
                   :long (string-to-number (nth 4 gtfs-stop)))))

(defun imalison:muni-populate-stop-cache (&optional filepath)
  (cl-loop for gtfs-stop in (imalison:muni-get-gtfs-stops filepath)
           do (let ((muni-stop
                     (imalison:build-muni-stop-from-gtfs-stop gtfs-stop)))
                (when muni-stop
                  (pcache-put imalison:muni-stop-cache
                              (intern (oref muni-stop :id)) muni-stop)))))

(defun imalison:muni-stop-from-id (stop-or-id)
  (if (imalison:muni-stop-p stop) stop-or-id
    (pcache-get imalison:muni-stop-cache stop-or-id)))
#+END_SRC
* Get route information
#+BEGIN_SRC emacs-lisp
(defun imalison:muni-get-route-ids (route-name &optional direction)
  (delete-dups
   (mapcar (imalison:compose-fn intern car)
           (s-match-strings-all
            "^\\([[:digit:]]\\{1,10\\}\\)"
            (shell-command-to-string
             (format "muni show %s %s -v" route-name (or direction "")))))))
#+END_SRC
* Stop selection functions
#+BEGIN_SRC emacs-lisp
(cl-defun imalison:closest-stop (stops &key location)
  (unless location (setq location (imalison:get-lat-long)))
  (let (minimizing (minimum 1.0e+INF))
    (cl-loop for stop in stops
             do (let* ((actual-stop (imalison:muni-stop-from-id stop))
                       (stop-lat-long (imalison:muni-stop-get-lat-long actual-stop))
                       (this-dist (imalison:haversine-distance location stop-lat-long)))
                  (when (< this-dist minimum)
                    (setq minimizing actual-stop
                          minimum this-dist))))
    minimizing))

(cl-defun imalison:muni-stops-within (stops &key (radius .25) current-location)
  (unless current-location (setq current-location (imalison:get-lat-long)))
  (cl-loop for stop in stops
           with actual-stop = nil
           when (let ((stop-lat-long (imalison:muni-stop-get-lat-long actual-stop)))
                  (setq actual-stop (imalison:muni-stop-from-id stop))
                  (< (imalison:haversine-distance current-location stop-lat-long) radius))
           collect actual-stop))
#+END_SRC
* A function that shells out to get upcoming trains
#+BEGIN_SRC emacs-lisp
(defun imalison:muni-predict (route direction stop)
  (s-trim
   (shell-command-to-string
    (format "muni predict %s %s %s" route direction stop))))
#+END_SRC

#+BEGIN_SRC emacs-lisp
(defun imalison:parse-muni-info (info-string)
  (when (string-match "\\([[:digit:]]\\{1,3\\}\\) *minutes" info-string)
    (match-string-no-properties 1 info-string)))
#+END_SRC
* A cached version of the muni functions for use in spaceline and elsewhere.
#+BEGIN_SRC emacs-lisp
(defvar imalison:muni-cache (pcache-repository :object-name "imalison-muni"))
(defvar imalison:current-location-ttl 10)
(defvar imalison:muni-arrival-ttl 25)

(defun imalison:get-cached-muni-time ()
  (let ((current-location (pcache-get imalison:muni-cache 'current-location))
        (arrival-time (pcache-get imalison:muni-cache 'arrival-time)))
    (unless arrival-time
      (unless current-location
        (setq current-location (imalison:get-lat-long))
        (pcache-put imalison:muni-cache 'current-location current-location
                    imalison:current-location-ttl))
      (setq arrival-time (imalison:parse-muni-info
                          (imalison:get-closest-muni-info current-location)))
      (pcache-put imalison:muni-cache 'arrival-time arrival-time
                  imalison:muni-arrival-ttl))
    arrival-time))
#+END_SRC
