;;;	UTILS > -*- Mode:LISP; Package:USER; Base:10.; readtable: CL -*-

;;; This file contains miscellaneous functions of general utility.

(defsubst skip-to (col)
  (send *TERMINAL-IO* :set-cursorpos col nil :character))


;;; DISPLAY VARIOUS INFORMATION ON MEMORY USAGE.

(defvar *AREA-TEST-ALREADY-PRINTED-THIS*)

(defun area-test (object &aux *AREA-TEST-ALREADY-PRINTED-THIS*)
  (area-test2 object "Object: ")
  (terpri)
  (values))

(defun area-test2 (object &rest strings)
  (cond ((memq object *AREA-TEST-ALREADY-PRINTED-THIS*))
	(t (push object *AREA-TEST-ALREADY-PRINTED-THIS*)
	   (apply #'TERPRINC2 strings)
	   (prin1 object)
	   (let ((type (type-of object)))
	     (princ-rest "  Type: " type)
	     (let ((area (%area-number object)))
	       (terprinc "Area: " area
			 " (" (if (integerp area)
				  (area-name area)
				"NO AREA!!")
			 ")"))
	     (cond ((memq type '(SYMBOL FIXNUM BIGNUM CHARACTER SINGLE-FLOAT SHORT-FLOAT)))
		   ((consp object)
		    (area-test2 (car object) "Car: ")
		    (area-test2 (cdr object) "Cdr: "))
		   ((and (si:named-structure-p object)
			 (cond ((and (get (si:named-structure-p object)
					  'SI:NAMED-STRUCTURE-INVOKE)
				     (memq ':DESCRIBE
					   (si:named-structure-invoke object :WHICH-OPERATIONS)))
				(terpri)
				(si:named-structure-invoke object :DESCRIBE)
				t)
			       ((get (si:named-structure-p object) 'SI:DEFSTRUCT-DESCRIPTION)
				(my-describe-defstruct object)
				t)
			       (t nil))))
		   ((arrayp object) (my-describe-array object))
		   ((instancep object) (my-describe-instance object))
		   (t (describe object)))))))


(proclaim '(special LAM:%%ARRAY-LONG-LENGTH-FLAG))

(defun my-describe-array (array)
  (fresh-line)
  (let ((rank (array-rank array))
	(long-length-flag (%p-ldb-offset LAM:%%ARRAY-LONG-LENGTH-FLAG array 0)))
    (format t "This is an ~S type array. (element-type ~S)~%"
	    (array-type array) (array-element-type array))
    (case rank
      (0 (format T "It is of zero rank."))
      (1 (format t "It is a vector, with a total size of ~S elements" (array-total-size array))
	 (unless (stringp array)
	   (dotimes (i (array-total-size array))
	     (area-test2 (aref array i) "Element " i ": "))))
      (t (format T "It is ~S-dimensional, with dimensions " rank)
	 (dotimes (d rank) (format t "~S " (array-dimension array d)))
	 (format t ". Total size ~S elements" (array-total-size array))))
    (when (array-has-leader-p array)
      (let ((length (array-leader-length array)))
	(if (and (= rank 1)
		 (= length 1)
		 (fixnump (array-leader array 0)))
	    (format t "~%It has a fill-pointer: ~S" (fill-pointer array))
	  (format t "~%It has a leader, of length ~S.  Contents:" length)
	  (dotimes (i length)
	    (area-test2 (array-leader array i) "Leader " i ": ")))))
    (when (array-displaced-p array)
      (cond ((array-indirect-p array)
	     (format t "~%The array is indirected to ~S"
		     (%p-contents-offset array (+ rank long-length-flag)))
	     (and (array-indexed-p array)
		  (format T ", with index-offset ~S"
			  (%p-contents-offset array (+ rank long-length-flag 2))))
	     (format t "~%Description of that array:")
	     (si:describe-1 (%p-contents-offset array (+ rank long-length-flag))))
	    (t (format t "~%The array is displaced to ~S"
		       (%p-contents-offset array (+ rank long-length-flag))))))))

(DEFUN my-DESCRIBE-DEFSTRUCT (X)
  (fresh-line)
  (LET ((DESCRIPTION (GET (IF (CONSP X) (CAR X) (NAMED-STRUCTURE-P X))
			  'si:DEFSTRUCT-DESCRIPTION)))
    (FORMAT T "~S is a ~S~%" X (si:DEFSTRUCT-DESCRIPTION-NAME DESCRIPTION))
    (DO ((slots (si:DEFSTRUCT-DESCRIPTION-SLOT-ALIST DESCRIPTION) (cdr slots))
	 (index 0 (1+ index)))
	((null slots))
      (let* ((L (car slots))
	     (value (EVAL `(,(si:DEFSTRUCT-SLOT-DESCRIPTION-REF-MACRO-NAME (CDR L)) ',X))))
	(area-test2 value "Slot " index ", " (CAR L) ", Value:
 ")))))

(defun my-describe-instance (instance)
  "Prints out each of the slots of INSTANCE.
This bypasses the :DESCRIBE method of the instance."
  (fresh-line)
  (let* ((flavor (si:%instance-flavor instance))
	 (ivars (si:flavor-all-instance-variables flavor)))
    (format t "~S is an instance of flavor ~S" instance flavor)
    (loop for v in ivars
	  for n from 1
	  do (let ((value (si:%instance-ref instance n)))
	       (area-test2 value "Instance Variable " n ", " v ", Value:
 ")))))


;;; GENERAL "OPERATING SYSTEM" UTILITIES:

(defun purge (&rest directories)
  (when (y-or-n-p "Purge RSG ?")
    (filesystem-purge :directory "RSG" :versions-to-keep 2))
  (when (y-or-n-p "Purge MZWEI ?")
    (filesystem-purge :directory "MZWEI" :versions-to-keep 2))
  (do ((dirs directories (cdr dirs))
       (dir))
      ((null dirs))
    (setq dir (string-upcase (car dirs)))
    (when (y-or-n-p "Purge ~A ?" dir)
      (print (filesystem-purge :directory dir :versions-to-keep 2))))
  (terpri)
  (values))

(defun flavor-ops (object string)
  (do ((items (send object :which-operations) (cdr items))
       (outputlist))
      ((null items)
       (mapc #'PRINT outputlist)
       (terpri)
       (values))
    (when (search string (string (car items)) :test #'CHAR-EQUAL)
      (push (car items) outputlist))))


;;; GENERAL-PURPOSE UTILITY FUNCTIONS.

(defun cl (system &optional recompile?)
  (sa)
  (make-system system (if recompile? :recompile :compile)))

(defun halt ( )
  (sa)
  (when (y-or-n-p "Are you sure you want to halt ?")
    (logout)
    (si:%halt)))

(defun sa ( )
  (let ((ZWEI:*WINDOW* t)
	(ZWEI:*NUMERIC-ARG-P* nil))
    (zwei:com-save-all-files)
    (values)))

(defun ma (&rest args)				; Avoids garbage printout of
  (apply #'MAPC args)				; MAPC's return value.
  (terpri)
  (values))

(defun change (index new-value change-list)
  (when (fixp index)
    (setf (nth index change-list)
	  (cond ((floatp new-value) (small-float new-value))
		(t new-value))))
  change-list)

(defun mult (list num)
  (map #'(lambda (x)
	   (rplaca x (* (car x) (small-float num))))
       list)
  list)


;;; CHANGES ALL FLOATS IN A TREE (OR LIST) TO SMALL-FLOAT.

(defun make-small-float (tree &optional copy?)
  (when copy?
    (setq tree (copy-tree tree)))
  (when (numberp (car tree))
    (rplaca tree (small-float (car tree))))
  (when (numberp (cdr tree))
    (rplacd tree (small-float (cdr tree))))
  (unless (atom (car tree))
    (make-small-float (car tree)))
  (unless (atom (cdr tree))
    (make-small-float (cdr tree)))
  tree)


;;; GENERAL-PURPOSE UTILITY FUNCTIONS (MOSTLY I/O).

(defun terprinc (&rest things)
  (fresh-line)
  (apply #'PRINC-REST things))

(defun terprinc2 (&rest things)
  (fresh-line)
  (terpri)
  (apply #'PRINC-REST things))

(defun princ-rest (&rest things)
  (do ((item things (cdr item)))
      ((null item))
    (princ (car item))))

(defun soft-print (line col item)
  (cursorset line col item)
  (princ item))

(defun print-line (line col &rest things)
  (cursorset line col)
  (apply #'PRINC-REST things))

(defun line-print (col &rest things)
  (skip-to col)
  (apply #'PRINC-REST things))

(defun cursorset (line col &optional (item '*NOT-SUPPLIED*) keyword &aux length)
  (setq col (* col 8.)
	line (* line 14.))
  (cursorpoint col line)
  (cond ((eq item '*NOT-SUPPLIED*) (tv:sheet-clear-eol *TERMINAL-IO*))
	(t (cond ((and (fixp item) (eq keyword 'CHARS))
		  (setq length item))
		 (t (setq length (+ (flatc item) 2.))))
	   (do ((i 0. (1+ i)))
	       ((>= i length))
	     (tv:sheet-clear-char *TERMINAL-IO*)
	     (write-char #\SPACE))
	   (cursorpoint col line))))

(defun cursorpoint (x y)
  (when (or (< x 0.) (> x *MAX-SCREEN-X*) (< y 0.) (> y *MAX-SCREEN-Y*))
    (ferror nil "Off-screen coordinates."))
  (tv:sheet-set-cursorpos *TERMINAL-IO* x y))


;;; Initializations:

(globalize "AREA-TEST")
(globalize "FLAVOR-OPS")
(globalize "SA")
(globalize "MA")
(globalize "HALT")


;;; End.
