;;AUTOLOAD: ascii-progress-bar
;;AUTODOC: ascii-progress-bar "displays an ascii progress bar on stderr"
;;(ascii-progress-bar current-size [total-size [no-new-line]])
;;
;;Displays on stderr a bargraph showing:
;; ##########################------------------------------------- [124M/345M]
;;   [part received]              [part to receive]             [numeric info]
;;for (ascii-progress-bar 130023424 361758720)
;;
;;Sizes are expressed in bytes. The system will print in k or mega if needed.
;;First call must include the total size expected, to initialise things,
;;subsequent calls should not use it, as passing total-size will display a 
;;new bar underneath (passing no-new-line as t will continue on the same line)
;;If you do not know the size at init, just pass 0 for size.
;;The display is only updated when the number of # changes, so that you can 
;;update often without worrying about screen flashes
;;The width of the page, which defaults to 79, can be set as
;;ascii-progress-bar:page-width
;;The # and - characters can be changed by setting the variables:
;;ascii-progress-bar:# ascii-progress-bar:- holding their ascii code
;;The numeric info format string, "[%0/%1]" can be changed with the
;;variable ascii-progress-bar:numinfo-format
;;After changing these variables, you must re-do an init by passing total-size
;;If you did not want to start a new bar, do before it a:
;; (setq ascii-progress-bar::must-up ())

;;AUTOLOAD: ascii-progress-bar-with-speed
;;AUTODOC: ascii-progress-bar-with-speed "displays an ascii progress bar on stderr with instant speed"
;;
;;(ascii-progress-bar-with-speed current-size [total-size [no-new-line]])
;;
;;Same as ascii-progress-bar, but shows the instant speed underneath and 
;;elapsed/remaining times

(setq *package* 'ascii-progress-bar)
					;customisations
(defvar ascii-progress-bar:page-width 79)
(defvar ascii-progress-bar:# #\#)
(defvar ascii-progress-bar:- #\-)
(defvar ascii-progress-bar:numinfo-format "[%0/%1]")
					;internal state variables
(defun %:M (s) (print-format String "%0M" (/ s 1048576)))
(defun %:k (s) (print-format String "%0k" (/ s 1024)))
(defvar %:must-up ())	
(defvar %:print-size %:k)
(defvar %:total-size ())
(defvar %:total-size-string ())
(defvar %:last-N -1)
(defvar %:start-time ())
(defvar %:last-size 0)
(defvar %:start-size 0)


(defun ascii-progress-bar (size &optional total newline &aux N)
  (if total (%:init size total newline))
  (setq N (/ size %:delta))
  (if (/= N %:last-N) (progn
      (setq %:last-N N)
      (if %:must-up 
	(write-chars "\e[A" () *standard-error*)
	(setq %:must-up t)
      )
      (dotimes N (write-char ascii-progress-bar:# *standard-error*))
      (setq N (- %:chars N))
      (dotimes N (write-char ascii-progress-bar:- *standard-error*))
      (print-format *standard-error* 
	(+ " " ascii-progress-bar:numinfo-format "\n")
	(%:print-size size) %:total-size-string)
  )))
)

(defun ascii-progress-bar-with-speed (size &optional total newline &aux
    (delta-time (progn (ascii-progress-bar size total newline)
	(/ (- %:last-time (setq %:last-time 
	      (mod (get-internal-run-time) *maxint*))) 1000.
  ))))					;delta-time is negative
  (if (and (< delta-time 0.0) (not total))
    (%:print-speed
      (/ (- %:last-size (setq  %:last-size size)) delta-time)
      (- %:last-time %:start-time)
      size
    )
    (if total (setq %:last-size size))
))

(defun %:print-speed (speed elapsed-time size &aux
    (s (if (> speed 64000.0) 
	(print-format String "%0 kps" (Int (/ speed 1024)))
	(> speed 0.0)
	(print-format String "%0 bps" (Int speed))
	"Stalled"
    ))
    (monitored-size (- size %:start-size))
    (ts (/ elapsed-time 1000))
    (et (%:print-time ts))
    (rt (if (> speed 0.0) (%:print-time (Int (/ (- %:total-size size) speed)))
	"..."))
    (rtm (if (> monitored-size 0) (%:print-time 
	  (Int (/ (* (Real ts) (- %:total-size size)) monitored-size))))
    )
  )
  (print-format  *standard-error* 
    "Speed: %1%0  Time: %3%2 / rem. avg: %5%4 inst: %7%6\n\e[A" 
    s (make-string (- 9 (length s)))
    et (make-string (- 8 (length et)))
    rtm (make-string (- 8 (length rtm)))
    rt (make-string (- 8 (length rt)))
  )
)

(defun %:print-time (seconds)
  (if (< seconds 0) (setq seconds 0))
  (print-format String "%0%1%2%3s"
    (if (>= seconds 3600)
      (+ (String (/ seconds 3600)) "h ") ""
    )
    (if (>= seconds 60)
      (+ (String (/ (mod seconds 3600) 60)) "mn ") ""
    )
    (if (> (mod seconds 60) 9) "" 0)
    (mod seconds 60)
  )
)

  

(defun %:init (size total newline)
  (setq %:print-size (if (> total 10485760) %:M %:k)) ; M if total-size > 10M
  (setq %:total-size total)
  (setq %:total-size-string (%:print-size total))
  (setq %:chars (- ascii-progress-bar:page-width 
      (+ 1 (length (print-format String ascii-progress-bar:numinfo-format
	      %:total-size-string %:total-size-string
    )))))
  (setq %:delta (/ total %:chars))
  (if (< %:delta 1) (setq %:delta 1))
  (if newline (setq %:must-up ()))	;cursor will not go up first time
  (setq %:last-N -1)
  (setq %:start-time (mod (get-internal-run-time) *maxint*))
  (setq %:last-time %:start-time)
  (setq %:last-size size)
  (setq %:start-size size)
)
