;;; -*- Mode: Lisp; Package: Puser; Base: 10. ; Options: ((World Turtle)); -*-
;;; (C) Copyright 1983,1984,1985, Uppsala University

(declare (special *xcor *ycor *heading *turtle *wrap *xorstate *drawstate))

(define-predicate help
  (:options (:argument-list ()))
  ((help)
   (lisp-value ?help prolog:*turtle-help* :dont-invoke)
   (format t "~%~a" ?help)))

;;; Examples. These generate difference lists of Turtle commands.

(define-predicate poly
  (:options (:argument-list (commands commands-end edges size &optional ignore)))
  ((poly ?com ?com ?edges ?)
   ( ?edges 1))
  ((poly ?com0 ?com ?edges ?size)
   (> ?edges 1)
   (quotient ?angle 360. ?edges)
   (poly ?com0 ?com ?angle ?edges 0 ?size))
  ((poly ?com ?com ? ?edges ?edges ?))
  ((poly ((:forward ?size) . ?com0) ?com ?angle ?edges ?fewer ?size)
   (= ?com0 ((:right ?angle) . ?com1))
   (< ?fewer ?edges)
   (sum ?more ?fewer 1)
   (poly ?com1 ?com ?angle ?edges ?more ?size)))
  
(define-predicate circle
  (:options (:argument-list (commands commands-end +size)))
  ((circle ?com0 ?com ?size) (= ?com0 ((:circle ?size) . ?com))))

