#lang racket
(require gigls/unsafe)

;;; Procedure:
;;;   log-new
;;; Parameters:
;;;   [None]
;;; Purpose:
;;;   Create a new log of values.
;;; Produces:
;;;   log, a log
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   We can apply log-add! and log-get to log.
(define log-new
  (lambda ()
    (vector null)))

;;; Procedure:
;;;   log-add!
;;; Parameters:
;;;   log, a log
;;;   val, a value
;;; Purpose:
;;;   Adds val to the log
;;; Produces:
;;;   [Nothing; called for the side effects]
;;; Preconditions:
;;;   (log-get log) is a value that we will call old-contents
;;; Postconditions:
;;;    We have added val to the end of the log.  That is,
;;;     (log-get log) = (append old-contents (list val))  
(define log-add!
  (lambda (log val)
    (vector-set! log 0 (cons val (vector-ref log 0)))))

;;; Procedure:
;;;   log-get
;;; Parameters:
;;;   log, a log
;;; Purpose:
;;;   Get the list of values added to the log
;;; Produces:
;;;   values, a list
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   values contains all of the values added by log-add!,
;;;   in the same order.
(define log-get
  (lambda (log)
    (reverse (vector-ref log 0))))

;;; Procedure:
;;;   turtle-forward-quick!
;;; Parameters:
;;;   turtle, a turtle
;;;   distance, a real number
;;; Purpose:
;;;   Move the turtle forward a bit more quickly than with
;;;   turtle-forward!  To get more speed, we use the current
;;;   color and brush, rather than the turtle's color and brush.
;;; Produces:
;;;   turtle, the same turtle
;;; Preconditions:
;;;   turtle is a valid turtle on a valid image
;;; Postconditions:
;;;   turtle has advanced by distance.  
;;;   If the turtle's brush is down, a line has been drawn from the
;;;     turtle's old position to the turtle's new position.
(define turtle-forward-quick!
  (let ((d2r (/ pi 180)))
    (lambda (turtle distance)
      (let ([col (turtle ':col)]
            [row (turtle ':row)]
            [angle (turtle ':angle)])
        (let ([newcol (+ col (* distance (cos (* d2r angle))))]
              [newrow (+ row (* distance (sin (* d2r angle))))])
          (when (turtle ':pen?)
            (image-draw-line! (turtle ':world)
                              col row
                              newcol newrow))
          (turtle ':set-col! newcol)
          (turtle ':set-row! newrow))
        turtle))))

; (sample-action! turtle forward!)
;   A sample action for turtles, using a customized forward! method.
;   Used primarily for timing
(define sample-action!
  (let ([steps (iota 800)])
    (lambda (turtle forward!)
      (for-each (lambda (i)
                  (forward! turtle 3)
                  (turtle-turn! turtle (mod i 150)))
                steps))))

; (sample-turtle)
;   Create a sample turtle for various examples.
(define sample-turtle
  (lambda ()
    (turtle-teleport! (turtle-new (image-show (image-new 200 200)))
                      50 50)))

; (sample-time forward!)
;   See how long it takes to use a particular forward! action
;   E.g., (sample-time turtle-forward!), (sample-time turtle-forward-quick!)
(define time-sample
  (lambda (forward!)
    (let ([turtle (sample-turtle)])
      (time (sample-action! turtle forward!)))))

;;; Procedure:
;;;   turtle-forward-log!
;;; Parameters:
;;;   turtle, a turtle
;;;   amt, a real number
;;;   log, a log
;;; Purpose:
;;;   Move the turtle forward the given amount without showing its
;;;   movement.  Then log the new position.
;;; Produces:
;;;   [Nothing; called for the side effect]
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   The turtle has moved the appropriate amount.
;;;   The log now contains the (col,row) of the turtle.
(define turtle-forward-log!
  (let ((d2r (/ pi 180)))
    (lambda (turtle distance log)
      (let ([col (turtle ':col)]
            [row (turtle ':row)]
            [angle (turtle ':angle)])
        (let ([newcol (+ col (* distance (cos (* d2r angle))))]
              [newrow (+ row (* distance (sin (* d2r angle))))])
          (turtle ':set-col! newcol)
          (turtle ':set-row! newrow)
          (log-add! log newcol)
          (log-add! log newrow)
          turtle)))))

;;; Procedure:
;;;   image-show-path!
;;; Parameters:
;;;   image, an image
;;;   path, a vector of the form #(x1 y1 x2 y2 ...)
;;; Purpose:
;;;   Show the path on the given image
;;; Produces:
;;;   [Nothing; called for the side effect]
(define image-show-path!
  (lambda (image path)
    (gimp-paintbrush-default (image-get-layer image)
                             (vector-length path)
                             path)))

;;; Procedure:
;;;   image-show-log!
;;; Parameters:
;;;   image, an image
;;;   log, a log whose elements were created by turtle-forward-log!
;;; Purpose:
;;    Show the path of the turtle.
;;; Produces:
;;;   [Nothing; called for the side effects.]
;;; Preconditions:
;;;   At least one point has been logged with turtle-forward-log!.
;;; Postconditions:
;;;   image contains a rendering of that log.
(define image-show-log!
  (lambda (image log)
    (image-show-path! image (list->vector (log-get log)))))

; (example turtle)
;   An example using turtle-forward-log!
(define example
  (lambda (turtle)
    (let ([log (log-new)])
      (sample-action! turtle
                      (lambda (turtle distance)
                        (turtle-forward-log! turtle distance log)))
      (image-show-log! (turtle-world turtle) log))))

; (time-example)
;   See how long it takes to run the example.
(define time-example
  (lambda ()
    (let ([turtle (sample-turtle)])
      (time (example turtle))
      (context-update-displays!)
      (turtle-world turtle))))
