
(define *texture* #f)
(define *width* #f)
(define *height* #f)
(define *center-x* #f)
(define *center-y* #f)

(define init
  (lambda (width height)
    (display "Scheme init called") (newline)
    (set! *width* width)
    (set! *height* height)
    (set! *center-x* (/ *width* 2.0))
    (set! *center-y* (/ *height* 2.0))
    (set! *texture* (make-typed-array 'u32 0 *width* *height*))))

(define gc
  (lambda ()
    (display "Scheme gc called") (newline)))

(define clamp
  (lambda (value)
    (max 0 (min 255 value))))

(define to-byte
  (lambda (value)
    (clamp (inexact->exact (floor value)))))

(define raw-plot
  (lambda (x y color)
    (array-set! *texture* color 
		(inexact->exact (floor y))
		(inexact->exact (floor x)))))

(define clipped-plot 
  (lambda (x y color)
    (if (and (>= x 0)
	     (< x *width*)
	     (>= y 0)
	     (< y *height*))
	(raw-plot x y color))))

(define clamped-plot
  (lambda (x y color)
    (raw-plot (clamp x) (clamp y) color)))

(define plot clipped-plot)

(define make-color
  (lambda (r g b a)
    (+ (to-byte r) 
       (* (to-byte g) 256) 
       (* (to-byte b) 65536) 
       (* (to-byte a) 16777216))))

(define render
  (lambda (time-in-millis)
;    (display "Scheme render called at time ") (display time-in-millis) (newline)
    (array-fill! *texture* (make-color 0 0 0 255))
    ;(render-simple-moving-dots time-in-millis)
    (render-tunnel-dots time-in-millis)
    *texture*
    ))

(define render-simple-moving-dots
  (lambda (time-in-millis)
    (plot (modulo (inexact->exact (floor (* time-in-millis 1.0))) *width*) 50 (make-color 255 0 0 255))
    (plot (modulo (inexact->exact (floor (* time-in-millis 0.5))) *width*) 60 (make-color 0 255 0 255))
    (plot (modulo (inexact->exact (floor (* time-in-millis 0.33))) *width*) 70 (make-color 0 0 255 255))))


(define pi 3.14159265358979323846264338)
(define make-3vector (lambda (x y z) (list x y z)))
(define get-x (lambda (vec) (car vec)))
(define get-y (lambda (vec) (cadr vec)))
(define get-z (lambda (vec) (caddr vec)))
(define interring-distance 0.3)

(define ring-location
  (lambda (ring-index)
    (let* ((repeat-period 150.)
	   (movement-radius 130.)
	   (phi (* 2 pi (/ ring-index repeat-period))))
      (make-3vector (* (+ (cos phi) (cos (* phi 3.7))) movement-radius)
		    (* (+ (sin phi) (sin (* phi 2.3))) movement-radius)
		    (* ring-index interring-distance)))))

(define ring-color
  (lambda (ring-index distance)
    (let* ((distance-dimming-factor (* 2 (exp (* -.3 distance))))
	   (brightness-period 8)
	   (phi (* 2 pi (/ ring-index brightness-period)))
	   (red (* .3 (+ 1.0 (cos (/ phi 10.7)))))
	   (green (* .9 (+ 1.5 (sin (/ phi 25.21)))))
	   (blue (* .3 (+ 1.0 (sin (/ phi 43.7321)))))
	   (brightness (* distance-dimming-factor (* .5 (+ 1.5 (cos phi))))))
    (make-color (* red brightness 255) 
		(* green brightness 255)
		(* blue brightness 255)
		255))))

(define object-location-projected-on-screen
  (lambda (object-loc viewer-loc camera-plane-distance camera-plane-width)
    (let ((scale-factor (/ camera-plane-distance (- (get-z object-loc) (get-z viewer-loc)))))
      (list (+ *center-x* (* scale-factor (- (get-x object-loc) (get-x viewer-loc))))
	    (+ *center-y* (* scale-factor (- (get-y object-loc) (get-y viewer-loc))))))))

(define object-radius-projected-on-screen
  (lambda (object-radius object-loc viewer-loc camera-plane-distance camera-plane-width)
    (let ((scale-factor (/ camera-plane-distance (- (get-z object-loc) (get-z viewer-loc)))))
      (* scale-factor object-radius))))

(define render-tunnel-dots 
  (lambda (time-in-millis) 
    (let* ((t (/ time-in-millis 1000.)) ; convert time to seconds
	   (dots-per-circle 50)
	   (ring-radius 230.0)
	   (max-view-distance 10.0)
	   (min-view-distance 1.0)
	   (camera-plane-distance 1.0)
	   (camera-plane-width *width*)
	   (viewer-speed 6.0)
	   (viewer-loc (ring-location (+ 5.32154 (/ (* viewer-speed t) interring-distance))))
	   (closest-visible-ring (ceiling (/ (+ (get-z viewer-loc) min-view-distance) interring-distance)))
	   (farthest-visible-ring (floor (/ (+ (get-z viewer-loc) max-view-distance) interring-distance))))
      (do ((ring-index closest-visible-ring (1+ ring-index)))
	  ((> ring-index farthest-visible-ring))
	(let* ((current-ring-loc (ring-location ring-index))
	       (current-ring-distance (- (get-z current-ring-loc) (get-z viewer-loc)))
	       (current-ring-color (ring-color ring-index current-ring-distance))
	       (current-ring-onscreen-center
		(object-location-projected-on-screen current-ring-loc viewer-loc
						     camera-plane-distance camera-plane-width))
	       (current-ring-onscreen-radius 
		(object-radius-projected-on-screen ring-radius current-ring-loc viewer-loc
						   camera-plane-distance camera-plane-width)))
	  (render-circle (car current-ring-onscreen-center)
			 (cadr current-ring-onscreen-center)
			 current-ring-onscreen-radius
			 dots-per-circle
			 current-ring-color))))))

(define render-circle
  (lambda (center-x center-y radius number-of-dots color)
    (do ((phistep (/ (* 2 pi) number-of-dots))
	 (phi 0 (+ phi phistep)))
	((> phi (* 2 pi)))
      (plot (+ center-x (* radius (cos phi)))
	    (+ center-y (* radius (sin phi)))
	    color))))
	 
	 

   