;;; -*- Mode:LISP; Package:TV; Readtable:ZL; Base:10 -*-

(DEFUN-METHOD DRAW-STRING-HORIZONTAL GRAPHICS-MIXIN
	      (STRING LAST-INDEX FROM-X Y EXTRA EXTRA-PER-GAP PERIOD
	       SHORTFALL BACK-P FAT-P ALU FONT-TABLE DEFAULT-FONT)
  (MULTIPLE-VALUE-BIND (X-EDGE Y-EDGE) (SEND SELF ':INSIDE-SIZE)
    (LOOP FOR I FROM 0 TO LAST-INDEX
	  FOR CHAR = (IF BACK-P (AREF STRING (- LAST-INDEX I)) (AREF STRING I))
	  FOR FONT = (IF FAT-P (AREF FONT-TABLE (LDB %%ch-font CHAR)) DEFAULT-FONT)
	  FOR FONT-CHAR-WIDTH-TABLE = (FONT-CHAR-WIDTH-TABLE FONT)
	  FOR FONT-BASELINE = (FONT-BASELINE FONT)
	  FOR FONT-HEIGHT = (FONT-CHAR-HEIGHT FONT)
	  WITH ADJUST = (COND ((MINUSP EXTRA) -1)
			      ((ZEROP EXTRA) 0)
			      (T 1))
	  FOR CONDITION = (AND PERIOD (NOT (ZEROP SHORTFALL)) (ZEROP (REMAINDER I PERIOD)))
	  FOR X = FROM-X THEN (IF BACK-P
				  (- X CH-WIDTH EXTRA-PER-GAP
				     (COND (CONDITION (SETQ SHORTFALL (- SHORTFALL ADJUST))
						      ADJUST)
					   (T 0)))
				  (+ X CH-WIDTH EXTRA-PER-GAP
				     (COND (CONDITION (SETQ SHORTFALL (- SHORTFALL ADJUST))
						      ADJUST)
					   (T 0))))
	  FOR CH-WIDTH = (IF FONT-CHAR-WIDTH-TABLE
			       (AREF FONT-CHAR-WIDTH-TABLE (LDB %%CH-CHAR CHAR))
			       (FONT-CHAR-WIDTH FONT))
	  DO (IF BACK-P
		 (DRAW-CHAR-TO-LEFT (LDB %%CH-CHAR CHAR) FONT X Y FONT-HEIGHT CH-WIDTH
				    FONT-BASELINE SELF X-EDGE Y-EDGE ALU)
		 (DRAW-CHAR-TO-RIGHT (LDB %%CH-CHAR CHAR) FONT X Y FONT-HEIGHT CH-WIDTH
				     FONT-BASELINE SELF X-EDGE Y-EDGE ALU))
	FINALLY (RETURN X Y))))

;;; Draw a (perhaps multi-font) string vertically, expanding or compressing
(DEFUN-METHOD DRAW-STRING-VERTICAL GRAPHICS-MIXIN
	      (STRING LAST-INDEX FROM-Y X EXTRA EXTRA-PER-GAP PERIOD SHORTFALL
	       FAT-P ALU FONT-TABLE DEFAULT-FONT RIGHT-P)
  (MULTIPLE-VALUE-BIND (X-EDGE Y-EDGE) (SEND SELF ':INSIDE-SIZE)
    (LOOP FOR I FROM 0 TO LAST-INDEX
	  FOR CHAR = (AREF STRING I)
	  FOR FONT = (IF FAT-P (AREF FONT-TABLE (LDB %%CH-FONT CHAR)) DEFAULT-FONT)
	  FOR FONT-CHAR-WIDTH-TABLE = (FONT-CHAR-WIDTH-TABLE FONT)
	  FOR FONT-BASELINE = 0			;to fake out character drawing code
	  FOR FONT-HEIGHT = (FONT-CHAR-HEIGHT FONT)
	  WITH ADJUST = (COND ((MINUSP EXTRA) -1)
			      ((ZEROP EXTRA) 0)
			      (T 1))
	  FOR CONDITION = (AND PERIOD (NOT (ZEROP SHORTFALL)) (ZEROP (REMAINDER I PERIOD)))
	  FOR Y = FROM-Y THEN (+ Y FONT-HEIGHT EXTRA-PER-GAP
				 (COND (CONDITION (SETQ SHORTFALL (- SHORTFALL ADJUST))
						  ADJUST)
				       (T 0)))
	  FOR CH-WIDTH = (IF FONT-CHAR-WIDTH-TABLE
			       (AREF FONT-CHAR-WIDTH-TABLE (LDB %%CH-CHAR CHAR))
			       (FONT-CHAR-WIDTH FONT))
	  DO (IF RIGHT-P
		 (DRAW-CHAR-TO-RIGHT (LDB %%CH-CHAR CHAR) FONT X Y FONT-HEIGHT CH-WIDTH
				     FONT-BASELINE SELF X-EDGE Y-EDGE ALU)
		 (DRAW-CHAR-TO-LEFT (LDB %%CH-CHAR CHAR) FONT X Y FONT-HEIGHT CH-WIDTH
				    FONT-BASELINE SELF X-EDGE Y-EDGE ALU))
	FINALLY (RETURN X Y))))

;;; Draw a (perhaps multi-font) string diagonally, expanding or compressing
(DEFUN-METHOD DRAW-STRING GRAPHICS-MIXIN
	      (STRING LAST-INDEX FROM-X FROM-Y TO-X TO-Y X-EXTRA Y-EXTRA
	       X-EXTRA-PER-GAP Y-EXTRA-PER-GAP X-PERIOD Y-PERIOD SHORTFALL-X
	       SHORTFALL-Y BACK-P FAT-P ALU FONT-TABLE DEFAULT-FONT
	       DELTA-X DELTA-Y STRETCH-P)
  (MULTIPLE-VALUE-BIND (X-EDGE Y-EDGE)
      (SEND SELF ':INSIDE-SIZE)
      (LOOP FOR I FROM 0 TO LAST-INDEX
	    FOR CHAR = (IF BACK-P (AREF STRING (- LAST-INDEX I)) (AREF STRING I))
	    FOR FONT = (IF FAT-P (AREF FONT-TABLE (LDB %%CH-FONT CHAR)) DEFAULT-FONT)
	    FOR FONT-CHAR-WIDTH-TABLE = (FONT-CHAR-WIDTH-TABLE FONT)
	    FOR FONT-BASELINE = (FONT-BASELINE FONT)
	    WITH X-ADJUST = (COND ((MINUSP X-EXTRA) -1)
				  ((ZEROP X-EXTRA) 0)
				  (T 1))
	    AND Y-ADJUST = (COND ((MINUSP Y-EXTRA) -1)
				 ((ZEROP Y-EXTRA) 0)
				  (T 1))
	    WITH UP-P = (LESSP TO-Y FROM-Y)
	    FOR CONDITION-X = (AND X-PERIOD (NOT (ZEROP SHORTFALL-X))
				   (ZEROP (REMAINDER I X-PERIOD)))
	    AND CONDITION-Y = (AND Y-PERIOD (NOT (ZEROP SHORTFALL-Y))
				   (ZEROP (REMAINDER I Y-PERIOD)))
	    FOR LX = FROM-X THEN (IF BACK-P
				    (- LX CH-WIDTH X-EXTRA-PER-GAP ;(break back)
				       (COND (CONDITION-X (SETQ SHORTFALL-X
								(- SHORTFALL-X X-ADJUST))
							X-ADJUST)
					     (T 0)))
				    (+ LX CH-WIDTH X-EXTRA-PER-GAP ;(break forward)
				       (COND (CONDITION-X (SETQ SHORTFALL-X
								(- SHORTFALL-X X-ADJUST))
							X-ADJUST)
					     (T 0))))
	    FOR LY = FROM-Y THEN (IF UP-P
				    (- LY FONT-HEIGHT Y-EXTRA-PER-GAP ;(break up)
				       (COND (CONDITION-Y (SETQ SHORTFALL-Y
								(- SHORTFALL-Y Y-ADJUST))
							Y-ADJUST)
					     (T 0)))
				    (+ LY FONT-HEIGHT Y-EXTRA-PER-GAP ;(break down)
				       (COND (CONDITION-Y (SETQ SHORTFALL-Y
								(- SHORTFALL-Y Y-ADJUST))
							Y-ADJUST)
					     (T 0))))
	    FOR CH-WIDTH = (IF FONT-CHAR-WIDTH-TABLE
				 (AREF FONT-CHAR-WIDTH-TABLE (LDB %%CH-CHAR CHAR))
				 (FONT-CHAR-WIDTH FONT))
	    FOR FONT-HEIGHT = (FONT-CHAR-HEIGHT FONT)
	    WITH X-LONGER-P = (GREATERP (ABS DELTA-X) (ABS DELTA-Y))
	    WITH TEST = (// (IF X-LONGER-P (ABS DELTA-X) (ABS DELTA-Y)) 2)
	    WITH X = FROM-X AND Y = FROM-Y
	    WITH RIGHT-P = (GREATERP TO-X FROM-X)
	    DO (MULTIPLE-VALUE (X Y TEST)
		 (NEXT-FREE-PIXEL X Y TEST DELTA-X DELTA-Y
				  X-LONGER-P LX LY UP-P RIGHT-P STRETCH-P))
	    (IF RIGHT-P (DRAW-CHAR-TO-RIGHT (LDB %%CH-CHAR CHAR) FONT X Y FONT-HEIGHT
					    CH-WIDTH FONT-BASELINE SELF X-EDGE Y-EDGE ALU)
		(DRAW-CHAR-TO-LEFT (LDB %%CH-CHAR CHAR) FONT X Y FONT-HEIGHT CH-WIDTH
				   FONT-BASELINE SELF X-EDGE Y-EDGE ALU)))))

;;; Draw a character string at an arbitrary place in a window.  Only
;;; printing characters (including space) are allowed in the string.  The
;;; left baseline points of the characters lie on the line defined by FROM-X, FROM-Y
;;; TOWARD-X and TOWARD-Y.  If TOWARD-X is less than FROM-X, the string is written backwards
;;; (i.e. the right-most character is at FROM-X, FROM-Y and the right baseline points
;;; are used); otherwise the leftmost character starts at FROM-X, FROM-Y.
;;; If STRETCH-P is true, the string will end near END-X by END-Y (though maybe no
;;; exactly, because of roundoff error.)
;;; The given ALUF and FONT are used.  Art-fat-strings (with font changes) are OK.
;;; The stopping location is returned (as two values).

;;; Problems:
;;; extreme slopes cause character overstrikes
;;; doesn't check for illegal characters
;;; doesn't use vsp in vertical space calculation
;;; doesn't skip everything if string lies completely outside screen
;;; doesn't apportion compress/squeeze space with regard to character size
;;; should really return place to start continuation of string, rather than coordinates
;;;    of last char

(DEFMETHOD (GRAPHICS-MIXIN :DRAW-STRING)
	   (STRING FROM-X FROM-Y &OPTIONAL (TOWARD-X (1+ FROM-X)) (TOWARD-Y FROM-Y)
					   (STRETCH-P NIL) (FONT (SHEET-CURRENT-FONT SELF))
					   (ALU CHAR-ALUF))
  "Draws a character string between given points.
If stretch-p is non-nil, the characters will be spaced to closely fit
between the points;  otherwise, they merely use the line defined by
the points.  If toward-x is less than from-x, the string ends at from-x from-y;
otherwise it starts there and in both cases it will read from left to right."

 (LET* ((STRING (STRING STRING))			;convert possible randomness to string
        (LENGTH (ARRAY-ACTIVE-LENGTH STRING))
        (END-DISPLACEMENT (1- LENGTH))		;highest aref index and also number of gaps
						;between characters
        (DELTA-X (- TOWARD-X FROM-X))
        (DELTA-Y (- TOWARD-Y FROM-Y))
        (FONT-TABLE (SEND SELF ':FONT-MAP))
        (FAT-STRING-P (EQ 'ART-FAT-STRING (ARRAY-TYPE STRING))) ;predicate for 16-bit strings
        (BACKWARDS-P (MINUSP DELTA-X))		;predicate for backwards strings
        (VERTICAL-P (ZEROP DELTA-X))
        (HORIZONTAL-P (ZEROP DELTA-Y)))
  (COND (HORIZONTAL-P
	 (COND (STRETCH-P
		(MULTIPLE-VALUE-BIND (X-DESIRED IGNORE)
		    (STRING-NORMAL-SIZE STRING END-DISPLACEMENT FAT-STRING-P FONT-TABLE FONT)
		  (LET* ((EXCESS-X-SPACE (- (ABS DELTA-X) (ABS X-DESIRED)))	;+ = expand
			 (EXCESS-X-PER-GAP (// EXCESS-X-SPACE END-DISPLACEMENT))
			 (SHORTFALL-X (- EXCESS-X-SPACE
					 (* END-DISPLACEMENT EXCESS-X-PER-GAP)))
			 (X-PERIOD (IF (ZEROP SHORTFALL-X) NIL
				       (// END-DISPLACEMENT SHORTFALL-X))))
		    (DRAW-STRING-HORIZONTAL STRING END-DISPLACEMENT FROM-X FROM-Y
					    EXCESS-X-SPACE EXCESS-X-PER-GAP X-PERIOD
					    SHORTFALL-X BACKWARDS-P FAT-STRING-P ALU
					    FONT-TABLE FONT))))
	       (T (DRAW-STRING-HORIZONTAL STRING END-DISPLACEMENT FROM-X FROM-Y 0 0 NIL 0
					  BACKWARDS-P FAT-STRING-P ALU FONT-TABLE FONT))))
	(VERTICAL-P
	 (LET* ((Y-START (MIN FROM-Y TOWARD-Y))	;canonicalize vertical to always go down
		(Y-END (MAX FROM-Y TOWARD-Y))
		(RIGHT-FLAG (LESSP FROM-Y TOWARD-Y))  ;put chars on right side of line if down
		(DELTA-Y (- Y-END Y-START)))
	   (COND (STRETCH-P
		  (MULTIPLE-VALUE-BIND (IGNORE Y-DESIRED)
		      (STRING-NORMAL-SIZE STRING END-DISPLACEMENT FAT-STRING-P
					  FONT-TABLE FONT)
		    (LET* ((EXCESS-Y-SPACE (- (ABS DELTA-Y) (ABS Y-DESIRED)))
			   (EXCESS-Y-PER-GAP (// EXCESS-Y-SPACE END-DISPLACEMENT))
			   (SHORTFALL-Y (- EXCESS-Y-SPACE (* END-DISPLACEMENT
							     EXCESS-Y-PER-GAP)))
			   (Y-PERIOD (IF (ZEROP SHORTFALL-Y) NIL (// END-DISPLACEMENT
								     SHORTFALL-Y))))
		      (DRAW-STRING-VERTICAL STRING END-DISPLACEMENT Y-START FROM-X
					    EXCESS-Y-SPACE EXCESS-Y-PER-GAP Y-PERIOD
					    SHORTFALL-Y FAT-STRING-P ALU FONT-TABLE FONT
					    RIGHT-FLAG))))
		 (T (DRAW-STRING-VERTICAL STRING END-DISPLACEMENT Y-START FROM-X
					  0 0 NIL 0 FAT-STRING-P ALU FONT-TABLE FONT
					  RIGHT-FLAG)))))
	(T (COND (STRETCH-P
		  (MULTIPLE-VALUE-BIND (X-DESIRED Y-DESIRED)
		      (STRING-NORMAL-SIZE STRING END-DISPLACEMENT FAT-STRING-P
					  FONT-TABLE FONT)
		    (LET* ((EXCESS-X-SPACE (- (ABS DELTA-X) (ABS X-DESIRED)))
			   (EXCESS-X-PER-GAP (// EXCESS-X-SPACE END-DISPLACEMENT))
			   (SHORTFALL-X (- EXCESS-X-SPACE (* END-DISPLACEMENT
							     EXCESS-X-PER-GAP)))
			   (X-PERIOD (IF (ZEROP SHORTFALL-X) NIL (// END-DISPLACEMENT
								     SHORTFALL-X)))
			   (EXCESS-Y-SPACE (- (ABS DELTA-Y) (ABS Y-DESIRED)))
			   (EXCESS-Y-PER-GAP (// EXCESS-Y-SPACE END-DISPLACEMENT))
			   (SHORTFALL-Y (- EXCESS-Y-SPACE (* END-DISPLACEMENT
							     EXCESS-Y-PER-GAP)))
			   (Y-PERIOD (IF (ZEROP SHORTFALL-Y) NIL (// END-DISPLACEMENT
								     SHORTFALL-Y))))
		      (DRAW-STRING STRING END-DISPLACEMENT FROM-X FROM-Y TOWARD-X TOWARD-Y
				   EXCESS-X-SPACE EXCESS-Y-SPACE EXCESS-X-PER-GAP
				   EXCESS-Y-PER-GAP X-PERIOD Y-PERIOD SHORTFALL-X SHORTFALL-Y
				   BACKWARDS-P FAT-STRING-P ALU FONT-TABLE FONT
				   DELTA-X DELTA-Y STRETCH-P))))
		 (T (DRAW-STRING STRING END-DISPLACEMENT FROM-X FROM-Y TOWARD-X TOWARD-Y
				 0 0 0 0
				 NIL NIL	0 0 BACKWARDS-P FAT-STRING-P ALU FONT-TABLE
				 FONT DELTA-X DELTA-Y STRETCH-P)))))))

;Go through a string (perhaps multi-font) accumulating character height and width
(DEFUN STRING-NORMAL-SIZE (STRING LAST-INDEX FAT-P FONT-MAP DEFAULT-FONT)
  (LOOP FOR I FROM 0 TO LAST-INDEX
	FOR CHAR = (AREF STRING I)
	FOR FONT = (IF FAT-P (AREF FONT-MAP (LDB %%CH-FONT CHAR)) DEFAULT-FONT)
	FOR FONT-CHAR-WIDTH-TABLE = (FONT-CHAR-WIDTH-TABLE FONT)
	SUM (IF FONT-CHAR-WIDTH-TABLE (AREF FONT-CHAR-WIDTH-TABLE (LDB %%CH-CHAR CHAR))
		(FONT-CHAR-WIDTH FONT)) INTO X-TOTAL
	SUM (FONT-CHAR-HEIGHT FONT) INTO Y-TOTAL
	FINALLY ;(format t "x-size=~D, y-size=~D" x-total y-total)
	        (RETURN X-TOTAL Y-TOTAL)))

(DEFUN NEXT-FREE-PIXEL (CURRENT-X CURRENT-Y TEST DELTA-X DELTA-Y X-LONG-P X-LIM Y-LIM
			UP-P RIGHT-P STRETCH-P)
  (LOOP UNTIL (AND-OR-OR-BASED-ON-STRETCH-P STRETCH-P
					    (COND (RIGHT-P (>= CURRENT-X X-LIM))
						  (T (<= CURRENT-X X-LIM)))
					    (COND (UP-P (<= CURRENT-Y Y-LIM))
						  (T (>= CURRENT-Y Y-LIM))))
	WITH LAST-X = CURRENT-X AND LAST-Y = CURRENT-Y
	AND LAST-TEST = TEST
	DO (SETQ LAST-X CURRENT-X LAST-Y CURRENT-Y LAST-TEST TEST)
	   ;(format t "x=~D, y=~D, lx=~D, ly=~D~%" current-x current-y last-x last-y)
	   (MULTIPLE-VALUE (CURRENT-X CURRENT-Y TEST)
	     (COND ((MINUSP DELTA-X)
		    (NEXT-PIXEL-TO-LEFT CURRENT-X CURRENT-Y TEST DELTA-X DELTA-Y X-LONG-P))
		   (T (NEXT-PIXEL-TO-RIGHT CURRENT-X CURRENT-Y TEST
					   DELTA-X DELTA-Y X-LONG-P))))
	FINALLY (RETURN LAST-X LAST-Y LAST-TEST)))

(DEFUN AND-OR-OR-BASED-ON-STRETCH-P (FLAG C1 C2)
  (COND (FLAG (AND C1 C2))
	(T (OR C1 C2))))

;;; given current point, total displacements and test (initially long/2)
;;; return new x, y and test
(DEFUN NEXT-PIXEL-TO-RIGHT (CURRENT-X CURRENT-Y TEST DELTA-X DELTA-Y X-LONG-P)
  (LET ((Y-INCR (COND ((LESSP DELTA-Y 0)
		       -1)
		      ((GREATERP DELTA-Y 0)
		       1)
		      (T 0))))
    (COND (X-LONG-P
	   (SETQ TEST (- TEST (ABS DELTA-Y)))
	   (IF (MINUSP TEST) (SETQ TEST (+ TEST DELTA-X)
				  CURRENT-Y (+ CURRENT-Y Y-INCR)))
	   (SETQ CURRENT-X (1+ CURRENT-X)))
	  (T
	   (SETQ TEST (- TEST DELTA-X))
	   (IF (MINUSP TEST) (SETQ TEST (+ TEST (ABS DELTA-Y))
				  CURRENT-X (1+ CURRENT-X)))
	   (SETQ CURRENT-Y (+ CURRENT-Y Y-INCR))))
    (VALUES CURRENT-X CURRENT-Y TEST)))

(DEFUN NEXT-PIXEL-TO-LEFT (CURRENT-X CURRENT-Y TEST DELTA-X DELTA-Y X-LONG-P)
  (LET ((Y-INCR (COND ((LESSP DELTA-Y 0)
		       -1)
		      ((GREATERP DELTA-Y 0)
		       1)
		      (T 0))))
    (COND (X-LONG-P
	   (SETQ TEST (- TEST (ABS DELTA-Y)))
	   (IF (MINUSP TEST) (SETQ TEST (+ TEST (MINUS DELTA-X))
				  CURRENT-Y (+ CURRENT-Y Y-INCR)))
	   (SETQ CURRENT-X (1- CURRENT-X)))
	  (T
	   (SETQ TEST (- TEST (MINUS DELTA-X)))
	   (IF (MINUSP TEST) (SETQ TEST (+ TEST (ABS DELTA-Y))
				  CURRENT-X (1- CURRENT-X)))
	   (SETQ CURRENT-Y (+ CURRENT-Y Y-INCR))))
    (VALUES CURRENT-X CURRENT-Y TEST)))

;;; draw character so left-side baseline is at x,y and clip if whole character won't fit
(DEFUN DRAW-CHAR-TO-RIGHT (CHAR FONT X Y HEIGHT WIDTH BASELINE WINDOW X-LIM Y-LIM ALU)
  (LET ((NEW-X (- X (LEFT-KERN CHAR FONT)))
	(NEW-Y (- Y BASELINE)))
    (AND (>= NEW-X 0)
	 (>= NEW-Y 0)
	 (LESSP (+ NEW-X WIDTH) X-LIM)
	 (LESSP (+ NEW-Y HEIGHT) Y-LIM)
	 (SEND WINDOW ':DRAW-CHAR FONT CHAR NEW-X NEW-Y ALU))))

;;; draw character so right-side baseline is at x,y and clip if whole character won't fit
(DEFUN DRAW-CHAR-TO-LEFT (CHAR FONT X Y HEIGHT WIDTH BASELINE WINDOW X-LIM Y-LIM ALU)
  (LET ((NEW-X (- X WIDTH (LEFT-KERN CHAR FONT)))
	(NEW-Y (- Y BASELINE)))
    (AND (>= NEW-X 0)
	 (>= NEW-Y 0)
	 (LESSP (+ NEW-X WIDTH) X-LIM)
	 (LESSP (+ NEW-Y HEIGHT) Y-LIM)
	 (SEND WINDOW ':DRAW-CHAR FONT CHAR NEW-X NEW-Y ALU))))

;;; draw character so center of baseline is at x, y and clip if whole character won't fit
(DEFUN DRAW-CHAR-CENTERED (CHAR FONT X Y HEIGHT WIDTH BASELINE WINDOW X-LIM Y-LIM ALU)
  (LET ((NEW-X (- X (// WIDTH 2) (LEFT-KERN CHAR FONT)))
	(NEW-Y (- Y BASELINE)))
    (AND (>= NEW-X 0)
	 (>= NEW-Y 0)
	 (LESSP (+ NEW-X (// WIDTH 2)) X-LIM)
	 (LESSP (+ NEW-Y HEIGHT) Y-LIM)
	 (SEND WINDOW ':DRAW-CHAR FONT CHAR NEW-X NEW-Y ALU))))

;;; get left kern of character
(DEFUN LEFT-KERN (CHAR FONT)
   (LET ((TABLE (TV:FONT-LEFT-KERN-TABLE FONT)))
     (IF TABLE (AREF TABLE CHAR) 0)))
