#include <SDL/SDL.h>
#include <SDL_gfxPrimitives.h>
#include <SDL_framerate.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#define DEFAULT_SCREEN_WIDTH 500
#define DEFAULT_SCREEN_HEIGHT 500
#define DEFAULT_SCREEN_DEPTH 500

#define DEFAULT_FPS 10
#define DEFAULT_RADIUS 3
#define DEFAULT_V_MAX 10
#define DEFAULT_N_PARTICLES 6000

#if (defined (WIN32) || defined (WINDOWS))
#undef RAND_MAX
#define RAND_MAX 32768
#endif

struct particle {
	int x;
	int y;
	int z;

	int r;
	
	int dx;
	int dy;
	int dz;
};

static int nParticles = DEFAULT_N_PARTICLES;
static int v_max = 0;

int getAlpha(int z, int z_max)
{
	return (int) (((z+0.0) / z_max) * 255.0);
}

int getRand(double min, double max)
{
	return (int) min + (max - min) * ((double)rand()) / (RAND_MAX + 1.0);
}

void initParticles(struct particle *p, int r, int w, int h, int d)
{
	int i;

	for(i=0; i < nParticles; i++)
	{
		if((i % 2) == 0)
			p[i].x = getRand(0, w / 2);
		else
			p[i].x = getRand(w / 2, w);
		p[i].y = getRand(0, h);
		p[i].z = getRand(0, d);
		p[i].r = r;

		p[i].dx = getRand(-v_max, v_max);
		p[i].dy = getRand(-v_max, v_max);
		p[i].dz = getRand(-v_max, v_max);

		//printf("(%d, %d, %d) (%d, %d, %d)\n", p[i].x, p[i].y, p[i].z,
		//		p[i].dx, p[i].dy, p[i].dz);
	}
}

void drawScreen(SDL_Surface *screen, struct particle *p, int z_max)
{
	int i;
	Uint8 r, g, b, a;


	SDL_FillRect(screen, NULL, SDL_MapRGBA(screen->format, 0, 0, 0, 0));

	for(i=0; i < nParticles; i++)
	{
		a = getAlpha(p[i].z, z_max);
		if((i % 2) == 0)
		{
			r = 240;
			g = 134;
			b = 15;
		}
		else
		{
			r = 85;
			g = 15;
			b = 240;
		}

		if(p[i].r < 1)
			pixelRGBA(screen, p[i].x, p[i].y, r, g, b, a);
		else
		{
			filledCircleRGBA(screen, p[i].x, p[i].y, p[i].r, r, g, b, a);
		}
	}
}

