(define-module (game main)
  #:use-module (ice-9 pretty-print)
  #:use-module (game model level)
  #:use-module (game model hero)
  #:use-module (game model other)
  #:use-module (game model key)
  #:use-module (game model food)
  #:use-module (game render level)
  #:use-module (game render hero)
  #:use-module (game render other)
  #:use-module (game render key)
  #:use-module (game render food)
  #:use-module (game util assets)
  #:use-module (chickadee)
  #:use-module (chickadee math rect)
  #:use-module (chickadee math vector)
  #:use-module (chickadee graphics color)
  #:use-module (chickadee graphics sprite)
  #:use-module (chickadee graphics texture)
  #:use-module (chickadee graphics text)
  #:use-module (chickadee graphics tile-map)
  #:use-module (system repl coop-server)
  #:use-module (srfi srfi-1)
  #:export (launch-game))

(define levels #f)
(define level #f)
(define repl #f)
(define hero #f)
(define others #f)
(define keys #f)
(define foods #f)
(define calories 0.0)
(define inputs '())
(define state 'running)

(define (load)
  "Load the game initial game state."
  (assets-load)
  (render-level-load)
  (set! levels (assets-map-levels level-parse))
  (load-level)
  (set! repl (spawn-coop-repl-server)))

(define (load-level)
  "Load the next level and reset game state."
  (set! level (car levels))
  (set! levels (cdr levels))
  (render-level-set! level)
  (set! hero (hero-load level))
  (set! others (other-load-others level))
  (set! keys (keys-load level))
  (set! foods (food-load level)))

(define (update dt)
  "Update the game state."
  (poll-coop-repl-server repl)
  (set! hero (hero-update hero level inputs keys (food-total-calories-eaten foods) dt))
  (set! others (map (lambda (other) (other-update other level hero others dt)) others))
  (set! keys (keys-update keys hero))
  (set! foods (food-update foods hero))
  (if (any (lambda (other) (hero-collides? hero (other-position other))) others)
      (set! state 'lost))
  (if (level-same-cell? (hero-position hero) (level-find-goal level))
      (if (null? levels)
          (set! state 'won) 
          (load-level))))

(define (render-level)
  "Render the level and entities for the running state"
  (render-level-draw level)
  (for-each render-other others)
  (render-keys keys (level-find-goal level))
  (render-foods foods)
  (render-hero hero))

(define (draw _alpha)
  "Draw the game screen depending on state"
  (case state 
    ((won)  (render-victory))
    ((lost) (render-defeat))
    (else (render-level))))


;; Input handling
;;  keep a set of buttons which are currently pressed
;; 

(define (set-add set item)
  "Add an item to a list if it is not already present."
  (if (member item set)
      set
      (cons item set)))


(define (key-press key _modifiers _repeat?)
  "Handle key press events by adding the input to the inputs set."
  (set! inputs
    (cond ((eq? key 'left) (set-add inputs 'left))
          ((eq? key 'right) (set-add inputs 'right))
          ((eq? key 'up) (set-add inputs 'up))
          ((eq? key 'down) (set-add inputs 'down ))
          ((eq? key 'space) (set-add inputs 'dig ))
          (else inputs)))
  )


(define (key-release key _modifiers)
  "Handle key release events by removing the input from the inputs set."
  (set! inputs
    (cond ((eq? key 'left) (delete 'left inputs))
          ((eq? key 'right) (delete 'right inputs))
          ((eq? key 'up) (delete 'up inputs))
          ((eq? key 'down) (delete 'down inputs))
          ((eq? key 'space) (delete 'dig inputs))
          (else inputs))))

(define (launch-game args)
  "Launch the game with the given arguments."
  (run-game 
   #:window-title "Bloatrunner"
   #:load (lambda () (load))
   #:update (lambda (dt) (update dt))
   #:draw (lambda (alpha) (draw alpha))
   #:key-press (lambda (key modifiers repeat?) (key-press key modifiers repeat?))
   #:key-release (lambda (key modifiers) (key-release key modifiers))
   ))


