#include "tetris.h"

#include <string.h>
#include "util/intdef.h"

/* Board co-ordinates:
 * 0 1 2 3
 * 4 5 6 7
 * 8 9 A B
 * C D E F */
const u16 tetromino_shapes[7][4] = {
	/*         NONE    CW_90   CW_180  CW_270 */
	[TET_I] = {0x159D, 0x89AB, 0x26AE, 0x4567},
	[TET_O] = {0x569A, 0x569A, 0x569A, 0x569A},
	[TET_T] = {0x1456, 0x1569, 0x4569, 0x1459},
	[TET_J] = {0x0456, 0x1259, 0x456A, 0x1589},
	[TET_L] = {0x4536, 0x159A, 0x4568, 0x0159},
	[TET_S] = {0x1245, 0x156A, 0x5689, 0x0459},
	[TET_Z] = {0x0156, 0x2569, 0x459A, 0x1458},
};

void tetris_get_blocks(u8 tetromino, uint *restrict positions) {
	uint shape = tetromino_shapes[tetromino & 7][tetromino >> 3];
	uint *end = positions + 8;
	while (positions < end) {
		*positions += shape & 3;
		shape >>= 2;
		positions++;
	}
}

void tetris_init_rows(const u8 *restrict data, const u8 *restrict *restrict out) {
	const u8 *restrict *end = out + TET_HEIGHT;
	while (out < end) {
		*out = data;
		data += TET_WIDTH;
		out++;
	}
}

int tetris_clear_rows(u8 *restrict *restrict rows) {
	int count = 0;
	u8 *cache[4]; /* the maximum amount of rows the user can clear at once is four */

	for (int y = 0; y < TET_HEIGHT; y++) {
		int x = 0;
		while (x < TET_WIDTH && rows[y][x] && count < 4) x++;
		if (x >= TET_WIDTH) cache[count++] = rows[y];
		else rows[y - count] = rows[y];
	}

	if (count) {
		for (int i = 0; i < count; i++) {
			memset(cache[i], 0, TET_WIDTH);
			rows[TET_HEIGHT - 1 - i] = cache[i];
		}
	}
	return count;
}

int tetris_intersect(const u8 *restrict const *restrict rows, u8 tetromino, uint x, uint y) {
	uint *pos = (uint[8]){x, y, x, y, x, y, x, y};
	uint *end = pos + 8;
	tetris_get_blocks(tetromino, pos);
	for (; pos < end; pos += 2) {
		if (*(pos + 0) >= TET_WIDTH) return 2;
		if (*(pos + 1) >= TET_HEIGHT) return 2;
		if (rows[*(pos + 1)][*pos]) break;
	}
	return pos < end;
}

void tetris_place(u8 *restrict *restrict rows, u8 tetromino, uint x, uint y) {
	uint *pos = (uint[8]){x, y, x, y, x, y, x, y};
	uint *end = pos + 8;
	tetris_get_blocks(tetromino, pos);
	tetromino &= 7;    /* strip rotation data */
	tetromino |= TET_NON_EMPTY;
	for (; pos < end; pos += 2)
		rows[*(pos + 1)][*pos] = tetromino;
}
