;      -*- Mode:LISP; Package:OBJ; Base:10; Readtable:CL; Syntax: Common-lisp -*-

;; Copyright (C) Gary Drescher 1984, 1985
;; Licensed to and distributed by Lisp Machine, Inc.
;;   See filename "Copyright" for
;; licensing and release information.

(in-package "OBJ")

;;;; Utilities

#+(or lambda symbolics)
(defprop defclassvar "Class variable" si:definition-type-name)

(defmacro DEFCLASSVAR ((sym obj) &optional value)
  `(progn
     (defvar ,sym)
     (establish-class-var ',sym ,obj (obl ,value))))

(defmacro DEFCLASSVARS (obj &body vars)
  `(progn
     ,@(nloop (for-in var vars)
	      (collect (if (symbolp var)
			   `(defclassvar (,var ,obj) )
			   `(defclassvar (,(car var) ,obj)
				         ,(cadr var)))))))

(defun establish-class-var (sym obj value)
  (if (record-source-file-name sym 'defclassvar)
      (ask-aux obj (have sym value))))

#+(or lambda symbolics)
(defprop definstancevar "Instance variable" si:definition-type-name)

(defmacro DEFINSTANCEVAR ((sym obj) &optional value &aux key)
  (flet ((value-fcn (form)	;; See BIND-BUILTIN-INSTANCE-VARS.
	   (cond ((null form) nil)
		 ((symbolp form) `',form)
		 ((not (consp form)) `',form)
		 ((eq (car form) 'quote) `',(ncons (cadr form)))
		 (t `#'(lambda () ,form)))))
    (if (consp sym)
	(setq key (cadr sym)  sym (car sym))
	(setq key sym))
    `(progn
       (defvar ,sym)
       (establish-instance-var ',sym ',key ,obj ,(value-fcn value)))))

(defmacro DEFINSTANCEVARS (obj &body vars)
  `(progn
     ,@(nloop (for-in var vars)
	      (collect (if (symbolp var)
			   `(definstancevar (,var ,obj) )
			   `(definstancevar
			      (,(car var) ,obj) ,(cadr var)))))))

; VAL-FCN is either a function or an atomic form.
(defun establish-instance-var (sym key obj val-fcn &aux old)
  (check-type sym symbol)
  (if (record-source-file-name sym 'definstancevar)
      (if (env-instance-var-inits (own-env obj))
	  (if (setq old (assq sym (env-instance-var-inits (own-env obj))))
	      (rplacd old (list key val-fcn))
	      (nconc (env-instance-var-inits (own-env obj))
		     (ncons (list sym key val-fcn))))
	  (setf (env-instance-var-inits (own-env obj))
		(ncons (list sym key val-fcn)))))
  nil)

(defun UNDEF-INSTANCEVAR (sym obj)
  (check-obj obj)
  (setf (env-instance-var-inits (own-env obj))
	(remq (assq sym (env-instance-var-inits (own-env obj)))
	      (env-instance-var-inits (own-env obj)))))

(defun bind-builtin-instance-vars (obj args &aux override)
  (nloop
    (for-in env (obj-envs obj))	;; See DEFINSTANCEVAR.
    (nloop (for-in sym-key-init (env-instance-var-inits env))
	    (for sym (car sym-key-init))
	    (for key (cadr sym-key-init))
	    (for init (caddr sym-key-init))
	   (unless (own? sym obj)
	     (if (setq override (memq key args))
		 (ask-aux obj (have sym (cadr override)))
	         (ask-aux obj
		   (have sym (cond ((functionp init) (funcall init))
				   ((symbolp init) (get-sym-val init))
				   ((consp init) (car init))
				   (t init)))))))))

(defun INSTANCEVAR-DEFS (&optional (obj *object))
  (copy-list (env-instance-var-inits (own-env obj))))

#+(or lambda symbolics)
(defprop defkind "Object" si:definition-type-name)

(defmacro DEFKIND (name &rest superclasses)
  `(progn
     (defvar ,name)
     (%defkind ',name (mapcar #'symbol-value ',(copy-list superclasses)))))

(defun DEFINE-KIND (name &rest superclasses)
  (%defkind name (copy-list superclasses)))

(defun %defkind (name superclasses
		      &aux (obj (if (boundp name) (symbol-value name))))
  (when (record-source-file-name name 'defkind)
    (if (object? obj)
	(setq obj (apply #'remake-obj obj superclasses))
        (setq obj (%kindof t t nil superclasses)))
    (ask-aux obj (have 'class-name name))
    (set name obj)))

(defun SPECIALIZATIONS (&optional (obj *object))
  (when obj
    (remq obj (append (copy-list (env-class-objs (own-env obj)))
		      (copy-list (env-instance-objs (own-env obj)))))))

;;;; Description

(defun CURRENT-OBJ ()
  (if (eq *object *internal-global-obj)
      nil
      *object))

(defun WHAT (&optional (obj *object) &aux subs subinsts)
  (check-obj obj)
  (if (global-obj? obj)
      (print "The global object.")
      (apply #'format t
	     "~%Object ~s, including~#[ only the global object~
           ~; ~s~; ~s and ~s~:;~@{~#[~; and~] ~s~^,~}~]." 
	     obj (base-objs obj)))
  (setq subs (specializations obj))
  (setq subinsts (nloop (collecting t)
			(for-in sub subs)
			(if (not (obj-class? sub))
			    (collect sub))
		        (or (obj-class? sub)
			    (setq subs (remq sub subs)))))
  (if subinsts
      (format t "~% ~d instance~:p." (length subinsts))
      (if subs (terpri)))
  (if subs
      (apply #'format t
	     " Class specialization~p~#[~
	     ~; ~s~; ~s and ~s~:;~@{~#[~; and~] ~s~^,~}~]."
	     (length subs) subs)))

(defun SHOW (&optional (obj *object))
  (what obj)
  (unless (null obj) (show1 (own-env obj))))

(defun show1 (env &aux (vals (env-val-bindings env))
	               (fcns (env-fcn-bindings env)))
  (iff (zerop (table-count vals))
       (format t "~%  No variables bound.")
    (format t "~%  Variables: ")
    (bindings-map #'(lambda (binding) (format t "~s " (nonglobal-binding-sym binding)))
		  vals))
  (iff (zerop (table-count fcns))
       (format t "~%  No functions bound.")
    (format t "~%  Functions: ")
    (bindings-map #'(lambda (binding) (format t "~s " (nonglobal-binding-sym binding)))
		  fcns))
  (when (env-instance-var-inits env)
	(format t "~%  Instance variable inits: ")
        (nloop (for-in sym-key-init (env-instance-var-inits env))
	       (format t "~s " (car sym-key-init))))
  nil)

(defun SHOW-VALS (&optional (obj *object))
  (what obj)
  (unless (global-obj? obj)
    (format t "~% Values:")
    (bindings-map #'(lambda (binding)
		      (format t "~%  ~s: ~s" (nonglobal-binding-sym binding)
			                     (binding-val binding)))
		  (env-val-bindings (own-env obj))))
  nil)

(defun SHOW-ALL (&optional (obj *object))
  (what obj)
  (unless (global-obj? obj)
    (nloop (for-in env (obj-envs obj))
	   (format t "~%~s: " (env-object env))
	   (show1 env))))

(defun OWN (&optional (obj *object))
  (check-obj obj)
  (unless (global-obj? obj)
      (bindings-map-return #'(lambda (binding) (nonglobal-binding-sym binding))
			   (env-val-bindings (own-env obj)))))

(defun OWN? (sym &optional (obj *object) &aux link)
  (check-obj obj)
  (or (and (global-obj? obj) (boundp sym))
      (and (setq link (get-val-link? sym))
	   (not (null (binding-from-env link (own-env obj)))))))

(defun WHERE (sym &optional (obj *object) &aux link)
  (check-obj obj)
  (if (or (global-obj? obj) (null (setq link (get-val-link? sym))))
      nil
      (some #'(lambda (env) (if (binding-from-env link env)
				(env-object env)))
	    (obj-envs obj))))

(defun THERE? (sym &optional (obj *object) &aux link)
  (check-obj obj)
  (or (and (setq link (get-val-link? sym))
	   (not (null (some #'(lambda (env) (binding-from-env link env))
			    (obj-envs obj)))))
      (boundp sym)))

(defun MAPC-OWN (fcn &optional (obj *object))
  (check-obj obj)
  (unless (global-obj? obj)
    (bindings-map #'(lambda (binding) (funcall fcn (nonglobal-binding-sym binding)))
		  (env-val-bindings (own-env obj)))))

(defun MAPCAR-OWN (fcn &optional (obj *object))
  (check-obj obj)
  (unless (global-obj? obj)
    (bindings-map-return #'(lambda (binding) (funcall fcn (nonglobal-binding-sym binding)))
      (env-val-bindings (own-env obj)))))

(defun FOWN (&optional (obj *object))
  (check-obj obj)
  (unless (global-obj? obj)
    (bindings-map-return #'(lambda (binding) (nonglobal-binding-sym binding))
			 (env-fcn-bindings (own-env obj)))))

(defun FOWN? (sym &optional (obj *object) &aux link)
  (check-obj obj)
  (or (and (global-obj? obj) (fboundp sym))
      (and (setq link (get-fcn-link? sym))
	   (not (null (fbinding-from-env link (own-env obj)))))))

(defun FWHERE (sym &optional (obj *object) &aux link)
  (check-obj obj)
  (if (or (global-obj? obj) (null (setq link (get-fcn-link? sym))))
      nil
      (nloop (collecting t)
	     (for-in env (obj-envs obj))
	     (if (not (null (fbinding-from-env link env)))
		 (collect (env-object env))))))

(defun FTHERE? (sym &optional (obj *object) &aux link)
  (check-obj obj)
  (or (and (setq link (get-fcn-link? sym))
	   (not (null (some #'(lambda (env) (binding-from-env link env))
			    (obj-envs obj)))))
      (fboundp sym)))

(defun FDOC (sym &optional (obj *object) &aux link)
  (check-obj obj)
  (iff (or (global-obj? obj) (null (setq link (get-fcn-link? sym))))
       nil
    (format t "~%Function binding of ~s in ~s is found in ~%" sym obj)
    (nloop (for-in env (obj-envs obj))
	   (for bind (fbinding-from-env link env))
	   (if bind (format t "~s, " (env-object env))))
    (if (fboundp sym) (format t "and the global object."))))

(defun MAPC-FOWN (fcn &optional (obj *object))
  (check-obj obj)
  (unless (global-obj? obj)
    (bindings-map #'(lambda (binding) (funcall fcn (nonglobal-binding-sym binding)))
	  (env-fcn-bindings (car (obj-envs obj))))))

(defun MAPCAR-FOWN (fcn &optional (obj *object))
  (check-obj obj)
  (unless (global-obj? obj)
    (bindings-map-return #'(lambda (binding) (funcall fcn (nonglobal-binding-sym binding)))
	  (env-fcn-bindings (car (obj-envs obj))))))
