(provide 'zsigs)

(defvar zsigs-inited nil)
(defvar zsigs-load-buffer-name "*zsigs-load*")
(defvar zsigs-file-name "~/.zsigs")
(defvar zsigs-userlist ())
(defvar zsigs-sigslist ())
(defvar zsigs-default-sig "You don't look for love, it's gonna for you.")

(defmacro zsigs-load-buffer () '(get-buffer-create zsigs-load-buffer-name))

(defun zsigs-init ()
  (interactive)
  (zsigs-load-file zsigs-file-name)
  (setq zsigs-inited t))

(defun zsigs-load-file (filename)
  (interactive "fSigs file: ")
  (save-excursion
    (set-buffer (zsigs-load-buffer))
    (insert-file zsigs-file-name)
    (zsigs-process-buffer (zsigs-load-buffer))
    (kill-buffer (zsigs-load-buffer))))

(defun zsigs-process-buffer (buffer)
  ; Assumes that the zsigs-load-buffer is the current buffer, and that
  ; it can frotz with it without fear.
  (point-min)
  (let ((userlist (read (zsigs-load-buffer)))
	(sigslist (read (zsigs-load-buffer))))
    (mapcar 'zsigs-add-userentry userlist)
    (mapcar 'zsigs-add-sigentry sigslist)))

(defun zsigs-add-userentry (entry)
  (let ((found (assoc (car entry) zsigs-userlist)))
    (if (null found)
	(setq zsigs-userlist (append zsigs-userlist 
				     (list (append entry '(0)))))
      (setcdr found (append (cdr entry) '(0))))))

(defun zsigs-add-sigentry (entry)
  (let ((found (assoc (car entry) zsigs-sigslist)))
    (if (null found)
	(setq zsigs-sigslist (append zsigs-sigslist (list entry))) 
      (setcdr found (cdr entry)))))

(defun zsigs (class instance recipient)
  (let* ((userentry (zsigs-find-entry class instance recipient))
	 (sig (if userentry
		  (funcall (nth 1 userentry) 
			   userentry 
			   (zsigs-assemble-sigslist (nth 2 userentry))))))
;			   (nth 1 (assoc (nth 2 userentry) zsigs-sigslist))
    (if (null sig)
	zsigs-default-sig
      sig)))

(defun zsigs-assemble-sigslist (l)
  (if (atom l)
      (nth 1 (assoc l zsigs-sigslist))
    (zsigs-flatten (mapcar 'zsigs-assemble-sigslist l))))

(defun zsigs-find-entry (class instance recipient)
; Try to match the triple, then just the class, then class:instance
; then just the instance, 
  ; then just the recipient, then get the default.
  (let ((found (cond 
		((assoc (zsigs-build-user-string class instance recipient)
		       zsigs-userlist))
		((assoc (zsigs-build-user-string class nil nil)
		       zsigs-userlist))
		((assoc (zsigs-build-user-string class instance nil)
		       zsigs-userlist))
		((assoc (zsigs-build-user-string nil instance nil)
		       zsigs-userlist))
		((assoc (zsigs-build-user-string nil nil recipient)
		       zsigs-userlist))
		((assoc "" zsigs-userlist)))))
    found))

(defun zsigs-build-user-string (class instance recipient)
  (concat class ":" instance ":" recipient))

(defun zsigs-random (user sigs-list)
  (let ((index (mod (random) (length sigs-list))))
    (nth (if (< index 0)
	     (- index)
	   index)
	 sigs-list)))

(defun zsigs-multirandom (user sigs-list)
  (progn
    (zsigs-flatten sigs-list)
    "foo"))

(defun zsigs-sequence (user sigs-list)
  (let* ((num (nth 3 user))
	 (sig (nth num sigs-list)))
    (setcar (cdr (cdr (cdr user)))
	    (mod (1+ num) (length sigs-list)))
    sig))

(defun zsigs-flatten (l)
  (cond ((not l) nil)
	((atom l) (list l))
	(t (append (zsigs-flatten (car l)) (zsigs-flatten (cdr l))))))
