(require 'cl)
(require 'emacs-wiki)
(defgroup emacs-wiki-journal nil
  "Options controlling the behaviour of Emacs Wiki journaling.
See `emacs-wiki-journal-add-entry' for more information."
  :group 'emacs-wiki)
(defcustom emacs-wiki-journal-wiki "MyJournal"
  "*Default name of the file to which journal entries are added."
  :type 'string
  :group 'emacs-wiki-journal)
(defcustom emacs-wiki-journal-category-directory nil
  "*Default directory to search for category wiki files."
  :type 'directory
  :group 'emacs-wiki-journal)
(defcustom emacs-wiki-journal-icons-subdirectory "images"
  "*Default base url to a directory containing category icons."
  :type 'string
  :group 'emacs-wiki-journal)
(defcustom emacs-wiki-journal-time-format "%a, %e %b. %2y"
  "*Format for the date string of journal entries.
See `format-time-string' for more information."
  :type 'string
  :group 'emacs-wiki-journal)
(defcustom emacs-wiki-journal-category-regexp "^Category"
  "*Each of the category index Wiki files start with this prefix."
  :type 'string
  :group 'emacs-wiki-journal)
(defun emacs-wiki-journal-category-alist (&optional no-check-p)
  "Return possible category index Wikis in `emacs-wiki-directories'.
If NO-CHECK-P is non-nil, then don't check for changes in the directories
to decide whether to re-read the cached alist, just re-read the disk."
  (let ((file-alist (emacs-wiki-file-alist no-check-p))
        (category-alist nil))
    (save-match-data
      (while file-alist
        (if (string-match emacs-wiki-journal-category-regexp
                          (caar file-alist))
            (setq category-alist (cons (car file-alist)
                                       category-alist)))
        (setq file-alist (cdr file-alist))))
    category-alist))
(defun emacs-wiki-journal-prompt-for-category-wiki ()
  "Prompt for a category index file."
  (let* ((directory (or emacs-wiki-journal-category-directory
                        (car emacs-wiki-directories)))
         (file-alist (emacs-wiki-journal-category-alist))
         (emacs-wiki-default-page (or (caar file-alist) "CategoryEmacs")))
    (emacs-wiki-read-name file-alist "Category Wiki: ")))
(defun emacs-wiki-journal-make-extended-link (target-url
                                              &optional link-description)
  "Make an Emacs Wiki extended link from TARGET-URL and LINK-DESCRIPTION,"
  (concat "[[" target-url
          (and link-description (concat "][" link-description))
          "]]"))
(defun emacs-wiki-journal-add-category-entry (wiki target-url
                                              &optional link-description)
  "Find category index file and add an entry for today."
  (emacs-wiki-find-file wiki)
  (undo-boundary)
  (goto-char (point-min))
    (goto-char 
   (or (search-forward-regexp "^\\* " (point-max) t) (point)))
  (beginning-of-line)
  (forward-line 1)
  (goto-char 
   (or (search-forward-regexp "^- " (point-max) t) (point)))
  (beginning-of-line)
  (insert (concat "- "
                  (format-time-string emacs-wiki-journal-time-format)
                  " "
                  (emacs-wiki-journal-make-extended-link
                   target-url link-description)
                  "\n")))
(defun emacs-wiki-journal-1+-string (value)
  "Increment an ascii encoded number."
  (int-to-string (1+ (string-to-int value))))
(defun emacs-wiki-journal-add-entry (&optional other-window)
  "Find journal file and add an entry and category index for today."
  (interactive)
  (let* ((category-wiki (emacs-wiki-journal-prompt-for-category-wiki))
         (journal-entry-heading (read-from-minibuffer "Journal Heading: "))
         (category-anchor-base (downcase category-wiki))
         (anchor-regexp (concat "^#" (regexp-quote category-anchor-base)
                                "\\([0-9][0-9]*\\)"))
         (anchor-ord "0"))
    (emacs-wiki-find-file emacs-wiki-journal-wiki)
    (goto-char (point-min))
    (save-excursion
      (if (search-forward-regexp anchor-regexp (point-max) t)
          (setq anchor-ord 
                (emacs-wiki-journal-1+-string
                 (buffer-substring (match-beginning 1)
                                   (match-end 1))))))
    (save-excursion
      (emacs-wiki-journal-add-category-entry
       category-wiki
       (concat emacs-wiki-journal-wiki "#" category-anchor-base anchor-ord)
       journal-entry-heading))
        (while (and (looking-at "^\\(#\\|\n\\)")
                (equal 0 (forward-line 1))))
        (goto-char 
     (or (search-forward-regexp "^\\* " (point-max) t) (point)))
    (beginning-of-line)
    (let* ((time-string (format-time-string
                         emacs-wiki-journal-time-format))
           (icon-file-name (concat emacs-wiki-journal-icons-subdirectory "/"
                                   category-wiki ".png"))
           (icon-link (if (file-exists-p icon-file-name)
                          (concat (emacs-wiki-journal-make-extended-link
                            icon-file-name) " ")
                        "")))
            (if (not (looking-at (regexp-quote (concat "* " time-string))))
          (insert (concat "\n* " time-string "\n\n"))
        (forward-line 1)
        (insert "\n"))
      (open-line 1)
      (insert (concat "#" category-anchor-base anchor-ord " " icon-link "\n** " 
                      journal-entry-heading "\n*** " category-wiki "\n\n\n")))
    (emacs-wiki-find-file emacs-wiki-journal-wiki)
    (forward-line -1)                       ))
(defun emacs-wiki-journal-add-entry-other-window ()
  "Find category index file in another window and add an entry for today."
  (interactive)
  (emacs-wiki-journal-add-entry t))
(provide 'emacs-wiki-journal)