/* truchetdraw
 * Chris Lu <lulabs.net>
 * gcc -o truchetdraw truchetdraw.c -lSDL
 */

#include <SDL/SDL.h>
#include <stdio.h>
#include <stdlib.h>

#define XRES 800
#define YRES 600
#define BLOCK_SIZE 40
#define WIDTH 3
#define COLOR_R 0x00FFFFFF
#define COLOR_L 0x00FFFFFF
#define DELAY 3300

SDL_Surface *screen;
char state[YRES/BLOCK_SIZE][XRES/BLOCK_SIZE];
int randomize = 0;
SDL_TimerID timerid;

void draw_one_square_l(unsigned int*, int, int);
void draw_one_square_r(unsigned int*, int, int);
void reset(unsigned int*, int opt);
void update();
Uint32 rnd_cb(Uint32, void*);

void print_scr()
{
	FILE *file = fopen("truchetdraw.pnm", "w");

	if(!file) {
		printf("Could not open truchetdraw.pnm\n");
		return;
	}
	fprintf(file, "P6 %d %d 255\n", XRES, YRES);

	SDL_LockSurface(screen);
	unsigned int i, *p = screen->pixels;
	for(i = 0; i < XRES * YRES; i++) {
		unsigned int pix = p[i];
		fprintf(file, "%c%c%c", (char)((pix >> 16) & 0xFF), (char)((pix >> 8) & 0xFF), (char)(pix & 0xFF));
	}
	SDL_UnlockSurface(screen);

	fclose(file);
	printf("Saved screenshot to truchetdraw.pnm.\n");
}

void handle_event(SDL_Event *ev)
{
	switch(ev->type) {
	case SDL_QUIT:
		exit(0);
	case SDL_KEYUP: {
		SDL_KeyboardEvent kev = *(SDL_KeyboardEvent*)(ev);
		switch(kev.keysym.sym) {
		unsigned int *p;
		case SDLK_r:
			SDL_LockSurface(screen);
			p = screen->pixels;
			reset(p, 1);
			SDL_LockSurface(screen);
			break;
		case SDLK_l:
			SDL_LockSurface(screen);
			p = screen->pixels;
			reset(p, 0);
			SDL_LockSurface(screen);
			break;
		case SDLK_s:
			randomize = !randomize;
			if(randomize) {
				timerid = SDL_AddTimer(50, rnd_cb, NULL);
				if(!timerid) {
					randomize = !randomize;
					printf("Error setting timer...\n");
				}
			}
			else
				SDL_RemoveTimer(timerid);
			break;
		case SDLK_p:
			print_scr();
			return;
		case SDLK_q:
			printf("Bye!\n");
			exit(0);
		default:
			break;
		}
		update();
		return;
	}
	case SDL_MOUSEBUTTONUP: {
		SDL_MouseButtonEvent mev = *(SDL_MouseButtonEvent*)(ev);
		if(mev.button = SDL_BUTTON_LEFT) {
			int rx = mev.x/BLOCK_SIZE;
			int ry = mev.y/BLOCK_SIZE;
			SDL_LockSurface(screen);
			unsigned int *p = screen->pixels;
			if(state[ry][rx])
				draw_one_square_l(p, rx, ry);
			else
				draw_one_square_r(p, rx, ry);
			SDL_UnlockSurface(screen);
		}
		update();
		return;
	}
	default:
		return;
	}
}

void draw_one_square_l(unsigned int *pixels, int x, int y)
{
	int i, j;
	int min = BLOCK_SIZE/2 - WIDTH;
	int max = BLOCK_SIZE/2 + WIDTH;
	int x_off = x * BLOCK_SIZE;
	int y_off = y * BLOCK_SIZE;
	state[y][x] = 0;

	for(i = 0; i < BLOCK_SIZE; i++) {
		for(j = 0; j < BLOCK_SIZE; j++) {
			int sum = (i + j) % BLOCK_SIZE;
			int color = sum < max && sum > min ? COLOR_L : 0;
			pixels[XRES * (y_off + i) + x_off + j] = color;
		}
	}
}

void draw_one_square_r(unsigned int *pixels, int x, int y)
{
	int i, j;
	int min = BLOCK_SIZE/2 - WIDTH;
	int max = BLOCK_SIZE/2 + WIDTH;
	int x_off = x * BLOCK_SIZE;
	int y_off = y * BLOCK_SIZE;
	state[y][x] = 1;

	for(i = 0; i < BLOCK_SIZE; i++) {
		for(j = 0; j < BLOCK_SIZE; j++) {
			int sum = (i - j + BLOCK_SIZE) % BLOCK_SIZE;
			int color = sum < max && sum > min ? COLOR_R : 0;
			pixels[XRES * (y_off + i) + x_off + j] = color;
		}
	}
}

void reset(unsigned int *p, int opt)
{
	int i, j;
	for(i = 0; i < XRES / BLOCK_SIZE; i++) {
		for(j = 0; j < YRES / BLOCK_SIZE; j++) {
			if(opt)
				draw_one_square_l(p, i, j);
			else
				draw_one_square_r(p, i, j);
		}
	}
}

static inline void do_rnd()
{
	unsigned int *p, xpos, ypos, i;

	SDL_LockSurface(screen);

	p = screen->pixels;
	for(i = 0; i < 6; i++) {
		xpos = rand() % (XRES / BLOCK_SIZE);
		ypos = rand() % (YRES / BLOCK_SIZE);
		if(rand() & 1)
			draw_one_square_l(p, xpos, ypos);
		else
			draw_one_square_r(p, xpos, ypos);
	}
	SDL_UnlockSurface(screen);

}

Uint32 rnd_cb(Uint32 x, void* p)
{
	if(randomize)
		do_rnd();

	update();
	return x;
}

void update()
{
	SDL_Flip(screen);
}

int main(int argc, char **argv)
{
	int delay;
	unsigned int *p;
	SDL_Event ev;

	screen = NULL;
	if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
		printf("Init failed: %s\n", SDL_GetError());
		return 1;
	}
	atexit(SDL_Quit);
	screen = SDL_SetVideoMode(XRES, YRES, 32, SDL_SWSURFACE);
	if(!screen) {
		printf("Video mode init failed: %s\n", SDL_GetError());
		return 1;
	}

	SDL_WM_SetCaption("truchetdraw", "truchetdraw");
	printf("click to toggle tile\n"
	       "r: reset\n"
	       "l: reset in other direction\n"
	       "s: toggle randomizing\n"
	       "p: save screenshot\n"
	       "q: quit\n");

	atexit(SDL_Quit);
	srand(time(NULL));

	SDL_LockSurface(screen);
	p = screen->pixels;
	reset(p, 0);
	SDL_UnlockSurface(screen);
	update();

	while (SDL_WaitEvent(&ev)) {
		handle_event(&ev);
	}
	return 1;
}

