(ffi:clines "
#include <SDL2/SDL.h>

SDL_Renderer *renderer;
SDL_Window *window;
SDL_Event e;

const Uint8 *state;

int quitted;

int up_arrow, down_arrow, left_arrow, right_arrow;
")

(Defpackage "jam-no-theme" (:use cl) (:nicknames :ja))

(in-package :ja)
(defvar *SCREEN-WIDTH* 640)
(defvar *SCREEN-HEIGHT* 480)
(defvar *scale* '5)
(defvar *unit-width* (/ *screen-width* *scale*))
(defvar *unit-height* (/ *screen-height* *scale*))

;;;Underlying sdl2 mechanism
(defmacro game ((screen-width screen-height)
               (&rest unused)
               update-closures)
`(let ((screen-width ,screen-width)
       (screen-height ,screen-height))
  ,(append '(declare) '((:int screen-width screen-height)))
  (multiple-value-bind
   (up down left right aa bb) (check-arrow-scancodes)
   (multiple-value-setq (*up* *down* *left* *right* *a* *B*)
                       (values up down left right aa bb)))
 (unwind-protect
  (ffi:c-progn (screen-width screen-height)
   "

if (SDL_Init(SDL_INIT_VIDEO) < 0) {
       SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
           \"Failed to init %s\",
           SDL_GetError());
       " (error "failed to SDL_Init(video)") "
}

if (SDL_CreateWindowAndRenderer(#0,#1,SDL_WINDOW_RESIZABLE,
       &window, &renderer)) {
       SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
           \"Failed to create w & r%s\",
           SDL_GetError());
       " (error "failed to create window and renderer") "
}

quitted = 0;
for (;;) {
       while(SDL_PollEvent(&e))
           if (e.type == SDL_QUIT) quitted = 1;
           else if (e.type == SDL_KEYDOWN)
               switch (e.key.keysym.sym) {
               case SDLK_q:
                   quitted = 1;
                   break;
               }
       if (quitted) break;


       SDL_SetRenderDrawColor(renderer, 0, 10, 20, 255);
       SDL_RenderClear(renderer);
       "

       (mapc 'funcall ,update-closures)

       "
       SDL_RenderPresent(renderer);
       SDL_Delay(125);
}

SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();")
  (ffi:c-inline () () nil "SDL_Quit();"))))


;;; SDL_SetRenderDrawColor in lisp.
(defun set-color (r g b &optional (a 255))
(ffi:c-inline (r g b a) (:int :int :int :int) nil
 "SDL_SetRenderDrawColor(renderer, #0, #1, #2, #3)"
 :one-liner t))

;;; SDL_RenderFillRect as a lisp function.
(defun fill-rectangle (x y w h)
(ffi:c-inline ((* *scale* x)
               (* *scale* y)
               (* *scale* w)
               (* *scale* h))
 (:int :int :int :int) nil
 "
  SDL_RenderFillRect(renderer,
      &(struct SDL_Rect){.x = #0, .y = #1,
                         .w = #2, .h = #3})"
 :one-liner t))

;; Add plants.
(defvar *plants* (list))
(defun spawn-plant-in (x y w h)
(push (list 'leaves (+ x (random w)) (+ y (random h)))
 *plants*))

(defparameter *leaves/flowers* 2/31)
(defparameter *flowers/berries* 2/21)
(defparameter *berry-satiation* 6)
(defvar *max-seeds* 4)
(defvar *seedbox* 5)

;; leaves->flowers flowers->berries
(defun advance-some-plants (&rest from-to-frac/tions)
(setf *plants*
 (loop for plant in *plants*
  nconc
  (or
   (loop for (from to frac/tion) in from-to-frac/tions
    when (< (random (denominator frac/tion)) (numerator frac/tion))
    do
    (when (eq (car plant) from) (return `((,to ,@(cdr plant))))))
   `(,plant)))))

;; Painting the lilly
(defun paint-plants ()
(loop for plant in *plants*
 for xy-position = (cdr plant)
 for color = (case (car plant)
              (leaves '(0 255 0))
              (flower '(255 0 255))
              (berry '(255 0 0)))
 when color
 do (apply 'set-color color)
    (apply 'fill-rectangle
     (append (mapcar '- xy-position
              (funcall (ensure-player) :get-position t)
              (mapcar '- *orig-player-position*))
             '(1 1)))))

;;once the player/robots need to spawn, they should spawn from the base.
(defvar *base* nil)
(defvar *orig-player-position* (list (truncate *unit-width* 2)
                                    (truncate *unit-height* 2)))
(defun ensure-base ()
(or *base*
 (setf *base*
  (let ((xy-position '(1 2)) (color '(255 255 255)))
   (lambda (&key get-position move paint seed-plant create-bot)
    (cond (seed-plant)
          (get-position (values xy-position))
          (move (case move
                 (e (decf (car xy-position)))
                 (n (incf (cadr xy-position)))
                 (w (incf (car xy-position)))
                 (s (decf (cadr xy-position)))))
          (create-bot (spawn-bot))
          (paint
           (apply 'set-color color)
           (apply 'fill-rectangle
             (append (mapcar '- xy-position
                      (funcall (ensure-player) :get-position t)
                      (mapcar '- *orig-player-position*))
              '(1 1))))))))))

(defun make-counter (&optional (default 5))
(lambda (&key (increase nil) (check nil))
 (cond (check (values default))
  (increase (incf default increase))
  (t (decf default)))))

(defparameter *default-treasures* 10)
(defparameter *treasures* nil)

(defun ensure-treasures ()
(if *treasures* *treasures*
 (setf *treasures*
  (let ((locations (list)) (treasure-count (make-counter 0)))
   (lambda (&key paint generate collect devour treasure-meter count-treasure)
    (cond (count-treasure (funcall treasure-count :check t))
          (treasure-meter (loop initially (set-color 200 175 75)
                            for n below (funcall treasure-count :check t) do
                            (fill-rectangle n 5 1 5)))
          (paint (loop initially (When (null locations)
                                  (funcall *change-secret-image*)
                                  (loop repeat *default-treasures*
                                   for x = (- (random *unit-width*)
                                              (truncate *unit-width* 2))
                                   for y = (- (random *unit-height*)
                                              (truncate *unit-height* 2))
                                   do (push (list x y) locations)))
                 for location in locations
                 for (x y) = (mapcar '- location
                              (funcall (ensure-player) :get-position t)
                              (mapcar '- *orig-player-position*))
                 for color = (loop repeat 3 collect (random 256))
                 do (apply 'set-color color)
                 do (apply 'fill-rectangle x y '(1 1))))
         (devour (when (member devour locations :test 'equal)
                  (setf locations (delete devour locations :test 'equal))
                  (funcall treasure-count :increase 1)
                  (values t)))))))))

;; controlled by arrowkeys.
(defvar *player* nil)
(defvar *player-starting-hunger* 20)
(defun ensure-player ()
(if *player* *player*
 (setf *player*
  (let ((xy-position (copy-list '(0 0)))
        (color '(255 255 0))
        (satiety (make-counter *player-starting-hunger*)))
   (lambda (&key get-position move paint consume hunger render-hunger)
    (cond (get-position (values xy-position))
          (render-hunger
           (loop initially (set-color 100 255 150)
            for n below (funcall satiety :check t) do
            (fill-rectangle (- n 6) 1 5 5)))
          (consume
           (funcall (ensure-treasures) :devour xy-position)
           (let ((flora (rassoc xy-position *plants* :test 'equal)))
            (when flora
             (when (eq 'berry (car flora))
              (setf *plants* (delete flora *plants* :test 'equal))
              (funcall satiety :increase *berry-satiation*)
              (funcall satiety :increase
               (random (1+ (funcall (ensure-treasures) :count-treasure t))))
              (dotimes (n (random *max-seeds*))
               (let* ((xy (loop for m below 2 collect (+ (nth m xy-position)
                                                      (- (random (* 2 *seedbox*))
                                                       *seedbox*))))
                      (flora (rassoc xy *plants* :test 'equal)))
                (if flora
                 (progn
                  (setf *plants* (delete flora *plants* :Test 'equal))
                  (setf *plants* (push (rplaca flora 'leaves) *plants*)))
                 (setf *plants* (push `(leaves ,@xy) *plants*)))))))))
          (hunger (funcall satiety))
          (move (case move
                 (e (incf (car xy-position))) (n (decf (cadr xy-position)))
                 (w (decf (car xy-position))) (s (incf (cadr xy-position)))))
          (paint (apply #'set-color color)
           (apply #'fill-rectangle `(,@*orig-player-position* 1 1)))))))))

;;Robot stuff
(defvar *default-program*
'(lambda (self) (Funcall self :move (nth (random 4) '(e n w s)))))
(defparameter *programming* (eval *default-program*))

