#lang racket
(require gigls/unsafe)


;;; Procedure:
;;;   image-series
;;; Parameters:
;;;   n, a positive integer
;;;   width, a positive integer
;;;   height, a positive integer
;;; Produces: 
;;;   pretty-picture, an image
;;; Purpose:
;;;   Create an image that reflects a series of images based on the given n value
;;; Pre-conditions:
;;;   n should be between 0 and 999, inclusive.
;;;   For optimal viewing pleasure, the width/height ratio should be 16/9
;;; Post-conditions:
;;;   (image-height pretty-picture) is height
;;;   (image-width pretty-picture) is width
;;;   (pretty? pretty-picture) is #t


(define image-series
  (lambda (n width height)
    ;set background to black
    (gimp-context-set-background 0)
    (letrec (;creates a canvas
             [picture (image-new (round (* (/ 16 9) height)) height)]
             ;creates a sphere-ish illusion
             [shaded-circle (lambda (n diameter x-loc y-loc rgb image)
                              (let (;define how much to shift up
                                    [shift-factor (/ diameter n)]
                                    ;define how much to change colors
                                    [rgb-red-change (ceiling (/ (- 255 (rgb-red (rgb-complement rgb))) n))]
                                    [rgb-green-change (ceiling (/ (- 255 (rgb-green (rgb-complement rgb))) n))]
                                    [rgb-blue-change (ceiling (/ (- 255 (rgb-blue (rgb-complement rgb))) n))]
                                    ;makes the cute brightness thing always point to the upper right hand corner, no matter what the height/width :)
                                    [deg (atan (/ (- (image-width image) (+ x-loc (/ diameter 2))) (+ y-loc (/ diameter 2))))]
                                    )
                                (letrec ([recurse (lambda (n rgb iteration)
                                                    ;makes the next color
                                                    (let* ([new-red (+ (rgb-red rgb) rgb-red-change)]
                                                           [new-green (+ (rgb-green rgb) rgb-green-change)]
                                                           [new-blue (+ (rgb-blue rgb) rgb-blue-change)])
                                                      ;shift selection up to make brighter- creates illusion of sphere
                                                      (image-select-ellipse! image INTERSECT x-loc (- y-loc (floor (* shift-factor iteration)))  diameter diameter)
                                                      (gimp-context-set-foreground rgb)
                                                      (image-fill-selection! image)
                                                      ;recursion!
                                                      (cond [(not (=  n iteration))
                                                             (recurse n (rgb-new new-red new-green new-blue) (+ iteration 1))
                                                             ])))])
                                  
                                  ;selects original circle and fills black
                                  (image-select-ellipse! image ADD (- x-loc 1) (- y-loc 1) diameter diameter)
                                  (gimp-context-set-foreground 0)
                                  (image-fill-selection! image)
                                  ;sets in the recursion. Clever name, right?
                                  (recurse n (rgb-new (+ (rgb-red 0) rgb-red-change) (+ (rgb-green 0) rgb-green-change) (+ (rgb-blue 0) rgb-blue-change))1)
                                  (image-select-ellipse! image ADD (- x-loc 2) (- y-loc 2) (+ 2 diameter) (+ 2 diameter))
                                  ;rotate the "spheres"
                                  (gimp-item-transform-rotate (image-get-layer image) deg 1 (/ diameter 2) (/ diameter 2))
                                  (gimp-image-flatten image)
                                  (image-select-nothing! image))))]
             ;creates five turtles on picture, aptly named
             [abby (turtle-new picture)]
             [toby (turtle-new picture)]
             [alex (turtle-new picture)]
             [sam (turtle-new picture)]
             [friend (turtle-new picture)]
             ;component of turtle-polygon! - draws a side-length and rotates the turtle appropriately 
             [action01
              (lambda (turtle side-length sides)
                (turtle-forward! turtle side-length)
                (turtle-turn! turtle (/ 360 sides)))]
             ;draws a polygon of given side-length and number of sides
             [turtle-polygon!
              (lambda (turtle side-length sides)
                (repeat sides action01 turtle side-length sides))]
             ;draws a centered polygon of a given radius and number of sides
             [turtle-centered-polygon!
              (lambda (turtle radius sides)
                (let ([interior-angle (/ (* 180 (- sides 2)) sides)])
                  (turtle-up! turtle)
                  (turtle-forward! turtle radius)
                  (turtle-down! turtle)
                  (turtle-turn! turtle (- 180 (/ interior-angle 2)))
                  (turtle-polygon! turtle (* 2 radius (sin (/ pi sides))) sides)
                  (turtle-turn! turtle (/ interior-angle 2))
                  (turtle-up! turtle)
                  (turtle-forward! turtle radius)
                  (turtle-turn! turtle 180)
                  (turtle-down! turtle)))]
             ; Set-Darker is a procedure that given a color, adds 4 to each component. Less extreme rgb-changer.
             [set-darker
              (lambda (color)
                (rgb-new (+ 4 (rgb-red color)) (+ 4 (rgb-green color)) (+ 4 (rgb-blue color))))]
             ; Count-darker is a procedure that given a color, i, and p uses set-darker on the given color a set amount of times 
             ; depending on i and p.
             [count-darker
              (lambda (color i p)
                (let ([number (modulo i p)])
                  (if (= number 0)
                      color
                      (count-darker (set-darker color) (- number 1) p))))]
             
             ;draws the five polygons at the lower right-hand corner of the image.
             [pretty-polygons
              (lambda (i)
                ;moves the turtles to a location determined by the image width and height
                (turtle-teleport! friend (- (image-width picture) (* (image-width picture) (/ 75 1600))) (- (image-height picture) (* (image-width picture) (/ 50 900))))
                (turtle-teleport! abby  (- (image-width picture) (* (image-width picture) (/ 150 1600))) (- (image-height picture) (* (image-width picture) (/ 50 900))))
                (turtle-teleport! toby  (- (image-width picture) (* (image-width picture) (/ 100 1600))) (- (image-height picture) (* (image-width picture) (/ 100 900))))
                (turtle-teleport! alex  (- (image-width picture) (* (image-width picture) (/ 150 1600))) (- (image-height picture) (* (image-width picture) (/ 150 900))))
                (turtle-teleport! sam  (- (image-width picture) (* (image-width picture) (/ 50 1600))) (- (image-height picture) (* (image-width picture) (/ 100 900))))
                ;each n value sets the polygons to a different shade of the given colors. The colors cycle from light to dark on a given prime modulus. 
                ;each turtle is assigned a different prime number.
                (turtle-set-color! abby (count-darker (color->rgb "lightblue") i 5))
                ;for a given n value, each turtle draws a polygon with the number of sides determined by the same prime modulus of n.  
                ;the number of sides cycle from low to approximately the threshold when the polygon looks circular.
                (turtle-centered-polygon! abby (* (image-width picture) (/ 30 1600))  ( + (modulo i 5) 3))
                (turtle-set-color! toby (count-darker (color->rgb "slateblue") i 11))
                (turtle-centered-polygon! toby (* (image-width picture) (/ 35 1600)) ( + (modulo i 11) 4))
                (turtle-set-color! alex (count-darker (color->rgb "silver") i 17))
                (turtle-centered-polygon! alex (* (image-width picture) (/ 15 1600)) ( + (modulo i 17) 5))
                (turtle-set-color! sam (count-darker (color->rgb "cornflowerblue") i 3))
                (turtle-centered-polygon! sam (* (image-width picture) (/ 50 1600)) ( + (modulo i 7) 3))
                (turtle-set-color! friend (count-darker (color->rgb "green") i 13))
                (turtle-centered-polygon! friend (* (image-width picture) (/ 28 1600)) ( + (modulo i 13) 4)))])
      ;sphere draw
      (cond
        ;if n outside of range, display nice error message.
        [(or  (< n    0)   (> n 1000) (not (integer? n))) (error   "image-series expects first parameter to be and integer between 0 and 999 inclusive.")]
        ;draw first sphere if n<200
        [(and (>= n   0)   (< n 200))  ((lambda()
                                          (shaded-circle                 n  (* height (/ 28   75)) (* width (/ 7   100)) (* height (/ 135 225))  (rgb-darker (color-name->rgb "maroon")) picture)))]
        ;draw two spheres.
        [(and (>= n 200)   (< n 400))  ((lambda()
                                          (shaded-circle (+ 1 (mod n 200))  (* height (/ 56  225)) (* width (/ 21  100)) (* height (/ 28   75)) (color-name->rgb "red") picture)
                                          (shaded-circle               200  (* height (/ 28   75)) (* width (/ 7   100)) (* height (/ 135 225))  (rgb-darker (color-name->rgb "maroon"))picture)))]
        ;etc
        [(and (>= n 400)   (< n 600))  ((lambda()
                                          (shaded-circle (+ 1 (mod n 400))  (* height (/ 28  255)) (* width (/ 25  160)) (* height (/ 19   90))  (color-name->rgb "orange"    ) picture)
                                          (shaded-circle               200  (* height (/ 56  225)) (* width (/ 21  100)) (* height (/ 28   75))  (color-name->rgb "red") picture)
                                          (shaded-circle               200  (* height (/ 28   75)) (* width (/ 7   100)) (* height (/ 135 225))  (rgb-darker (color-name->rgb "maroon")) picture)))]
        ;etc
        [(and (>= n 600)   (< n 800))  ((lambda()
                                          (shaded-circle (+ 1 (mod n 600))  (* height (/ 14  255)) (* width (/ 21  200)) (* height (/ 25  225))  (color-name->rgb "gold"  ) picture)
                                          (shaded-circle               200  (* height (/ 28  255)) (* width (/ 25  160)) (* height (/ 19   90))  (color-name->rgb "orange"    ) picture)
                                          (shaded-circle               200  (* height (/ 56  225)) (* width (/ 300  1600)) (* height (/ 315 900))  (color-name->rgb "red") picture)
                                          (shaded-circle               200  (* height (/ 28   75)) (* width (/ 7   100)) (* height (/ 135 225))  (rgb-darker (color-name->rgb "maroon")) picture)))]
        ;etc
        [(and (>= n 800)   (< n 1000)) ((lambda()
                                          (shaded-circle (+ 1 (mod n 800)) (* height (/ 17  375)) (* width (/ 7   200)) (* height (/ 7   200))  (color-name->rgb "yellow"     ) picture)
                                          (shaded-circle               200 (* height (/ 14  255)) (* width (/ 21  200)) (* height (/ 25  225))  (color-name->rgb "gold"   ) picture)
                                          (shaded-circle               200 (* height (/ 28  255)) (* width (/ 25  160)) (* height (/ 19   90))  (color-name->rgb "orange"     ) picture)
                                          (shaded-circle               200 (* height (/ 56  225)) (* width (/ 285  1600)) (* height (/ 315 900)) (color-name->rgb "red") picture)
                                          (shaded-circle               200 (* height (/ 28   75)) (* width (/ 7   100)) (* height (/ 135 225))  (rgb-darker (color-name->rgb "maroon")) picture)))])
      
      ; This line does a 3x3 blur on the image. 
      (plug-in-blur 0 picture (car (gimp-image-get-active-layer picture)))
      ; Polygon draw
      (pretty-polygons n)
      (image-show picture)
      ; Sets brush to custom-made brush. 
      (context-set-brush! "TMA-Brush" width)
      ; Sets foreground color to white.
      (context-set-fgcolor! (color->rgb "white"))
      ; Blots the image with the brush with the location thht we want it at.
      (image-draw-line! picture (round (* (/ 16 9) height)) 0 (round (* (/ 16 9) height)) 0)
      ; post-production scaling
      (gimp-image-scale picture width height)
      
      (context-update-displays!))))
