;;; -*- Mode:LISP; Package:LISP-INTERNALS; Readtable:CL; Base:10 -*-

;;;********************************************************************************
; Required Common Lisp Constants

(defconstant char-code-limit 256.
  "Character code values must be less than this.")

(defconstant char-font-limit 16.
  "Font codes in characters must be less than this.")

(defconstant char-bits-limit 16.
  "All the special bits in a character must be less than this.
They are Control, Meta, Super and Hyper.")

;;;********************************************************************************
; Other useful constants

(defconstant %%ch-char 	     (byte 8. 0.)    "Position of character value")
(defconstant %%ch-bits       (byte 4. 8.)    "Position of bits value")
(defconstant %%ch-font       (byte 4. 12.)   "Position of font value")
(defconstant %%ch-font-and-bits  (byte 8. 8.)    "Position of font and bits value")
(defconstant %%ch-fat	     (byte 16. 0.)   "Position of all the stuff in a character")

(defconstant char-control-bit 1
  "This bit within the bits of a character, is the Control bit.")

(defconstant char-meta-bit 2
  "This bit, within the bits of a character, is the Meta bit.")

(defconstant char-super-bit 4
  "This bit, within the bits of a character, is the Super bit.")

(defconstant char-hyper-bit 8.
  "This bit, within the bits of a character, is the Hyper bit.")

;;; defsubst
(defun %char-int (c)
  (hw:ldb c %%ch-char 0))

;;; defsubst
(defun %fat-char-int (c)
  (hw:ldb c %%ch-fat 0))
 
(defconstant char-int-0       #.(lisp:char-int #\0))
(defconstant char-int-9       #.(lisp:char-int #\9))
(defconstant char-int-lower-a #.(lisp:char-int #\a))
(defconstant char-int-lower-z #.(lisp:char-int #\z))
(defconstant char-int-upper-a #.(lisp:char-int #\A))
(defconstant char-int-upper-z #.(lisp:char-int #\Z))

;;;********************************************************************************


(defsubst CHAR-CODE (c)
  (li:%trap-if-not-character c)
  (hw:ldb c %%ch-char 0))

(defsubst CHAR-BITS (c)
  (li:%trap-if-not-character c)
  (hw:ldb c %%ch-bits 0))

(defsubst CHAR-FONT (c)
  (li:%trap-if-not-character c)
  (hw:ldb c %%ch-font 0))

(defsubst CHAR-INT (c)
  (li:%trap-if-not-character c)
  (hw:dpb-boxed c %%ch-fat 0))

(defsubst INT-CHAR (i)
  (hw:dpb-boxed i %%ch-fat gr:*dtp-character*))


(defun CHARACTERP (c)
  "Returns T if c is a character, otherwise nil"
  (vinc:characterp c))

(defun STRING-CHAR-P (c)
  "Returns T if c is a character with zero font and bit codes"
  (li:%trap-if-not-character c)
  (zerop (hw:ldb c %%ch-font-and-bits 0)))

(defun GRAPHIC-CHAR-P (c)
  "Returns T if c is a character with zero font and bit codes and a standard print reresentation"
  (li:%trap-if-not-character c)
  (and (zerop (hw:ldb c %%ch-font-and-bits 0))
       (<= (hw:ldb c %%ch-char 0) 127.)))

(defun STANDARD-CHAR-P (c)
  "Returns T if c is a is one of the standard ASCII printable chars, space, or newline"
  (and
    (zerop (hw:ldb c %%ch-font-and-bits 0))		;no special bits or fonts
    (or (and (char>= c #\Space)
	     (char<= c #\~))
	(char= c #\Newline))))

(defsubst UPPER-CASE-P (c)
  "Return T if c is an upper case character A-Z"
  (char<= #\A c #\Z))

(defsubst LOWER-CASE-P (c)
  "Return t if c is a lower case character a-z"
  (char<= #\a c #\z))

(defun ALPHA-CHAR-P (c)
  "Returns T if c is a alphabetic character"
  (and (zerop (hw:ldb c %%ch-font-and-bits 0))		;no special bits or fonts
       (or (char<= #\A c #\Z)
	   (char<= #\a c #\z))))

(defun BOTH-CASE-P (c)
  (or (upper-case-p c)
      (lower-case-p c)))


(defun DIGIT-CHAR-P (c &optional (radix 10.))
  "Returns value if c is a numeric character"
  (and
    (zerop (hw:ldb c %%ch-font-and-bits 0))		;no special bits or fonts
    (let* ((char (%char-int c))
	   (n (cond
		((and (char>= c #\0) (char<= c #\9))
		 (- char char-int-0))
		((and (char>= c #\A) (char<= c #\Z))
		 (- char (- char-int-upper-a 10.)))
		((and (char>= c #\a) (char<= c #\z))
		 (- char (- char-int-lower-a 10.)))
		(t (return-from digit-char-p nil)))))
      (if (< n radix)
	  n
	nil))))

(defun DIGIT-CHAR (weight &optional (radix 10) (font 0))
  (if (< weight radix)
      (make-char (array:svref "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" weight) 0 font)
    nil))

(defun ALPHANUMERICP (c)
  "Return T if c is either an alphabetic or numeric character"
  (or (alpha-char-p c)
      (digit-char-p c)))


(defun CHAR-UPCASE (c)
  (li:%trap-if-not-character c)
  (let ((char (char-code c)))
    (if (and (>= char char-int-lower-a) (<= char char-int-lower-z))
	(hw:dpb (- char (- char-int-lower-a char-int-upper-a)) %%ch-char c)
      c)))

;(defun CHAR-UPCASE (c)
;  (li:%trap-if-not-character c)
;  (if (char<= #\a c #\z)
;      (int-char (- (char-int c)
;		   (- (char-int #\a)
;		      (char-int #\A))))
;    c))

(defun CHAR-DOWNCASE (c)
  (li:%trap-if-not-character c)
  (let ((char (char-code c)))
    (if (and (>= char char-int-upper-a) (<= char char-int-upper-z))
	(hw:dpb (+ char (- char-int-lower-a char-int-upper-a)) %%ch-char c)
      c)))

;(defun CHAR-UPCASE (c)
;  (if (char<= #\A c #\Z)
;      (int-char (+ (char-int c)
;		   (- (char-int #\a)
;		      (char-int #\A))))
;    c))


(defun CHAR-BIT (c bit-name)
  "T if the bit specified by BIT-NAME (a keyword) is on in CHAR.
BIT-NAME can be :CONTROL, :META, :SUPER or :HYPER."
  (li:%trap-if-not-character c)
  (hw:32logbitp
    (+ (byte-position %%ch-bits)
       (case bit-name
	 (:CONTROL char-control-bit)
	 (:META    char-meta-bit)
	 (:SUPER   char-super-bit)
	 (:HYPER   char-hyper-bit)
	 (t (error "Illegal bit name to CHAR-BIT: ~s" bit-name))))
    c))

(defun SET-CHAR-BIT (c bit-name value)
  (li:%trap-if-not-character c)
  (hw:dpb-boxed
    value
    (byte 1. (cond
	       ((eq bit-name ':control)
		(+ char-control-bit (byte-position %%ch-bits)))
	       ((eq bit-name ':meta)
		(+ char-meta-bit (byte-position %%ch-bits)))
	       ((eq bit-name ':super)
		(+ char-super-bit (byte-position %%ch-bits)))
	       ((eq bit-name ':hyper)
		(+ char-hyper-bit (byte-position %%ch-bits)))
	       (t (trap::illop "Illegal bit name to CHAR-BIT"))))
    c))

;;; compiler rewiters for the character predicates are defined in "k-sys:FLEABIT;REWRITE.LISP".
;;; That's why it is O.K. to refer to the comparison function in these definitions.  Normally
;;; the compiler rewriters will implement these operations.  The definitions here exist to
;;; support use of these comparison operations as callable functions, for example with FUNCALL,
;;; APPLY and the interpreter.

(defmacro define-n-arg-compare-function (function)
  `(defun ,function (&rest args)
     (cond ((null args) t)
	   ((null (cdr args)) t)
	   (t (do ((args args (cdr args)))
		  ((null (cdr args)) t) 
		(unless (,function (first args) (second args))
		  (return nil)))))))

(define-n-arg-compare-function li:char<)
(define-n-arg-compare-function li:char<=)
(define-n-arg-compare-function li:char=)
(define-n-arg-compare-function li:char>=)
(define-n-arg-compare-function li:char>)

(defun CHAR/= (&rest characters)
  (cond ((null characters) t)
	((null (cdr characters)) t)
	(t (block foo
	     (do ((c1 characters (cdr c1)))
		 ((null (cdr c1)) (return-from foo t))
	       (do ((c2 (cdr c1) (cdr c2)))
		   ((null c2))
		 (unless (char/= (car c1) (car c2))
		   (return-from foo nil))))))))

(defmacro define-char-????-predicate-functions (n-arg-name 2-arg-name 3-arg-name primitive)
  `(progn

     (defun ,2-arg-name (char1 char2)
       (,primitive (char-upcase char1)
		       (char-upcase char2)))

     (defun ,3-arg-name (char1 char2 char3)
       (let ((c2 (char-upcase char2)))
	 (and (,primitive (char-upcase char1) c2)
	      (,primitive c2 (char-upcase char3)))))
       
     (defun ,n-arg-name (first-char &rest chars)
       (if (null chars)
	   t
	 (do* ((char (char-upcase first-char)
		     next-char)
	       (chars chars (cdr chars))
	       (next-char (char-upcase (car chars))
			  (char-upcase (car chars))))
	      ((null (cdr chars))
	       (,primitive char next-char))
	   (unless (,primitive char next-char)
	     (return nil)))))
       ))

(define-char-????-predicate-functions char-equal char-equal-2-args
				      char-equal-3-args li:%char-equal)
(define-char-????-predicate-functions char-lessp char-lessp-2-args
				      char-lessp-3-args li:%char-lessp)
(define-char-????-predicate-functions char-greaterp char-greaterp-2-args
				      char-greaterp-3-args li:%char-greaterp)
(define-char-????-predicate-functions char-not-lessp char-not-lessp-2-args
				      char-not-lessp-3-args li:%char-not-lessp)
(define-char-????-predicate-functions char-not-greaterp char-not-greaterp-2-args
				      char-not-greaterp-3-args li:%char-not-greaterp)

(defun char-not-equal-2-args (char1 char2)
  (li:%char-not-equal (char-upcase char1)
		      (char-upcase char2)))

(defun char-not-equal-3-args (char1 char2 char3)
  (let ((c1 (char-upcase char1))
	(c2 (char-upcase char2))
	(c3 (char-upcase char3)))
  (and (li:%char-not-equal c1 c2)
       (li:%char-not-equal c1 c3)
       (li:%char-not-equal c2 c3))))

(defun char-not-equal (char &rest chars)
  (do ((chars (mapcar #'char-upcase (cons char chars))
	      (cdr chars)))
      ((null chars) t)
    (do ((cs (cdr chars) (cdr cs)))
	((null cs))
      (unless (li:%char-not-equal (car chars) (car cs))
	(return-from char-not-equal nil)))))


(defun code-char (code &optional (bits 0) (font 0))
  (make-char (int-char code) bits font))

(defun CHARACTER (thing)
  "Coerce THING into a character"
  (cond
    ((vinc:characterp thing)	thing)
    ((stringp thing) (if (= 1 (array:%string-length thing))
			 (array:svref thing 0)
		       (error "The string ~s is the wrong length to be coerced into a character" thing)))
    ((vinc:symbolp thing) (let ((pname (symbol:symbol-name thing)))
		       (if (= 1 (array:%string-length thing))
			   (array:svref thing 0)
			 (error "The symbol ~s has a print name that is the wrong length to be coerced into a character" thing))))
    ((vinc:integerp thing) (int-char thing))
    (t		(error "The object ~s can not be coerced into a character" thing))))

(defun MAKE-CHAR (char &optional (bits 0) (font 0))
  (if (vinc:characterp char)
      (hw:dpb-boxed font %%ch-font (hw:dpb-boxed bits %%ch-bits char))
    (error "MAKE-CHAR expected a character, not ~a" char)))


(defun CHAR-NAME (c) ;;;; Fix ME
  nil)
