;-*- Mode:LISP; Package:(VIF :use (LISP)); Readtable:CL; Base:10 -*-


;;;; Grok Daisy Vectorized Intermediate Files


(defmacro read-word (stream)
  `(logior (read-byte ,stream)
	   (ash (read-byte ,stream) 8.)))

(defmacro read-opcode (stream)
  `(read-word ,stream))

(defmacro read-parameter (stream)
  `(read-word ,stream))

(defmacro read-coordinate (stream)
  `(values (read-word ,stream)
	   (read-word ,stream)))


;;; Control Opcodes
(defconstant VIF-OP-BEGIN-FILE      0.)
(defconstant VIF-OP-END-FILE        1.)

;;; Object Opcodes
(defconstant VIF-OP-POLYLINE        2.)
(defconstant VIF-OP-POLYGON         3.)
(defconstant VIF-OP-CIRCLE          4.)
(defconstant VIF-OP-RECTANGLE       5.)
(defconstant VIF-OP-ARC             6.)
(defconstant VIF-OP-TEXT            7.)

;;; Attribute Opcodes
(defconstant VIF-OP-SET-COLOR       9.)
(defconstant VIF-OP-LINE-WIDTH     10.)
(defconstant VIF-OP-LINE-STYLE     11.)
(defconstant VIF-OP-TEXT-WIDTH     12.)
(defconstant VIF-OP-TEXT-HEIGHT    13.)
(defconstant VIF-OP-TEXT-DIRECTION 14.)


(defun no-handler (s)
  (cerror "foo" "no handler"))