int main(int argc, char *argv[])
{
	SDL_Surface *screen;

	struct particle *p;

	int quitFlag = 0;
	int pauseFlag = 0;

	FPSmanager fpsManager;

	SDL_Event event;

	int i;

	int r = DEFAULT_RADIUS;

	int w = 0;
	int h = 0;
	int d = 0;

	int fps = 0;

	int vid_flags = 0;//SDL_HWSURFACE | SDL_DOUBLEBUF;

	srand(0xf6);

	for(i=1; i < argc; i++)
	{
		if(argv[i][0] == '-' && strlen(argv[i]) > 1)
		{
			switch(argv[i][1])
			{
				case 'l':
					vid_flags |= SDL_FULLSCREEN;
					break;

				case 'b':
					vid_flags |= SDL_DOUBLEBUF;
					break;

				case 's':
					vid_flags |= SDL_HWSURFACE;
					break;

				case 'n':
					if(strlen(argv[i]) > 2)
						nParticles = atoi(&argv[i][2]);
					break;

				case 'r':
					if(strlen(argv[i]) > 2)
						r = atoi(&argv[i][2]);
					break;

				case 'w':
					if(strlen(argv[i]) > 2)
						w = atoi(&argv[i][2]);
					break;

				case 'h':
					if(strlen(argv[i]) > 2)
						h = atoi(&argv[i][2]);
					break;

				case 'd':
					if(strlen(argv[i]) > 2)
						d = atoi(&argv[i][2]);
					break;

				case 'f':
					if(strlen(argv[i]) > 2)
						fps = atoi(&argv[i][2]);
					break;

				case 'v':
					if(strlen(argv[i]) > 2)
						v_max = atoi(&argv[i][2]);
					break;
			}
		}
		else
		{
			printf("Unknown argument, exiting: %s", argv[i]);
			exit(4);
		}
	}

	nParticles = (nParticles <= 0) ? DEFAULT_N_PARTICLES : nParticles;
	w = (w <= 0) ? DEFAULT_SCREEN_WIDTH : w;
	h = (h <= 0) ? DEFAULT_SCREEN_HEIGHT : h;
	d = (d <= 0) ? DEFAULT_SCREEN_DEPTH : d;
	fps = (fps <= 0) ? DEFAULT_FPS : fps;
	v_max = (v_max <= 0) ? DEFAULT_V_MAX : v_max;
		
	p = malloc(nParticles * sizeof(struct particle));

	if(SDL_Init(SDL_INIT_VIDEO) != 0)
	{
		printf("Failed to init SDL: %s\n", SDL_GetError());
		exit(1);
	}

	atexit(SDL_Quit);

	screen = SDL_SetVideoMode(w, h, 16, vid_flags);
	if(screen == NULL)
	{
		printf("Failed to set video mode: %s\n", SDL_GetError());
		exit(2);
	}

	SDL_SetAlpha(screen, SDL_SRCALPHA, 255);

	//printf("n = %d, r = %d\n", n, r);

	initParticles(p, r, screen->w, screen->h, d);
	drawScreen(screen, p, d);
	SDL_Flip(screen);

	SDL_initFramerate(&fpsManager);
	SDL_setFramerate(&fpsManager, fps);

	pauseFlag = 1;

	while(quitFlag == 0)
	{
		while(SDL_PollEvent(&event))
		{
			switch(event.type)
			{
				case SDL_KEYDOWN:
					if(event.key.keysym.sym == SDLK_q)
					{
						printf("Q pressed, quiting\n");
						quitFlag = 1;
					}
					break;

				case SDL_MOUSEBUTTONDOWN:
					if(event.button.button == SDL_BUTTON_RIGHT)
					{
						initParticles(p, r, screen->w, screen->h, d);
						drawScreen(screen, p, d);
						SDL_Flip(screen);
						pauseFlag = 1;
					}
					else
						pauseFlag = !pauseFlag;
					break;

				case SDL_QUIT:
					quitFlag = 1;
					break;
			}
		}

		if(!pauseFlag)
		{
			for(i=0; i < nParticles; i++)
			{
				p[i].x += p[i].dx;
				p[i].y += p[i].dy;
				p[i].z += p[i].dz;

				if(p[i].x < p[i].r)
				{
					p[i].x = p[i].r;
					p[i].dx = -p[i].dx;
				}
				else if(p[i].x > screen->w - p[i].r)
				{
					p[i].x = screen->w - p[i].r;
					p[i].dx = - p[i].dx;
				}

				if(p[i].y < p[i].r)
				{
					p[i].y = p[i].r;
					p[i].dy = -p[i].dy;
				}
				else if(p[i].y > screen->h - p[i].r)
				{
					p[i].y = screen->h - p[i].r;
					p[i].dy = - p[i].dy;
				}

				if(p[i].z < p[i].r)
				{
					p[i].z = p[i].r;
					p[i].dz = -p[i].dz;
				}
				else if(p[i].z > screen->h - p[i].r)
				{
					p[i].z = screen->h - p[i].r;
					p[i].dz = - p[i].dz;
				}
			}

			drawScreen(screen, p, d);

			SDL_Flip(screen);
		}

		SDL_framerateDelay(&fpsManager);
	}

	free(p);

	return 0;
}

