/* entheh */

#include <aldumb.h>
#include "main.h"
#include "timeloop.h"
#include "log.h"


static int in_progress = 0;
static int timeloop_speed;

static int fps_frames = 0;
volatile int fps = 0;


int game_time;

volatile int true_game_time;
int max_game_time;


void timeloop_handler(void) {

 if (true_game_time < max_game_time)
	 true_game_time++;

 if ((true_game_time % timeloop_speed) == 0) {
	 fps = fps_frames;
	 fps_frames = 0;
 }

} END_OF_FUNCTION(timeloop_handler);



/* This function is re-entrant (from the update function). Subtimeloops will
   use the same speed as their parents, though you may specify different
   values for everything else. true_game_time, and hence game_time, will
   continue to count evenly across the two loops.
*/
void timeloop(int speed,
              int max_skip,
              void *data,
              int (*update)(void *data),
              void (*draw)(void *data)) {

    if (in_progress++ == 0) {

        true_game_time = game_time = 0;
        max_game_time = max_skip;

        timeloop_speed = 1193181 / speed;

        install_int_ex(timeloop_handler, speed);
    }

	for (;;) {

		fps_frames++;
		al_poll_duh(adp);
		(*draw)(data);

		while (game_time >= true_game_time)
			yield_timeslice();

		while (game_time < true_game_time) {
			game_time++;

			al_poll_duh(adp);

			if ((*update)(data)) {

				if (--in_progress == 0)
					remove_int(timeloop_handler);

				return;
			}
		}

		max_game_time = game_time + max_skip;
	}
}



typedef struct TIMELOOP {

 struct TIMELOOP *next;

 int in_progress;
 int speed;

} TIMELOOP;


static TIMELOOP *parent = 0;



/* Call from inside the update function if you want to stop this timeloop.
   Its parameters will be saved for restart_timeloop(), but game_time will
   restart when you return. Meanwhile you may initiate a new timeloop with
   fresh game_time counters.
*/
void stop_timeloop() {

 TIMELOOP *newparent;

 remove_int(timeloop_handler);

 newparent = malloc(sizeof(TIMELOOP));
 if (!newparent) exit(37);

 newparent->in_progress = in_progress;
 newparent->speed = timeloop_speed;

 newparent->next = parent;
 parent = newparent;

 in_progress = 0;
}



void restart_timeloop() {

 TIMELOOP *old = parent;

 ASSERT(old);

 in_progress = old->in_progress;
 timeloop_speed = old->speed;

 parent = old->next;
 free(old);

 true_game_time = game_time = 0;
 max_game_time = 1;

 install_int_ex(timeloop_handler, BPS_TO_TIMER(timeloop_speed));
}



void initialise_timeloop() {
 LOCK_FUNCTION(timeloop_handler);
 LOCK_VARIABLE(true_game_time);
 LOCK_VARIABLE(max_game_time);
}
