;;; empc.el --- A simple elisp wrapper for mpd and mpc.

;;; Commentary:

;; 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, version 3.

;; 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/>.

;; This is a single window, single playlist, no thrills, bare-bones
;; mpc client for EMACS.

;;; Code:

(define-derived-mode empc-mode fundamental-mode "Empc")
(defvar empc-buffer-name "*empc*")
(defvar empc-search-initial-input "")

(defun empc ()
 "Create a new empc buffer. If it already exists switch to it."
 (interactive)
 (if (get-buffer empc-buffer-name)
     (set-window-buffer (selected-window) empc-buffer-name)
   (empc-create-playlist)))

(defun empc-create-playlist ()
 "Create the playlist buffer and set it to empc-mode."
 (get-buffer-create empc-buffer-name)
 (set-window-buffer (selected-window) empc-buffer-name)
 (with-current-buffer empc-buffer-name
   (empc-mode)
   (insert (shell-command-to-string "mpc playlist"))
   (rectangle-number-lines (point-min) (- (point-max) 1) 1 "%-5d ")
   (empc-finalize-buffer)))

(defun empc-finalize-buffer ()
 "Set some final settings for the empc buffer."
 (read-only-mode t)
 (toggle-truncate-lines t)
 (hl-line-mode t)
 (goto-char (point-min))
 (message "Hello :3"))

(defun empc-revert-playlist ()
 "Reread the playlist."
 (interactive)
 (when (y-or-n-p "Revert playlist buffer?" )
     (kill-buffer "*empc*")
     (empc)))

(defun empc-update-database ()
 "Run a databse update and revert the playlist buffer."
 (interactive)
 (when (y-or-n-p "Run database update?" )
   (shell-command-to-string "mpc clear && mpc update --wait  && mpc add /")
   (empc-revert-playlist)))

(defun empc-play-highlighted ()
 "Play the currently highlighted song."
 (interactive)
 (let ((pos (number-to-string (line-number-at-pos))))
   (shell-command-to-string (concat "mpc -q play " pos))
   (message (concat "Playing #" pos))))

(defun empc-search-playlist ()
 "Provide a jump box to search for specific songs."
 (interactive)
 (let* ((playlist
         (split-string
          (with-current-buffer empc-buffer-name
            (buffer-string))
          "\n"))
        (song-id (completing-read "Song: " playlist nil t
                                  empc-search-initial-input)))
   (shell-command-to-string (concat "mpc -q play "
                                    (car (split-string song-id " "))))
   (empc-follow)))

(defun empc-follow ()
 "Recenter the currently played/paused song."
 (interactive)
 (goto-char (point-min))
 (forward-line (1- (string-to-number
                   (shell-command-to-string "mpc current -f %position%"))))
 (recenter))

(defmacro empc-generic-command (cmd quiet follow)
 "Macro to generate functions for simple mpc commands.
Call command CMD. If QUIET is non-nil print the mpc status
message. If FOLLOW is non-nil follow the currently playing song."
 `(defun ,(intern (concat "empc-" cmd)) ()
    "This function was generated by the empc-generic-command
    macro."
    (interactive)
    (shell-command (concat "mpc " (when ,quiet "-q ") ,cmd))
    (when ,follow (empc-follow))))

(defun empc-define-keymaps ()
 "Define keys in ‘empc-mode-map’."
 (setq empc-mode-map (make-sparse-keymap))
 (define-key empc-mode-map "\r" 'empc-play-highlighted)
 (define-key empc-mode-map "p" (empc-generic-command "toggle" t nil))
 (define-key empc-mode-map "n" (empc-generic-command "next" t t))
 (define-key empc-mode-map "b" (empc-generic-command "prev" t t))
 (define-key empc-mode-map "c" (empc-generic-command "seek +5" nil nil))
 (define-key empc-mode-map "x" (empc-generic-command "seek -5" nil nil))
 (define-key empc-mode-map "r" (empc-generic-command "random" nil nil))
 (define-key empc-mode-map "R" (empc-generic-command "repeat" nil nil))
 (define-key empc-mode-map "s" (empc-generic-command "single" nil nil))
 (define-key empc-mode-map "." (empc-generic-command "status" nil nil))
 (define-key empc-mode-map "+" (empc-generic-command "volume +5" nil nil))
 (define-key empc-mode-map "-" (empc-generic-command "volume -5" nil nil))
 (define-key empc-mode-map "f" 'empc-follow)
 (define-key empc-mode-map "g" 'empc-revert-playlist)
 (define-key empc-mode-map "U" 'empc-update-database)
 (define-key empc-mode-map "q" 'quit-window)
 (define-key empc-mode-map "j" 'empc-search-playlist))

(empc-define-keymaps)
(provide 'empc)

;;; empc.el ends here