;; -*- emacs-lisp -*-
;;; diary-countdown.el --- a variation on standard diary-mode output

;; Author: Mark Triggs <mst@dishevelled.net>
;; $Id: diary-countdown.el,v 1.12 2004/05/22 05:59:58 mst Exp $
;; Keywords: calendar

;; This file 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 file 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:

;; This code provides a celebrat-style output format for diary-mode. Output
;; could be something like "Buy pet duck named Gerald in 5 days".

;; To use it, add the following form to your ~/.emacs:
;;
;;     (add-hook 'diary-display-hook 'mst-diary-display)
;;
;; I use a shell script to show upcoming stuff when I login. This is available
;; via http://dishevelled.net/misc/my-life.sh

;;; Code:
(require 'cl)

(defvar mst-diary-display-terms
  '((0 . "today")
    (1 . "tomorrow")
    (2 . "the day after tomorrow")
    (7 . "one week from now")
    (14 . "one fortnight from now"))
  "A mapping of 'days from now' numbers to natural language")

(defun mst-diary-make-buffer ()
  (prog1 (set-buffer (get-buffer-create "*Your Life*"))
    (setq buffer-read-only nil)
    (erase-buffer)
    (set-buffer-modified-p nil)))

(defun mst-diary-trim-string (s)
  (replace-regexp-in-string "^[ \t]+\\|[ \t]+$" "" s))

(defun mst-diary-display ()
  (let ((buffer (mst-diary-make-buffer)))
    (mapc
     (lambda (entry)
       (insert
        (if (assoc (car entry) mst-diary-display-terms)
            (format "%s %s\n"
                    (mst-diary-trim-string (cadr entry))
                    (cdr (assoc (car entry) mst-diary-display-terms)))
          (format "%s in %s days\n"
                  (mst-diary-trim-string (cadr entry))
                  (car entry)))))
     ;; For recurring events, only show the upcoming one
     (remove-duplicates (mst-diary-grok)
                        :key 'cadr :test 'string= :from-end t))
    (set-buffer-modified-p nil)
    (view-mode)
    (pop-to-buffer buffer)))


(defun mst-diary-grok ()
  (require 'time-stamp)
  (let* ((timestamp (split-string (time-stamp-yyyy/mm/dd) "/"))
         (today
          (calendar-absolute-from-gregorian
           (list (string-to-number (nth 1 timestamp))
                 (string-to-number (nth 2 timestamp))
                 (string-to-number (nth 0 timestamp))))))
    (sort (mapcar
           (lambda (diary-entry)
             (list
              ;; days from now
              (- (calendar-absolute-from-gregorian (car diary-entry))
                 today)

              ;; the diary entry string
              (cadr diary-entry)))
           (remove-duplicates diary-entries-list
                              :test (lambda (e1 e2)
                                      (and (string-match "diary-cyclic" e1)
                                           (string= e1 e2)))
                              :key #'caddr :from-end t))
          (lambda (rec1 rec2)
            (< (car rec1) (car rec2))))))

(provide 'diary-countdown)
;;; diary-countdown.el ends here
