#include <SDL.h>

Uint32 fp10s_acc = 0;
Uint32 fp10s_acc_samples = 0;

int w = 512;
int h = 384;

void update(Raytracer &rt, SDL_Surface *screen, Float *render_buffer)
{
	static Uint32 t = 0;
	Uint32 tnow = SDL_GetTicks();
	int fp10s = 10000/(int)(tnow - t);
	if (t != 0)
	{
		fp10s_acc += fp10s;
		++fp10s_acc_samples;
	}
	t = tnow;
	char s[40];
	sprintf(s, "fps:%3d.%1d", fp10s/10, fp10s%10);
	SDL_WM_SetCaption(s, NULL);

	rt.render(w, h, render_buffer);

	if (SDL_MUSTLOCK(screen))
		if (SDL_LockSurface(screen) < 0)
			return;

	Uint32 *bufp = (Uint32 *)screen->pixels;
	unsigned char c[3];
	for (Float *fd = render_buffer; fd != render_buffer + w*h*3; fd += 3)
	{
		for (int i = 0; i < 3; i++)
		{
			if (fd[i] > 1.0)
				c[i] = 255;
			else
				c[i] = (unsigned char)(fd[i] * 255.0);
		}
		*bufp = SDL_MapRGB(screen->format, c[0], c[1], c[2]);
		bufp++;
	}

	if (SDL_MUSTLOCK(screen))
		SDL_UnlockSurface(screen);

	if (screen->flags & SDL_DOUBLEBUF)
		SDL_Flip(screen);
	else
		SDL_UpdateRect(screen, 0, 0, w, h);
}

void loop_sdl(Raytracer &rt, Camera &cam,
	void (*update_callback)() = NULL, void (*key_callback)(int, int) = NULL)
{
	SDL_Surface *screen;
	Float *render_buffer;

	pyrit_verbosity = 0;
	render_buffer = (Float *) malloc(w*h*3*sizeof(Float));

	if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
		fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
		exit(1);
	}

	screen = SDL_SetVideoMode(w, h, 32, SDL_SWSURFACE|SDL_DOUBLEBUF|SDL_RESIZABLE);
	if ( screen == NULL ) {
		fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError());
		exit(1);
	}

	SDL_Event event;
	bool quit = false;
	Float roty = 0.0, rotx = 0.0, move = 0.0;
	while (!quit)
	{
		while (SDL_PollEvent(&event))
		{
			switch (event.type) {
				case SDL_VIDEORESIZE:
					w = (event.resize.w-1) / 8 * 8 + 8;
					h = (event.resize.h-1) / 8 * 8 + 8;
					render_buffer = (Float *) realloc(render_buffer, w*h*3*sizeof(Float));
					screen = SDL_SetVideoMode(w, h, 32, SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_RESIZABLE);
					break;
				case SDL_KEYDOWN:
					if (event.key.keysym.sym == SDLK_ESCAPE) {
						quit = true;
						break;
					}
					if (event.key.keysym.sym == SDLK_LEFT) {
						roty = -0.01;
						break;
					}
					if (event.key.keysym.sym == SDLK_RIGHT) {
						roty = +0.01;
						break;
					}
					if (event.key.keysym.sym == SDLK_DOWN) {
						rotx = +0.01;
						break;
					}
					if (event.key.keysym.sym == SDLK_UP) {
						rotx = -0.01;
						break;
					}
					if (event.key.keysym.sym == SDLK_w) {
						move = +0.5;
						break;
					}
					if (event.key.keysym.sym == SDLK_s) {
						move = -0.5;
						break;
					}
					if (event.key.keysym.sym == SDLK_c) {
						// print camera coordinates
						cout << "Camera: eye=" << cam.eye
						<< ", p=" << cam.p
						<< ", u=" << cam.u
						<< ", v=" << cam.v
						<< endl;
						break;
					}
					if (key_callback != NULL)
						key_callback(event.key.keysym.sym, 1);
					break;
				case SDL_KEYUP:
					if (event.key.keysym.sym == SDLK_LEFT || event.key.keysym.sym == SDLK_RIGHT) {
						roty = 0.0;
						break;
					}
					if (event.key.keysym.sym == SDLK_UP || event.key.keysym.sym == SDLK_DOWN) {
						rotx = 0.0;
						break;
					}
					if (event.key.keysym.sym == SDLK_w || event.key.keysym.sym == SDLK_s) {
						move = 0.0;
						break;
					}
					if (key_callback != NULL)
						key_callback(event.key.keysym.sym, 0);
					break;
				case SDL_QUIT:
					quit = true;
			}
		}
		cam.rotate(Quaternion(cos(roty),0,sin(roty),0).normalize());
		cam.rotate(Quaternion(cos(rotx),cam.u[0]*sin(rotx),0,cam.u[2]*sin(rotx)).normalize());
		cam.u.y = 0;
		cam.u.normalize();
		if (move != 0.0)
			cam.move(move,0,0);
		if (update_callback != NULL)
			update_callback();
		update(rt, screen, render_buffer);
	}
	free(render_buffer);

	Uint32 fp100s_aver = fp10s_acc*10/fp10s_acc_samples;
	printf("averlage fps: %3d.%2d\n\n", fp100s_aver/100, fp100s_aver%100);
	SDL_Quit();
}