;;; New cleaned-up Turtle interface to logic.
prolog:
(progn 'compile
(define-predicate puser:turtle-stream
  :(options (world graphics))
  ((puser:turtle-stream ?pos0 ())
   (puser:turtle-stream ?pos0))
  ((puser:turtle-stream ?pos0)
   (lisp-value ?instance (make-instance-in-area *prolog-work-area* 'puser:turtle-stream)
	       :dont-invoke)
   (turtle-stream-server ?instance ((:wipe) . ?pos0))))

(define-predicate puser:turtle-stream-concurrent
  :(options (world graphics))
  ((puser:turtle-stream-concurrent ?pos0 ((:unlock)))
   (lisp-value ?instance (make-instance-in-area *prolog-work-area* 'puser:turtle-stream)
	       :dont-invoke)
   (turtle-stream-server ?instance ((:lock) . ?pos0))))

(define-predicate puser:turtle-stream
  ((puser:turtle-stream . ?)))

(define-predicate turtle-stream-server
  :(options (world graphics))
  ((turtle-stream-server ? ()) (cut))
  ((turtle-stream-server ?instance (?msg . ?pos0))
   (lisp-predicate (apply '?instance '?msg) :invoke)
   (constrain ?pos0 (turtle-stream-server ?instance ?pos0))))
)

(defflavor turtle-stream () ())

(defvar *turtle-lock* nil)

(defun turtle-lock ()
  (or (%store-conditional (locf *turtle-lock*) nil current-process)
      (condition-case ()			;If time out then go ahead and clobber...
	  (process-lock (locf *turtle-lock*) nil "Turtle" #-symbolics 70.)
	(error (setq *turtle-lock* nil)
	       (turtle-lock)))))

(defun turtle-unlock ()
  (%store-conditional (locf *turtle-lock*) current-process nil))

(defmethod (turtle-stream :lock) ()
  (prolog:remind-call 'turtle-unlock)
  (turtle-lock)
  (placeturtle '(0 0 0)))

(defmethod (turtle-stream :unlock) ()
  (prolog:remind-call 'placeturtle (prolog:prolog-list *xcor *ycor *heading))
  (prolog:remind-call 'turtle-lock)
  (turtle-unlock))

(defmethod (turtle-stream :startdisplay) ()
  (startdisplay))

(defmethod (turtle-stream :wipe) ()
  (wipe))

(defmethod (turtle-stream :clearscreen) ()
  (prolog:remind-call 'clearscreen)
  (clearscreen))

(defmethod (turtle-stream :hideturtle) ()
  (prolog:remind-call 'showturtle)
  (hideturtle))

(defmethod (turtle-stream :showturtle) ()
  (prolog:remind-call 'hideturtle)
  (showturtle))

(defmethod (turtle-stream :forward) (steps)
  (prolog:remind-call 'back steps)
  (forward steps))

(defmethod (turtle-stream :back) (steps)
  (prolog:remind-call 'forward steps)
  (back steps))

(defmethod (turtle-stream :delx) (x)
  (prolog:remind-call 'delx (- x))
  (delx x))

(defmethod (turtle-stream :dely) (y)
  (prolog:remind-call 'dely (- y))
  (dely y))

(defmethod (turtle-stream :delxy) (x y)
  (prolog:remind-call 'delxy (- x) (- y))
  (delxy x y))

(defmethod (turtle-stream :home) ()
  (prolog:remind-call 'setturtle (prolog:prolog-list *xcor *ycor *heading))
  (home))

(defmethod (turtle-stream :wrap) ()
  (cond ((not *wrap) (prolog:remind-call 'nowrap)))
  (wrap))

(defmethod (turtle-stream :nowrap) ()
  (cond (*wrap (prolog:remind-call 'wrap)))
  (nowrap))

(defmethod (turtle-stream :right) (steps)
  (prolog:remind-call 'left steps)
  (right steps))

(defmethod (turtle-stream :left) (steps)
  (prolog:remind-call 'right steps)
  (left steps))

(defmethod (turtle-stream :sethead) (steps)
  (prolog:remind-call 'sethead (heading))
  (sethead steps))

(defmethod (turtle-stream :pendown) ()
  (cond ((not *xorstate) (prolog:remind-call 'xorup)))
  (xordown))

(defmethod (turtle-stream :penup) ()
  (cond (*xorstate (prolog:remind-call 'xordown)))
  (xorup))


;;Don't cause rounding errors...
(defmethod (turtle-stream :here) (xcor ycor heading)
  (and (prolog:unify xcor *xcor)
       (prolog:unify ycor *ycor)
       (prolog:unify heading *heading)))

(defmethod (turtle-stream :setturtle) (&rest state)
  (prolog:remind-call 'setturtle (prolog:prolog-list *xcor *ycor *heading))
  (setturtle state))

(defmethod (turtle-stream :placeturtle) (&rest state)
  (prolog:remind-call 'placeturtle (prolog:prolog-list *xcor *ycor *heading))
  (placeturtle state))

(defun placeturtle (state)
  (let (*drawstate) (setturtle state)))

(defmethod (turtle-stream :hatch) (name)
  (cond ((prolog:unbound-variable-p name)
	 (prolog:unify name (prolog:generate-interned-symbol name))
	 (setq name (prolog:%dereference name))))
  (prolog:remind-call 'useturtle *turtle)
  (hatch name))

(defmethod (turtle-stream :useturtle) (name)
  (prolog:remind-call 'useturtle *turtle)
  (useturtle name))

(defmethod (turtle-stream :mark) (text)
  (prolog:remind-call 'mark text)
  (mark text)
  t)

;;The grammar kit needs to display a centered label and skip vertically after.
(defmethod (turtle-stream :markv) (text)
  (prolog:remind-call 'markv text *ycor)
  (markv text nil)
  t)

(defun markv (text up)
  (let-if (symbolp text) ((text (get-pname text)))
    (let (*drawstate) ;inhibit drawing
      (cond ((not up)
	     (send tvrtle-window ':set-cursorpos
		   (- (tv-x *xcor) (// (send tvrtle-window ':string-length text) 2))
		   (+ 5 (tv-y *ycor)))
	     (send tvrtle-window ':string-out text)
	     (dely -30.))
	    (t (sety up)
	       (send tvrtle-window ':set-cursorpos
		     (- (tv-x *xcor) (// (send tvrtle-window ':string-length text) 2))
		     (+ 5 (tv-y *ycor)))
	       (send tvrtle-window ':string-out text))))))

(defmethod (turtle-stream :point) ()
  (prolog:remind-call 'point)
  (point))

(defmethod (turtle-stream :arc) (rad deg)
  (prolog:remind-call 'arc rad deg)
  (arc rad deg))

(defmethod (turtle-stream :circle) (rad)
  (prolog:remind-call 'circle rad)
  (circle rad))

(ADD-WORLD :GRAPHICS)