(defmacro def-device (name)
  `(defconstant ,name (make-array 15. :initial-element #'no-handler)))

(defmacro def-vif-op-handler ((device op) lambda-list &body body)
  `(progn
     (defun ,op ,lambda-list
       ,@body)
     (setf (aref ,device ,op) #',op)))

(defun do-vif-file (file device)
  (with-open-file (s file :raw t)
    (read-opcode s)
    (dotimes (i 30) (read-byte s))
    (do ((op (read-opcode s) (read-opcode s)))
	((= op VIF-OP-END-FILE))
 ;     (print op)
      (funcall (aref device op) s))))

;;; Draw on Lisp Machine Windows

(def-device LM-WINDOW)

(defun display-vif-file (file)
  (zl:send tv:selected-window :set-font-map (list fonts:hl6))
  (zl:send tv:selected-window :clear-screen)
  (do-vif-file file LM-WINDOW)
  (zl:send tv:selected-window :set-cursorpos 0 680))


(def-vif-op-handler (LM-WINDOW VIF-OP-POLYLINE) (s)
  (let ((n-coordinates (read-parameter s))
	from-x from-y to-x to-y)
    (multiple-value-setq (from-x from-y) (read-coordinate s))
    (dotimes (i (1- n-coordinates))
      (multiple-value-setq (to-x to-y) (read-coordinate s))
      (draw-line from-x from-y to-x to-y)
      (setq from-x to-x
	    from-y to-y))))

(def-vif-op-handler (LM-WINDOW VIF-OP-ARC) (s)
  (multiple-value-bind (start-x start-y) (read-coordinate s)
    (multiple-value-bind (middle-x middle-y) (read-coordinate s)
      (multiple-value-bind (last-x last-y) (read-coordinate s)
	(multiple-value-bind (center-x center-y radius)
	    (circle-center-and-radius-from-3-points start-x start-y middle-x middle-y last-x last-y)
	  (let ((theta-1 (atan (- start-y center-y) (- start-x center-x)))
		(theta-2 (atan (- last-y center-y) (- last-x center-x))))
	    (when (> theta-1 theta-2)
	      (let ((theta theta-1))
		(setq theta-1 theta-2)
		(setq theta-2 theta)))
	    (draw-arc center-x center-y radius theta-1 theta-2)))))))
	
(defun draw-arc (center-x center-y radius theta-1 theta-2)
  (zl:send tv:selected-window :draw-circular-arc
	   (round (* center-x *x-scale*))
	   (+ *y-offset* (round (* center-y *y-scale*)))
	   (round (* radius   *x-scale*))
	   theta-1 theta-2))

(def-vif-op-handler (LM-WINDOW VIF-OP-CIRCLE) (s)
  (let ((radius (read-parameter s)))
    (multiple-value-bind (center-x center-y) (read-coordinate s)
      (draw-circle center-x center-y radius))))

(defun draw-circle (center-x center-y radius)
  (zl:send tv:selected-window :draw-circle
	   (round (* center-x *x-scale*))
	   (+ *y-offset* (round (* center-y *y-scale*)))
	   radius))


(def-vif-op-handler (LM-WINDOW VIF-OP-LINE-WIDTH) (s)
  (let ((width (read-parameter s)))))

(def-vif-op-handler (LM-WINDOW VIF-OP-RECTANGLE) (s)
  (cerror "foo" "implement rectangle"))

(def-vif-op-handler (LM-WINDOW VIF-OP-TEXT-DIRECTION) (s)
  (let ((direction (read-parameter s)))))

(defvar *x-scale* .62s0)
(defvar *y-offset* 650.)
(defvar *y-scale* -.62s0)

(def-vif-op-handler (LM-WINDOW VIF-OP-TEXT) (s)
  (multiple-value-bind (x y) (read-coordinate s)
    (zl:send tv:selected-window :set-cursorpos
	     (truncate (* x *x-scale*))
	     (+ -6 *y-offset* (truncate (* y *y-scale*)))))
  (let ((nchars (read-parameter s)))
    (dotimes (i (truncate nchars 2))
      (let ((ch2 (read-byte s))
	    (ch1 (read-byte s)))
	(zl:send tv:selected-window :tyo ch1)
	(zl:send tv:selected-window :tyo ch2)))
    (when (oddp nchars)
      (read-byte s)
      (zl:send tv:selected-window :tyo (read-byte s)))))

(defun draw-line (from-x from-y to-x to-y)
  (zl:send tv:selected-window :draw-line
	   (round (* from-x *x-scale*))
	   (+ *y-offset* (round (* from-y *y-scale*)))
	   (round (* to-x *x-scale*))
	   (+ *y-offset* (round (* to-y *y-scale*)))))



;;;; Create an impress file


(def-device IMPRESS)

(defun convert-vif-to-impress (vif-file &optional
			       impress-file)
  (let ((vif-pn (fs:merge-pathname-defaults vif-file nil :default-type "vif")))
    (unless impress-file
      (setq impress-file (zl:send vif-pn :new-pathname :type "imp")))
    (with-open-file (imagen-stream impress-file :direction :output :raw t)
      (setq *imagen-stream* imagen-stream)
      (format *imagen-stream* "@Document(JobHeader on,Language Impress,Owner ~a,Name \"~A\")"
	      zl:user-id impress-file)
      (imagen-setup-text)
      (imagen-nop)
      (do-vif-file vif-pn IMPRESS)
      (imagen-endpage)
      (imagen-eof))))

(def-vif-op-handler (IMPRESS VIF-OP-POLYLINE) (s)
  (let ((n-coordinates (read-parameter s))
	x y)
    (imagen-start-path n-coordinates)
    (dotimes (i n-coordinates)
      (multiple-value-setq (x y) (read-coordinate s))
      (imagen-output-vertex (+ 225 (ash y 1)) (+ 40 (ash x 1))))
    (imagen-draw-path 15.)))

(defconstant *imagen-angle* (/ 8196.0s0 3.14159s0))

(def-vif-op-handler (IMPRESS VIF-OP-ARC) (s)
  (multiple-value-bind (start-x start-y) (read-coordinate s)
    (multiple-value-bind (middle-x middle-y) (read-coordinate s)
      (multiple-value-bind (last-x last-y) (read-coordinate s)
	(multiple-value-bind (center-x center-y radius)
	    (circle-center-and-radius-from-3-points start-x start-y middle-x middle-y last-x last-y)
	  (let ((theta-2 (atan (- start-y center-y) (- start-x center-x)))
		(theta-1 (atan (- last-y center-y) (- last-x center-x))))
	    (imagen-set-abs-h (+ 225 (ash (round center-y) 1)))
	    (imagen-set-abs-v (+  40 (ash (round center-x) 1)))
	    (imagen-circ-arc (ash (round radius) 1)
			     (- 4096. (round (* theta-1 *imagen-angle*)))
			     (- 4096. (round (* theta-2 *imagen-angle*))))
	    (imagen-draw-path 15.)
	    ))))))

(def-vif-op-handler (IMPRESS VIF-OP-CIRCLE) (s)
  (let ((radius (read-parameter s)))
    (multiple-value-bind (center-x center-y) (read-coordinate s)
      (imagen-set-abs-h (+ 225 (ash (round center-y) 1)))
      (imagen-set-abs-v (+  40 (ash (round center-x) 1)))
      (imagen-circ-arc (ash (round radius) 1) 0 16383.)
      (imagen-draw-path 15.)
      )))

(def-vif-op-handler (IMPRESS VIF-OP-LINE-WIDTH) (s)
  (imagen-set-pen (read-parameter s)))

(def-vif-op-handler (IMPRESS VIF-OP-RECTANGLE) (s)
  (multiple-value-bind (x1 y1) (read-coordinate s)
    (multiple-value-bind (x2 y2) (read-coordinate s)
      (imagen-start-path 5)
      (imagen-output-vertex (+ 225 (ash y1 1)) (+ 40 (ash x1 1)))
      (imagen-output-vertex (+ 225 (ash y1 1)) (+ 40 (ash x2 1)))
      (imagen-output-vertex (+ 225 (ash y2 1)) (+ 40 (ash x2 1)))
      (imagen-output-vertex (+ 225 (ash y2 1)) (+ 40 (ash x1 1)))
      (imagen-output-vertex (+ 225 (ash y1 1)) (+ 40 (ash x1 1)))
      (imagen-draw-path 15.))))


(def-vif-op-handler (IMPRESS VIF-OP-TEXT-DIRECTION) (s)
  (multiple-value-bind (main sec)
      (case (read-parameter s)
	;right
	(1 (values down right)) 
	;up
	(3 (values right up)))
    (imagen-set-adv-dirs main sec)
    (imagen-set-text-rotation main)))


(def-vif-op-handler (IMPRESS VIF-OP-TEXT) (s)
  (multiple-value-bind (x y) (read-coordinate s)
    (imagen-set-position (+ 225 (ash y 1)) (+ 40 (ash x 1))))
  (let ((nchars (read-parameter s)))
    (dotimes (i (truncate nchars 2))
      (let ((ch2 (read-byte s))
	    (ch1 (read-byte s)))
	(imagen-write-char ch1)
	(imagen-write-char ch2)))
    (when (oddp nchars)
      (read-byte s)
      (imagen-write-char (read-byte s)))))






(defun vif-to-imp-dir (directory)
  (let ((dirlist (fs:directory-list directory :noerror)))
    (if (typep dirlist 'error)
	(progn
	  (terpri)
	  (zl:send dirlist :print-error-message si:current-stack-group nil t))
      (dolist (file (cdr dirlist))
	(cond ((string-equal "VIF" (pathname-type (car file)))
	       (convert-vif-to-impress (car file)))
	      ((get file :directory)
	       (vif-to-imp-dir (zl:send (zl:send (car file) :pathname-as-directory)
					:new-pathname
					:name :wild
					:type :wild
					:version :newest))))))))

