#lang racket
(require gigls/unsafe)
(provide (all-defined-out))

;;; File:
;;;   random-drawing-lab.rkt
;;; Authors:
;;;   Janet Davis
;;;   Samuel A. Rebelsky
;;;   Jerod Weinman
;;;   YOUR NAME HERE
;;; Summary:
;;;   Code for the lab entitled "Randomized Drawing"

; +--------------+------------------------------------------------------
; | Rolling Dice |
; +--------------+

;;; Procedure:
;;;   roll-a-die
;;; Parameters:
;;;   None
;;; Purpose:
;;;   To simulate the rolling of one six-sided die.
;;; Produces:
;;;   An integer between 1 and 6, inclusive.
;;; Preconditions:
;;;   [None]
;;; Postconditions:
;;;   Returns an integer between 1 and 6, inclusive.
;;;   It should be difficult (or impossible) to predict which
;;;     number is produced.
(define roll-a-die
  (lambda ()
    (+
      (random 6)    ; a value in the range [0 .. 5]
      1)))          ; now in the range [1 .. 6]

;;; Procedure:
;;;   roll-dice
;;; Parameters:
;;;   n, an integer
;;; Purpose:
;;;   Roll n six-sided dice and make a list of their values.
;;; Produces:
;;;   rolls, a list of integers
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   (length rolls) = n
;;;   Each element of rolls is a value between 1 and 6.
;;;   The values in rolls are reasonably evenly distributed.
;;;   The values in rolls are difficult to predict.
(define roll-dice
  (lambda (n)
    (if (zero? n)
        null
        (cons (roll-a-die) (roll-dice (- n 1))))))

; +----------------+----------------------------------------------------
; | Random Drawing |
; +----------------+

;;; Procedure:
;;;   random-rainbow-color
;;; Parameters:
;;;   [None]
;;; Purpose:
;;;   Picks a "random" (unpredictable) color from the basic colors
;;;   of the rainbow.
;;; Produces:
;;;   color, a string
;;; Preconditions:
;;;   [None]
;;; Postconditions:
;;;   color is a color.
;;;   It is difficult for someone to predict what color random-color
;;;     will return.
(define random-rainbow-color
  (let ([rainbow (map color->irgb
                      (list "red" "orange" "yellow" "green" "blue"
                            "indigo" "violet"))])
    (lambda ()
      (list-ref rainbow
                (random (length rainbow))))))

;;; Procedure:
;;;   random-color
;;; Parameters:
;;;   [none]
;;; Purpose:
;;;   Selects and returns a random color.
;;; Produces:
;;;   color, a color.
;;; Postconditions:
;;;   color is difficult to predict.
(define random-color
  (lambda ()
    (irgb (random 256) (random 256) (random 256))))

;;; Procedure:
;;;   randomly-select-brush!
;;; Parameters:
;;;   brushes, a list of strings
;;; Purpose:
;;;   Select one of brushes.
;;; Produces:
;;;   (nothing)
;;; Preconditions:
;;;   All the strings in brushes name valid brushes.
;;; Postconditions:
;;;   The current brush is an element of brushes.
;;;   It is equally likely that each element of brushes is now the
;;;     active brush
;;;   It is difficult to predict which brush will be selected.
(define randomly-select-brush!
  (lambda (brushes)
    (context-set-brush! (list-random-element brushes))))

;;; Procedure
;;;   select-random-brush!
;;; Parameters:
;;;   (none)
;;; Purpose:
;;;   Select one of the brushes.
;;; Produces:
;;;   (nothing)
;;; Postconditions:
;;;   It is difficult to predict the brush.
(define select-random-brush!
  (lambda ()
    (randomly-select-brush! (context-list-brushes))))

;;; Procedure: 
;;;   random-blue
;;; Parameters:
;;;   [None]
;;; Purpose:
;;;   Selects a color with blue in its name.
;;; Produces:
;;;   some-blue, an integer-encoded RGB color
;;; Preconditions:
;;;   [None]
;;; Postconditions:
;;;   some-blue is a color whose name includes "blue".
;;;   It is difficult to predict which blue it is.
(define random-blue
  (let ([colors-blues 
         (map color-name->irgb 
              (list "aliceblue" "blue" "blueviolet" "cadetblue"
                    "cornflowerblue" "darkblue" "darkslateblue"
                    "deepskyblue" "dodgerblue" "lightblue" "lightskyblue"
                    "lightsteelblue" "mediumblue" "mediumslateblue"
                    "midnightblue" "powderblue" "royalblue" "skyblue"
                    "slateblue" "steelblue"))])
    (lambda ()
      (list-random-element colors-blues))))

;;; Procedure:
;;;   draw-random-line!
;;; Parameters:
;;;   image, an image
;;; Purpose:
;;;   Draw a random line in the image, assuming that its width
;;;   and height are as specified.
;;; Produces:
;;;   (nothing)
;;; Postconditions:
;;;   A new line has been added to image, using the current color
;;;   and brush.
(define draw-random-line!
  (lambda (image)
    (image-draw-line! image 
                      (random (image-width image)) (random (image-height image))
                      (random (image-width image)) (random (image-height image)))))

;;; Procedure:
;;;   draw-sin-with-parallel-lines!
;;; Parameters:
;;;   image, an image
;;;   n, an integer
;;;   offset, an integer
;;;   start-col, an integer
;;;   mid-row, an integer
;;; Purpose:
;;;   Draw a sequence of parallel lines, with the height of the
;;;   parallel line dependent on the column.
;;; Produces:
;;;   [Nothing, called for the side effect.]
(define draw-sin-with-parallel-lines!
  (lambda (image n offset start-col mid-row)
    (let kernel! ([i 0]
                 [col start-col]
                 [row mid-row])
     (when (< i n)
       (image-draw-line! image 
                         col row
                         col (- row (* row (sin (* i pi 0.05)))))
       (kernel! (+ i 1) (+ col offset) row)))))

;;; Procedure:
;;;   draw-parallel-lines-with-decreasing-spacing!
;;; Parameters:
;;;   image, an image
;;;   col, an integer
;;;   start-row, an integer
;;;   end-row, an integer
;;;   spacing
;;;   close-enough
;;; Purpose:
;;;   Draw a sequence of vertical lines (each running from start-row
;;;   to end-row) starting at col, then spaced by spacing from col,
;;;   then by spacing/2 from that column, then spacing/4 from that
;;;   column, and so on and so forth until the distance between columns 
;;;   is less than or equal to close-enough.
;;; Produces:
;;;   [Nothing. Called for the side effects.]
(define draw-parallel-lines-with-decreasing-spacing!
  (lambda (image col start-row end-row spacing close-enough)
    (image-draw-line! image col start-row col end-row)
    (when (> spacing close-enough)
      (draw-parallel-lines-with-decreasing-spacing!
       image 
       (+ col spacing) start-row end-row
       (* spacing 0.5)
       close-enough))))

;;; Procedure:
;;;   splat!
;;; Parameters:
;;;   image, an image
;;; Purpose:
;;;   Draw a line between random points, using a random color and
;;;   a random brush.
;;; Produces:
;;;   (nothing)
;;; Postconditions:
;;;   The foreground color may have changed.
;;;   The brush may have changed.
;;;   The image now contains another line.
;;;   It should be difficult to predict what that line will look like.
(define splat!
  (lambda (image)
    (context-set-fgcolor! (random-color))
    (select-random-brush!)
    (draw-random-line! image)
    (context-update-displays!)
    image))
