#
# Yes, the unix cal program, kind of 
# some algorithms from SFRI-19 used
#
eval (define (normal) \
       (display (integer->char 27)) \
       (display (integer->char 91)) \
       (display "0") \
       (display "m"))
eval (define (underscore) \
       (display (integer->char 27)) \
       (display (integer->char 91)) \
       (display "4") \
       (display "m"))
eval (define (reversevideo) \
       (display (integer->char 27)) \
       (display (integer->char 91)) \
       (display "7") \
       (display "m"))
eval (define days (list "Su" "Mo" "Tu" "We" "Th" "Fr" "Sa"))
eval (define months (list "January" "February" "March" "April" "May" "June" "July" \
                          "August" "September" "October" "November" "December" \
                           "1" "2" "3" "4" "5" "6" "7" "8" "9" "10" "11" "12" \
                          "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" \
                          "Nov" "Dec"))
eval (define (months-number m) (if (number? m) m (+ 1 (remainder (- 36 (length (member m months))) 12))))
eval (define (tojulian day month year) \
  (let* ((m (months-number month)) \
         (a (quotient (- 14 m) 12)) \
         (y (- (+ year 4800) a (if (negative? year) -1  0))) \
         (m (- (+ m (* 12 a)) 3))) \
    (+ day \
       (quotient (+ (* 153 m) 2) 5) \
       (* 365 y) \
       (quotient y 4) \
       (- (quotient y 100)) \
       (quotient y 400) \
       -32045)))
eval (define (easter year) \
  (let* ((a (modulo year 19)) \
         (b (quotient year 100)) \
         (c (modulo year 100)) \
         (d (quotient b 4)) \
         (e (modulo b 4)) \
         (f (quotient (+ b 8) 25)) \
         (g (quotient (+ (- b f) 1) 3)) \
         (h (modulo (- (+ (* 19 a) b 15) d g) 30)) \
         (i (quotient c 4)) \
         (k (modulo c 4)) \
         (l (modulo (- (+ 32 (* 2 e) (* 2 i)) h k) 7)) \
         (m (quotient (+ a (* 11 h) (* 22 l)) 451)) \
         (month (quotient (- (+ h l 114) (* 7 m)) 31)) \
         (day (+ (modulo (- (+ h l 114) (* 7 m)) 31) 1))) \
    (list (tojulian day month year) (list day month year))))
eval (define (dayofweek jd) (+ (remainder jd 7) 1))
eval (define (firstmonday month year) \
        (let* ((d1 (tojulian 1 (months-number month) year)) \
              (offset (remainder (- 8 (dayofweek d1)) 7))) \
           (+ d1 offset)))
eval (define (julian-date) \
  (let* ((today (car (string-split (date)))) \
         (day (string->number (substring today 6 8))) \
         (month (months-number (string->number (substring today 4 6)))) \
         (year (string->number (substring today 0 4)))) \
     (list (tojulian day month year) (list day month year))))
eval (define (month-limits today) \
  (let* ((first (tojulian 1 (cadr (cadr today)) (caddr (cadr today)))) \
         (last (- (tojulian 1 (+ (months-number (cadr (cadr today))) 1) (caddr (cadr today))) 1))) \
    (list first (dayofweek first) \
          last (dayofweek  last))))
#
# Queensland public holidays
#
eval (define (list-holidays year) \
  (list (tojulian 1 1 year) (tojulian 26 1 year) \
        (- (car (easter year)) 2) (+  (car (easter year)) 1)  \
        (tojulian 25 4 year) \
        (tojulian 1 5 year) (+ (firstmonday 6 year) 7) \
        (tojulian 25 12 year) (tojulian 26 12 year)))
eval (define (scheme-cal args) \
  (let* ((arg-list (string-split args)) \
         (this-month (= (length arg-list) 0)) \
         (today (if this-month (julian-date) \
                   (list (tojulian 1 (car arg-list) (string->number (cadr arg-list))) \
                          (list 1 (car arg-list) (string->number (cadr arg-list)))))) \
         (holidays (list-holidays (caddr (cadr today)))) \
         (ends (month-limits today))) \
    (display "   (") \
    (display (list-ref months (- (months-number (cadr (cadr today))) 1))) \
    (display " ") \
    (display (caddr (cadr today))) \
    (display ")") \
    (newline) \
    (display days) \
    (newline) \
    (let loop ((i 0)) \
      (if (< i (remainder (cadr ends) 7)) \
         (begin (display "  -") (loop (+ i 1))))) \
    (let loop ((i (car ends)) (j 1) (k (cadr ends))) \
      (if (<= i (caddr ends)) \
         (begin (display " ") \
                (if (and this-month (= i (car today))) (reversevideo)) \
                (if (member i holidays) (underscore)) \
                (format "~2d" j) \
                (if (or (member i holidays) (and this-month (= i (car today)))) (normal)) \
                (if (= k 6) (newline)) \
                (loop (+ i 1) (+ j 1) (remainder (+ k 1) 7))) \
         (display " ")))))
macro cal
  eval (scheme-cal "%0")
;;;;
