/* rzim.c - generate random images of colorful square tiles
 * build: cc -o rzim rzim.c
 * run: ./rzim 1920 1080 > file.ppm ; xloadimage file.ppm # or whatever
 */

#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }

#define arraysz(a) ((sizeof(a) / sizeof((a)[0])))

typedef uint32_t color_t;

typedef struct point {
	int x;
	int y;
} point;

point mkpoint(int x, int y) {
	point p;
	p.x = x;
	p.y = y;
	return p;
}

point pointadd(point p, int dx, int dy) {
	p.x += dx;
	p.y += dy;
	return p;
}

color_t mkcolor(int r, int g, int b) {
	return (r << 16) | (g << 8) | b;
}

#define MKCOLOR(r, g, b) ((r << 16) | (g << 8) | b)

uint8_t color_r(color_t c) { return (c >> 16) & 0xff; }
uint8_t color_g(color_t c) { return (c >> 8) & 0xff; }
uint8_t color_b(color_t c) { return c & 0xff; }

color_t color_setr(color_t c, uint8_t r) {
	return (c & 0xffff) | (r << 16);
}

color_t color_setg(color_t c, uint8_t g) {
	return (c & 0xff00ff) | (g << 8);
}

color_t color_setb(color_t c, uint8_t b) {
	return (c & 0xffff00) | b;
}

struct im {
	int width;
	int height;
	uint8_t *pixels;
};

void im_init(struct im *im, int width, int height) {
	im->pixels = calloc(height * width * 3, 1);
	im->width = width;
	im->height = height;
}

void im_write(const struct im *im, FILE *f) {
	fprintf(f, "P6\n");
	fprintf(f, "%d %d\n", im->width, im->height);
	fprintf(f, "255\n");

	fwrite(im->pixels, im->width * im->height * 3, 1, f);
}

uint8_t *im_pixelfor(struct im *im, point p) {
	return im->pixels + (3 * (p.y * im->width + p.x));
}

color_t im_colorat(struct im *im, point p) {
	uint8_t *b = im_pixelfor(im, p);
	return mkcolor(b[0], b[1], b[2]);
}

void im_drawpx(struct im *im, point p, color_t c) {
	uint8_t *b = im_pixelfor(im, p);
	b[0] = color_r(c);
	b[1] = color_g(c);
	b[2] = color_b(c);
}

void im_fillrect(struct im *im, point p0, point p1, color_t c) {
	for (point p = p0; p.y < p1.y; p.y++)
		for (p.x = p0.x; p.x < p1.x; p.x++)
			im_drawpx(im, p, c);
}

void im_clear(struct im *im, color_t c) {
	uint8_t r = color_r(c), g = color_g(c), b = color_b(c);
	for (int y = 0; y < im->height; y++) {
		for (int x = 0; x < im->width; x++) {
			int bi = y * im->width + x;
			im->pixels[bi * 3] = r;
			im->pixels[bi * 3 + 1] = g;
			im->pixels[bi * 3 + 2] = b;
		}
	}
}

/* and now for the random part... */
int randto(int m) {
	return rand() % m;
}

int randtofrom(int m, int b) {
	return randto(m - b) + b;
}

int coinflip() {
	return rand() & 1;
}

color_t choose_base_color() {
	static color_t base_colors[] = {
		MKCOLOR(0xff, 0x85, 0xbc),
		MKCOLOR(0xda, 0x85, 0xff),
		MKCOLOR(0x85, 0xb6, 0xff),
		MKCOLOR(0x85, 0xff, 0xca),
		MKCOLOR(0xbd, 0xff, 0x96),
		MKCOLOR(0xff, 0xf1, 0x96),
		MKCOLOR(0xff, 0xb9, 0x96),
		MKCOLOR(0xff, 0x96, 0x9c),
	};
	return base_colors[randto(arraysz(base_colors))];
}

#define TILEPAD 16
#define TILEBODY 96
#define TILESIDE (TILEBODY + 2 * TILEPAD)
#define EDGEPAD 0

void draw_tile(struct im *im, int tx, int ty, color_t c) {
	int tpx = (tx * TILESIDE) + TILEPAD + EDGEPAD;
	int tpy = (ty * TILESIDE) + TILEPAD + EDGEPAD;
	im_fillrect(im, mkpoint(tpx, tpy),
	                mkpoint(tpx + TILEBODY, tpy + TILEBODY),
	                c);
}

color_t faded_between(color_t b, color_t e, int s, int ns) {
	uint8_t rs = (color_r(e) - color_r(b)) / ns;
	uint8_t gs = (color_g(e) - color_g(b)) / ns;
	uint8_t bs = (color_b(e) - color_b(b)) / ns;

	return mkcolor(color_r(b) + rs * s, color_g(b) + gs * s,
	               color_b(b) + bs * s);
}

enum {
	COLOR_WHITE = MKCOLOR(0xee, 0xee, 0xee),
	COLOR_BLACK = MKCOLOR(0x11, 0x11, 0x11),
};

void draw_pretty_row(struct im *im, int y) {
	int ntx = (im->width - 2 * EDGEPAD) / TILESIDE;	
	color_t background = im_colorat(im, mkpoint(0, 0));
	color_t base = choose_base_color();

	for (int x = 0; x < ntx; x++) {
		color_t bc = faded_between(base, background, x, ntx);
		color_t dc = faded_between(bc, background, 1, 5);
		if (y & 1)
			draw_tile(im, ntx - x - 1, y, dc);
		else
			draw_tile(im, x, y, dc);
	}
}

void draw_pretty_stuff(struct im *im) {
	int nty = (im->height - 2 * EDGEPAD) / TILESIDE;

	for (int y = 0; y < nty; y++)
		draw_pretty_row(im, y);
}

int main(int argc, char *argv[]) {
	if (argc != 3) {
		printf("Usage: %s width height\n", argv[0]);
		return 1;
	}

	srand(time(NULL));

	struct im im;
	im_init(&im, atoi(argv[1]), atoi(argv[2]));
	im_clear(&im, MKCOLOR(0x66, 0x66, 0x66));
	draw_pretty_stuff(&im);
	im_write(&im, stdout);
	return 0;
}
