;;; emacs-modules.el -- Fetch and Install Emacs Modules
;;
;;; Copyright (C) 2008 Kyle W T Sherman
;;
;; Author:   Kyle W T Sherman <kylewsherman at gmail dot com>
;; Created:  2008-02-16
;; Version:  1.0
;; Keywords: module install
;;
;; This file is not part of GNU Emacs.
;;
;; This 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; either version 2, or (at your option) any later version.
;;
;; This 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 GNU Emacs; see the file COPYING.  If not, write to the Free Software
;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
;;
;;; Commentary:
;;
;; Provides `emacs-modules' function that searches for and displays known
;; emacs modules that when selected are downloaded and installed.
;;
;;; Installation:
;;
;; Put `emacs-modules.el' where you keep your elisp files and add
;; something like the following to your .emacs file:
;;
;;   (require 'emacs-modules)
;;
;;; Usage:
;;
;; Call `emacs-modules' with a module name, or with no parameter for a
;; list to pick from.
;;
;; Example:
;;
;;   (emacs-modules "ibuffer")

;;; TODO Outline:
;; - write background fetching apps to get lists of available el packages for download
;; - check list versions against installed versions and highlight ones that are outdated
;; - put this one at the top of the list if it is outdated
;; - list modules from local saved data file
;; - background loader updates saved data file
;; - store code to download, unpack, install, compile, etc in lisp data object
;;   (list), so no new functions need to be made/altered with new package
;;   logic
;; - after loading a new version of this modules, you should be able to exec
;;   it and run the main function

;;; Code:

;; w3m
(require 'w3m)

;; customize group
(defgroup emacs-modules nil
  "Fetch and install Emacs modules."
  :prefix "emacs-modules-"
  :group 'applications)

;; display buffer name
(defvar emacs-modules-buffer-name
  "*Emacs-Modules*"
  "Buffer name to use for displaying and selecting emacs modules to install.")

;; elisp home
(defcustom emacs-modules-elisp-home
  (expand-file-name "~/.emacs.d/modules")
  "Location to store fetched Elisp files."
  :type 'file
  :group 'emacs-modules)

;; temp directory to hold downloads
(defvar emacs-modules-tmp-dir
  (expand-file-name "~/tmp/emacs-modules")
  "Temp directory to hold downloads.
\nIt is created and deleted before and after every fetch.")

;; install file macro
(defmacro emacs-modules-install-file (file)
  "Simple install macro to copy an Elisp FILE to `emacs-modules-elisp-home'."
  `(lambda ()
     ;; copy fetched file to elisp dir
     (shell-command (concat
                     "cp " (shell-quote-argument ,file)
                     " " (shell-quote-argument emacs-modules-elisp-home)))
     ;; compile file
     (batch-byte-compile-file ,file)))

;; install tarball macro
(defmacro emacs-modules-install-tarball-dir (file dir)
  "Install macro to unpack FILE and copy the contents to directory DIR in `emacs-modules-elisp-home'."
  `(lambda ()
     ;; unpack fetched file
     (shell-command (concat
                     "tar xvfz " (shell-quote-argument ,file)))
     ;; create elisp dir
     (shell-command (concat
                     "mkdir -p " (shell-quote-argument emacs-modules-elisp-home)
                     "/" (shell-quote-argument ,dir)))
     ;; copy fetched dir to elisp dir
     (shell-command (concat
                     "cp -r " (shell-quote-argument ,file) "/*"
                     " " (shell-quote-argument emacs-modules-elisp-home)
                     "/" (shell-quote-argument ,dir) "/"))
     ;; compile file
     (batch-byte-compile-file ,file)))

;; ;; database to hold module list information
;; ;; TODO: not sure if i'm going to use this or not
;; (defvar emacs-modules-database-list
;;   '(("Emacs Lisp List"
;;      "http://www.damtp.cam.ac.uk/user/sje30/emacs/ell.html"
;;      `(lambda ()
;;         ())))
;;   "Database of information about how to fetch module lists, how
;; to fetch modules, how to unpack and install them, etc.

;; The format is a list of lists as follows:

;;   ((NAME URL PARSE-CODE) ...)

;;   NAME is the name.
;;   URL is the url of the list.
;;   PARSE-CODE is a lambda function to eval in the buffer returned
;;   by the URL fetch."
;;   :type 'list
;;   :group 'Emacs-Modules)

;; database to hold module fetch/install information
(defvar emacs-modules-database-install
  `(("apel" "a portable emacs library"
     "ftp://ftp.m17n.org/pub/mule/apel/apel-9.23.tar.gz"
     "apel.tar.gz"
     ,(emacs-modules-install-tarball-dir "apel-*.tar.gz" "apel"))
    ("ascii" "ascii table"
     "http://www.emacswiki.org/cgi-bin/emacs/download/ascii.el"
     "ascii.el"
     ,(emacs-modules-install-file "ascii.el")))
  "Database of information about to fetch modules, unpack, and install them.

The format is a list of lists as follows:

  ((NAME DESC URL FILE CODE) ...)

  NAME is the name of the module.
  DESC is a longer description.
  URL is the url to fetch.
  FILE is the temp file to use when downloading.
  CODE is a function to run that will unpack, install, and
  compile the module.")

;; fetch a url and store the result in a file
(defun emacs-modules-fetch-url-to-file (url file)
  "Fetch URL to FILE."
  (let (cmd)
    ;; create request call
    (setq cmd (concat
               "wget"
               " \"-O\" \"" (shell-quote-argument file) "\""
               " \"" (shell-quote-argument url) "\""))
    (shell-command cmd)))

;; install module
(defun emacs-modules-install (module)
  "Install MODULE using information from ??? `emacs-modules-database-fetch'.
\nThe URL part of the module information is fetched, then the CODE
portion is executed to handle installing it."
  (interactive)
  ;; get module information
  (let* ((info (assoc module emacs-modules-database-install))
         (name (first info))
         (desc (second info))
         (url (third info))
         (file (fourth info))
         (code (fifth info)))
    ;; make sure temp dir exists
    (unless (file-exists-p emacs-modules-tmp-dir)
      (make-directory emacs-modules-tmp-dir))
    ;; fetch url
    (emacs-modules-fetch-url-to-file url file)
    ;; install module
    (eval code)))

(provide 'emacs-modules)

;;; emacs-modules.el ends here