;;; eltuki.el --- Tekuti functions

;; Copyright (C) 2012  Tom Willemsen

;; Author: Tom Willemsen <slash@drd>
;; Keywords: convenience

;; 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, either version 3 of the License, or
;; (at your option) any later version.

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

;;; Commentary:

;; Tekuti functions.

;;; Code:

(require 'org)

(defgroup eltuki
  nil
  "tekuti functions in Emacs."
  :group 'external)

(defcustom eltuki-blog-dir "~/blog"
  "Plain blog post directory, not the git repository."
  :group 'eltuki
  :type 'string)

(defcustom eltuki-default-status "publish"
  "Default status to use when status is unknown."
  :group 'eltuki
  :type 'string)

(defcustom eltuki-default-comment-status "open"
  "Default status for comments."
  :group 'eltuki
  :type 'string)

(defun eltuki-new-post ()
  (interactive)
  (switch-to-buffer (get-buffer-create "*eltuki*"))
  (org-mode))

(defun eltuki-get-title ()
  (save-excursion
    (goto-char (point-min))
    (if (re-search-forward "^#\\+TITLE: \\(.*\\)$" nil t)
        (buffer-substring-no-properties
         (match-beginning 1) (match-end 1))
      (error "Post has no title."))))

(defun eltuki-set-title (title)
  (interactive "MTitle: ")
  (setq title (concat " " title))
  (save-excursion
    (goto-char (point-min))
    (if (re-search-forward "^#\\+TITLE:\\(.*\\)$" nil t)
        (replace-match title t t nil 1)
      (insert "#+TITLE:" title "\n")
      (unless (= (char-after) ?\n)
        (insert-char ?\n)))))

(defun eltuki-get-timestamp ()
  (save-excursion
    (goto-char (point-min))
    (if (re-search-forward "^#\\+TIMESTAMP: \\([[:digit:]]+\\)$" nil t)
        (match-string 1)
      (format-time-string "%s"))))

(defun eltuki-set-timestamp ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((newtime (format-time-string " %s")))
      (if (re-search-forward "^#\\+TIMESTAMP:\\(.*\\)$" nil t)
          (replace-match newtime nil t nil 1)
        (when (search-forward "\n\n" nil t)
          (backward-char))
        (insert "#+TIMESTAMP:" newtime "\n")))))

(defun eltuki-get-tags ()
  (save-excursion
    (goto-char (point-min))
    (when (re-search-forward "^#\\+TAGS: \\(.*\\)$" nil t)
      (buffer-substring-no-properties
       (match-beginning 1) (match-end 1)))))

(defun eltuki-set-tags (tags)
  (interactive "MTags: ")
  (setq tags (concat " " tags))
  (save-excursion
    (goto-char (point-min))
    (if (re-search-forward "^#\\+TAGS:\\(.*\\)$" nil t)
        (replace-match tags t t nil 1)
      (when (search-forward "\n\n" nil t)
        (backward-char))
      (insert "#+TAGS:" tags "\n"))))

(defun eltuki-get-status ()
  (save-excursion
    (goto-char (point-min))
    (if (re-search-forward "^#\\+STATUS: \\(draft\\|publish\\)$" nil t)
        (buffer-substring-no-properties
         (match-beginning 1) (match-end 1))
      eltuki-default-status)))

(defun eltuki-toggle-status ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((newstatus (if (string= (eltuki-get-status) "draft")
                         " publish"
                       " draft")))
      (if (re-search-forward "^#\\+STATUS:\\(.*\\)$" nil t)
          (replace-match newstatus t t nil 1)
        (when (search-forward "\n\n" nil t)
          (backward-char))
        (insert "#+STATUS:" newstatus "\n")))))

(defun eltuki-get-comment-status ()
  (save-excursion
    (goto-char (point-min))
    (if (re-search-forward
         "^#\\+COMMENTSTATUS: \\(open\\|closed\\)$" nil t)
        (buffer-substring-no-properties
         (match-beginning 1) (match-end 1))
      eltuki-default-comment-status)))

(defun eltuki-toggle-comment-status ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((newstatus (if (string= (eltuki-get-comment-status) "closed")
                         " open"
                       " closed")))
      (if (re-search-forward "^#\\+COMMENTSTATUS:\\(.*\\)$" nil t)
          (replace-match newstatus t t nil 1)
        (when (search-forward "\n\n" nil t)
          (backward-char))
        (insert "#+COMMENTSTATUS:" newstatus "\n")))))

(defun eltuki-slugify-string (str)
  (while (string-match "[^a-zA-Z0-9 ]+" str)
    (setq str (replace-match "" nil t str)))
  (while (string-match " +" str)
    (setq str (replace-match "-" nil t str)))
  (downcase str))

(defun eltuki-get-directory ()
  (concat
   eltuki-blog-dir "/"
   (format-time-string "%Y%%2f%m%%2f%d%%2f")
   (eltuki-slugify-string (eltuki-get-title))))

(defun eltuki-write-content (dir)
  (with-current-buffer (org-export-region-as-html
                        (point-min) (point-max) t "*eltuki-html*")
    (write-region (point-min) (point-max) (concat dir "/content"))))

(defun eltuki-write-metadata (dir)
  (let ((timestamp (eltuki-get-timestamp))
        (tags (eltuki-get-tags))
        (status (eltuki-get-status))
        (title (eltuki-get-title))
        (name (eltuki-slugify-string (eltuki-get-title)))
        (commentstatus (eltuki-get-comment-status)))
  (with-temp-buffer
    (insert "timestamp: " timestamp "\n"
            "tags: " tags "\n"
            "status: " status "\n"
            "title: " title "\n"
            "name: " name "\n"
            "comment_status: " commentstatus)
    (write-region (point-min) (point-max) (concat dir "/metadata")))))

(defun eltuki-finish ()
  (interactive)
  (let ((buffer (get-buffer "*eltuki*"))
        (dest (eltuki-get-directory)))
    (unless (file-exists-p dest)
      (mkdir dest))

    (eltuki-write-content dest)
    (eltuki-write-metadata dest)
    (kill-buffer buffer)))

(provide 'eltuki)
;;; eltuki.el ends here
