(module (bd ui) ()
(import scheme
	(chicken base)
	(chicken module)
	raylib
	(imugi core)
	(imugi math)
	(imugi drawing)
	(imugi resource)
	(imugi input)
	(srfi 1)
	(srfi 4)
	(srfi 99))

;; The main font for the game.
(define base-font (font "../res/fonts/Felipa-Regular.ttf"))

;; Label is a generic text container.
;; Centered? here is a pair of booleans, basically
;; a mask saying which axis to center.
(define-record-type <label>
  (int:make-label position centered? layer font font-size color text)
  label?
  (position label-pos set-label-pos!)
  (centered? label-centered? set-label-centered!)
  (layer label-layer set-label-layer!)
  (font-size label-size set-label-size!)
  (font label-font set-label-font!)
  (color label-color set-label-color!)
  (text label-text set-label-text!))

(export process-dynamic-labels)
(define process-dynamic-labels
  (make-system
   'process-dynamic-labels
   9
   'entity
   '(<label> <dynamic-label>)
   (lambda (_ label d-label)
     (set-label-text! label ((label-func d-label))))))

(export dynamic-label)
(define-record-type <dynamic-label>
  (dynamic-label func)
  dynamic-label?
  (func label-func))

;; Title/subtitle/footer etc are basically just different styles
(export title)
(define (title position text
	       #!key
	       (centered (cons #f #f))
	       (layer 0)
	       (font base-font)
	       (size 128)
	       (color (make-color 0 0 0 1)))
  (int:make-label position centered layer font size color text))

(export subtitle)
(define (subtitle position text
		  #!key
		  (centered (cons #f #f))
		  (layer 0)
		  (font base-font)
		  (size 36)
		  (color (make-color 0 0 0 1)))
  (int:make-label position centered layer font size color text))

(export footer)
(define (footer position text
		#!key
		(centered (cons #f #f))
		(layer 0)
		(font base-font)
		(size 24)
		(color (make-color 0 0 0 1)))
  (int:make-label position centered layer font size color text))

;; This needs to be added to actually draw entities with labels
(export draw-labels)
(define draw-labels
  (make-system
   'draw-labels
   10
   'entity
   '(<label>)
   (lambda (_ label)
     (push-render-object
      'screen
      (label-layer label)
      (lambda ()
	(let* (;; measure text length in pixels in case
	       ;; we need it for horizontal centering
	       (text-length (f32vector->list
			     (measure-text-ex
			      (resource-contents (label-font label))
			      (label-text label)
			      (label-size label)
			      1)))
	       ;; center the x/y of the position
	       ;; based on label-centered?
	       (draw-pos (vec
			  (if (car (label-centered? label))
			      (- (/ (car (*window-size*))
				    2)
				 (/ (car text-length)
				    2))
			      (v-x (label-pos label)))
			  (if (cdr (label-centered? label))
			      (- (/ (cdr (*window-size*))
				    2)
				 (cadr text-length))
			      (v-y (label-pos label))))))
	  (draw-font-text-2d
	   draw-pos
	   (label-text label)
	   (label-size label)
	   (label-color label)
	   (label-font label))))))))

;; Button container
;; state is in '(ready hovered clicked)
(define-record-type <button>
  (int:make-button position size layer label state click-fn colors)
  button?
  (position button-pos set-button-pos!)
  (size button-size set-button-size!)
  (layer button-layer set-button-layer!)
  (label button-label set-button-label!)
  (state button-state set-button-state!)
  (click-fn button-click-fn set-button-click-fn!)
  (colors button-colors set-button-colors!))

;; Shortcut for making a button
(export button)
(define (button position label click-fn
		#!key
		(layer 0)
		(size (vec 100 60))
		(colors (list (make-color 0.48 0.48 0.48 1)
			      (make-color 0.63 0.63 0.63 1)
			      (make-color 0.39 0.39 0.39 1))))
  (int:make-button position size layer label 'ready click-fn colors))

;; Helper function for updating the button state
(define (check-button-state button)
  (let ((x-range (cons (v-x (button-pos button))
		       (+ (v-x (button-pos button))
			  (v-x (button-size button)))))
	(y-range (cons (v-y (button-pos button))
		       (+ (v-y (button-pos button))
			  (v-y (button-size button)))))
	(mouse-pos (cons (get-mouse-x) (get-mouse-y))))
    ;; Is the mouse in the rect drawn by x-range and y-range?
    (if (and (< (car x-range) (car mouse-pos) (cdr x-range))
	     (< (car y-range) (cdr mouse-pos) (cdr y-range)))
	(if (peek-event 'input 'click)
	    (set-button-state! button 'clicked)
	    (set-button-state! button 'hovered))
	(set-button-state! button 'ready))))

;; Helper function to render the button and its text
(define (draw-button button)
  (push-render-object
   'screen
   (button-layer button)
   (lambda ()
     (draw-rectangle-2d
      (button-pos button)
      (number->integer (v-x (button-size button)))
      (number->integer (v-y (button-size button)))
      (list-ref (button-colors button)
		(case (button-state button)
		  ((ready) 0)
		  ((hovered) 1)
		  ((clicked) 2)))
      #t
      1)))
  (let* ((txt (button-label button))
	 ;; Figure out the length of the text
	 (text-length (f32vector->list
		       (measure-text-ex
			(resource-contents (label-font txt))
			(label-text txt)
			(label-size txt)
			1)))
	 ;; Determine a centre offset internal to the button
	 (center-offset (vec (- (/ (v-x (button-size button))
				   2)
				(/ (car text-length)
				   2))
			     (- (/ (v-y (button-size button))
				   2)
				(/ (cadr text-length)
				   2)))))
    (push-render-object
     'screen
     (+ 1 (button-layer button)) ;; always draw text above the button
     (lambda ()
       (draw-font-text-2d
	(v+ (button-pos button)
	    center-offset)
	(label-text txt)
	(label-size txt)
	(label-color txt)
	(label-font txt))))))

;; If button is clicked, exec button click func
(define (do-button-callback button)
  (when (eqv? 'clicked (button-state button))
    ((button-click-fn button))))

;; Needs to be added to do buttons
(export handle-buttons)
(define handle-buttons
  (make-system
   'handle-buttons
   10
   'batch ;; batch because we check for events for every button
   '(<button>)
   (lambda (buttons)
     (for-each
      (lambda (b)	
	(let ((button (find button? b)))
	  (check-button-state button)
	  (draw-button button)
	  (do-button-callback button)))
      buttons)
     ;; At the end of the function, clear click events
     (pop-event 'input 'click))))
)