(defvar *bots* (list))
(defun spawn-bot (&key (program *programming*) (hunger 5)
                 (position (copy-list (funcall (ensure-base) :get-position t)))
                 (initial-memories (list)))
(let ((new-bot
 (let ((xy-position position) (satiety hunger) (programming program)
       (color '(0 255 255)) (memories initial-memories))
  (lambda (&key paint perambulate hunger consume move)
   (cond (perambulate (funcall programming perambulate))
         (paint (apply 'set-color color)
          (let ((rect-args (append (mapcar '- xy-position
                 (funcall (ensure-player) :get-position t)
                 (mapcar '- *orig-player-position*)) '(1 1))))
           (apply 'fill-rectangle rect-args)))
         (move (case move
                (e (decf (car xy-position))) (n (incf (cadr xy-position)))
                (w (incf (car xy-position))) (s (decf (cadr xy-position)))))
         (hunger (decf satiety)) (memories memories)
         (consume (funcall (ensure-treasures) :devour xy-position)
          (when (rassoc xy-position *plants* :test 'equal)
           (when (eq (car (rassoc xy-position *plants* :test 'equal)) 'berry)
            (incf satiety *berry-satiation*)
            (incf satiety
             (random (1+ (funcall (ensure-treasures) :count-treasure t))))
            (setf *plants* (delete (rassoc xy-position *plants* :test 'equal)
                            *plants*))
           (dotimes (n (random *max-seeds*))
            (let ((xy (loop for m below 2
                       collect (+ (nth m xy-position)
                                (- (random (* 2 *seedbox*)) *seedbox*)))))
             (if (rassoc xy *plants* :test 'equal)
              (rplaca (rassoc xy *plants* :test 'equal) 'leaves)
              (push (cons 'leaves xy) *plants*))))))))))))
 (push new-bot *bots*)))

(defvar *D34D8075* (list))
(defun queue-remove-bot (bot) (push bot *D34D8075*))
(defun delete-bots ()
(setf *bots* (delete-if (lambda (bot) (member bot *D34D8075*)) *bots*)
 *D34D8075* (list)))

(defun mechanical-process (&optional (bots *bots*))
(loop initially (funcall (ensure-base) :create-bot t)
 for bot in bots
 do (funcall bot :perambulate bot)
 do (funcall bot :consume t)
 do (funcall bot :paint t)
 when (zerop (funcall bot :hunger t))
 do (queue-remove-bot bot)
 finally (delete-bots)))

(defun get-key-state (scancode)
(let ((state (ffi:c-inline (scancode) (:int) :int
              "state = SDL_GetKeyboardState(NULL);
               @(return) = (state[#0]) ? 1 : 0;")))
 (values state)))

(defun check-arrow-scancodes ()
(ffi:c-inline () () (values :int :int :int :int :int :int :int :int)
 "@(return 0) = SDL_SCANCODE_UP;
  @(return 1) = SDL_SCANCODE_DOWN;
  @(return 2) = SDL_SCANCODE_LEFT;
  @(return 3) =  SDL_SCANCODE_RIGHT;
  @(return 4) =  SDL_SCANCODE_A;
  @(return 5) = SDL_SCANCODE_B;
  @(return 6) = SDL_SCANCODE_RETURN;"))

(defparameter *funs* (list))
(defun play-game () (game (*screen-width* *screen-height*) () *funs*))

(defun make-game () " Little-by-little game creation "
;; New game
(defparameter *funs* (list))


;; Add lines in either direction
;; Add player and base
(push (lambda ()  (Funcall (ensure-player) :paint t)) *funs*)
(push (lambda () (Funcall (ensure-base) :paint t)) *funs*)
(push
 (lambda ()
  (funcall (ensure-player) :move
   (cond ((not (zerop (get-key-state *down*)))  's)
         ((not (zerop (get-key-state *up*)))    'n)
         ((not (zerop (get-key-state *left*)))  'w)
         ((not (zerop (get-key-state *right*))) 'e))))
 *funs*)
(loop repeat 10 do (spawn-plant-in -5 -5 15 15))
(push
 (lambda ()
  (advance-some-plants
   `(leaves flower ,*leaves/flowers*)
   `(flower berry ,*flowers/berries*)))
 *funs*)
(push (lambda () (funcall (ensure-player) :consume t)) *funs*)
(push (lambda () (paint-plants)) *funs*)
(push (lambda () (mechanical-process *bots*)) *funs*)
(push (lambda ()
       (setf *player*
        (and (not (zerop (funcall (ensure-player) :hunger t)))
         (ensure-player))))
 *funs*)
 (push (lambda () (funcall (ensure-player) :render-hunger t)) *funs*)
 (push (lambda () (funcall (ensure-treasures) :paint t)) *funs*)
 (push (lambda () (funcall (ensure-treasures) :treasure-meter t)) *funs*)

;; secret image - alright not very secret
(push
 (lambda ()
  (loop initially (set-color 100 100 100)
   for b below 96 do
   (loop for a below 128
    when (not (zerop (funcall *secret-image*))) do
    (fill-rectangle a b 1 1))))
  *funs*)
(play-game))