;;; zenicb-stamp.el --- timestamping for ZenICB

;; Copyright (C) 1998 Faried Nawaz

;; Author: Faried Nawaz <fn@Hungry.COM>
;; Maintainer: Faried Nawaz <fn-icb@LISP-READER.Hungry.COM>
;; Keywords: extensions
;; Created: 1998/03/27

;; $Id: zenicb-stamp.el,v 1.1 1998/03/28 00:56:12 fn Exp $

;; 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 2, 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, you can either send email to this
;; program's maintainer or write to: The Free Software Foundation,
;; Inc.; 675 Massachusetts Avenue; Cambridge, MA 02139, USA.

;;; Commentary:

;; This code is meant as a demonstration of how to use the ZenICB
;; hook mechanism and timer code to cause ZenICB to do something at a
;; regular interval.  Since it was adapted from ZenIRC code, it may
;; be non-optimal.

;; The copyright/author info on the ZenIRC code is

;; Copyright (C) 1993, 1994 Ben A. Mesander
;; Author: Ben A. Mesander <ben@gnu.ai.mit.edu>

;;; Code:

(require 'zenicb)

(defvar zenicb-timer-hook nil)

(defvar zenicb-time-last-event nil)
(make-variable-buffer-local 'zenirc-time-last-event)


(defun zenicb-timer-handler (proc)
  "Call zenicb-timer-hook as often as possible. The maximum delay between
calls of zenicb-timer-hook is how often a server pings the client."
  (let ((now (zenicb-time-to-int (current-time-string))))
    (if (zenicb-time< '(0 0) (zenicb-time-diff now zenicb-time-last-event))
        (progn
          (and zenicb-debug-mainloop
               (zenicb-display-string proc
                               (format "[debug] timer: %s\n"
				       (current-time-string))))
          (zenicb-run-hook 'zenicb-timer-hook proc now)
          (setq zenicb-time-last-event now)))))

(defun zenicb-time-to-int (timestr)
  "Convert from time in string format as returned by current-time-string
to a double integer format, as returned by file-attributes.
                              
Written by Stephen Ma <ma_s@maths.su.oz.au>"
  (let* ((norm+ '(lambda (num1 num2)   
                  (let ((sumh (+ (car num1) (car num2)))
                        (suml (+ (car (cdr num1)) (car (cdr num2)))))
                    (list (+ sumh (/ suml 65536)) (% suml 65536)))))
         (norm* '(lambda (num1 num2)   
                  (let ((prodh (* num1 (car num2))) 
                        (prodl (* num1 (car (cdr num2)))))
                    (list (+ prodh (/ prodl 65536)) (% prodl 65536)))))
         (seconds (string-to-int (substring timestr 17 19)))  
         (minutes (string-to-int (substring timestr 14 16)))
         (hours (string-to-int (substring timestr 11 13)))
         (partdays (1- (string-to-int (substring timestr 8 10))))       
         (years (string-to-int (substring timestr 20 24)))
         (days (+ partdays
                  (cond ((and (= (% years 4) 0)
                              (/= (% years 100) 0))
                         (cdr (assoc (substring timestr 4 7)
                                     '(("Jan" . 0)
                                       ("Feb" . 31)
                                       ("Mar" . 60)
                                       ("Apr" . 91)
                                       ("May" . 121)
                                       ("Jun" . 152)
                                       ("Jul" . 182)
                                       ("Aug" . 213)
                                       ("Sep" . 244)
                                       ("Oct" . 274)
                                       ("Nov" . 305)
                                       ("Dec" . 335)))))
                        (t (cdr (assoc (substring timestr 4 7)
                                       '(("Jan" . 0)
                                         ("Feb" . 31)
                                         ("Mar" . 59)
                                         ("Apr" . 90)
                                         ("May" . 120)
                                         ("Jun" . 151)
                                         ("Jul" . 181)
                                         ("Aug" . 212)
                                         ("Sep" . 243)
                                         ("Oct" . 273)
                                         ("Nov" . 304)
                                         ("Dec" . 334))))))
                  (* (- years 1970) 365)
                  (/ (- years 1969) 4)
                  (- (/ (- years 1901) 100)))))
    (funcall norm+
             (funcall norm*
                      60
                      (funcall norm+ 
                               (funcall norm*
                                        60
                                        (funcall norm+
                                                 (funcall norm*
                                                          24
                                                          (list 0 days))
                                                 (list 0 hours)))       
                               (list 0 minutes)))
             (list 0 seconds))))

(defun zenicb-time= (a b)             
  "Compare two times, and return true if they are equal."
  (and (= (nth 0 a) (nth 0 b))       
       (= (nth 1 a) (nth 1 b))))

(defun zenicb-time< (a b)             
  "Compare two times, and return t if the first is earlier than the second."
  (or (< (nth 0 a) (nth 0 b))
      (and (= (nth 0 a) (nth 0 b))    
           (< (nth 1 a) (nth 1 b)))))

(defun zenicb-time-diff (a b)         
  "Return the difference between two times. This function requires      
the second argument to be earlier in time than the first argument."     
  (cond ((= (nth 0 a) (nth 0 b)) (list 0 (- (nth 1 a) (nth 1  b))))     
        ((> (nth 1 b) (nth 1 a)) (list (- (nth 0 a) (nth 0 b) 1)
                                       (- (+ 65536 (nth 1 a)) (nth 1 b))))
        (t (list (- (nth 0 a) (nth 0 b))
                 (- (nth 1 a) (nth 1 b))))))

(defun zenicb-epoch-seconds-to-time (seconds)
  (save-match-data
    (let (millions units high low)    
      (if (string-match "^\\(.*\\)\\(......\\)$" seconds)
          (setq millions (string-to-int (substring seconds
                                                   (match-beginning 1)  
                                                   (match-end 1)))      
                units (string-to-int (substring seconds
                                                (match-beginning 2)     
                                                (match-end 2))))
        (setq millions 0
              units (string-to-int seconds)))
      (setq high (+ (* millions 15) (/ (* millions 265) 1024) (/ units 65536)) 
            low (+ (% (+ (* (% millions 4) 16384) (* millions 576)) 65536)
                   (% units 65536))) 
      (if (> low 65535)
          (setq low (- low 65536)     
                high (1+ high)))     
      (list high low))))


(defvar zenicb-timestamp-interval '(0 600)
  "How often to insert timestamps into the ZenICB buffer. The default
is 600 seconds or 10 minutes. The value of this variable is a 32 bit
integer, expressed as a list of two 16 bit values, ie, the default
value of 600 seconds is expressed as (0 600).")
  
(defvar zenicb-last-timestamp '(0 0)
  "The time the last timestamp was inserted into the ZenICB buffer.  
You shouldn't have to frob this yourself.")

(defun zenicb-timestamp (proc now)    
  "Insert a timestamp into the the ZenICB buffer specified by the       
process PROC every zenicb-timestamp-interval seconds."
  (if (zenicb-time< zenicb-timestamp-interval
                    (zenicb-time-diff now zenicb-last-timestamp))       
      (progn
        (zenicb-display-string proc (concat "[time] "
					    (current-time-string) "\n"))
        (setq zenicb-last-timestamp now))))

(zenicb-add-hook 'zenicb-timer-hook 'zenicb-timestamp)
(setq zenicb-time-last-event (zenicb-time-to-int (current-time-string)))

(provide 'zenicb-stamp)