#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>

/* Fixed integer size type definitions. */
typedef __INT8_TYPE__   i8;
typedef __INT16_TYPE__  i16;
typedef __INT32_TYPE__  i32;
typedef __INT64_TYPE__  i64;
typedef __UINT8_TYPE__  u8;
typedef __UINT16_TYPE__ u16;
typedef __UINT32_TYPE__ u32;
typedef __UINT64_TYPE__ u64;

/* Variable integer sizes. */
typedef signed long long   llong;
typedef unsigned char      uchar;
typedef unsigned short     ushort;
typedef unsigned int       uint;
typedef unsigned long      ulong;
typedef unsigned long long ullong;

static const char *formats[3] = {
	"\033[1K"      // Erase from start of the line to the cursor
	"\033[%d;%dH"  // Set cursor to row and column (origin 1,1)
	"\033[9%dm%s", // Set colour and print text.

	"\033[%d;%dH"  // Set cursor to row and column (origin 1,1)
	"\033[9%dm%s", // Set colour and print text.

	"\033[%d;%dH"  // Set cursor to row and column (origin 1,1)
	"\033[1K"      // Erase from start of the line to the cursor
	"\033[9%dm%s", // Set colour and print text.
};

static const char *owo = "owo\0";
static const char *uwu = "uwu\0";
static const char *ovo = "ovo\0";

static void cursor_pos(int *row, int *col)
{
	/* Disable echo and canonical mode,
	 * So LF isn't needed to continue execution, and so the value isn't printed. */
	tcflag_t       flag;
	struct termios term;
	tcgetattr(STDIN_FILENO, &term);
	flag = term.c_lflag;
	term.c_lflag &= ~(ICANON | ECHO); // Disable echo and canonical mode
	tcsetattr(STDIN_FILENO, TCSANOW, &term);

	/* Acquire the cursor position(s). */
	fprintf(stderr, "\033[6n");
	getchar(); // ESC
	getchar(); // [
	char c;
	*row = 0;
	*col = 0;
	while ((c = getchar()) != ';')
		*row = (*row * 10) + (c - '0');
	while ((c = getchar()) != 'R')
		*col = (*col * 10) + (c - '0');

	/* Restore the previous flags. */
	term.c_lflag = flag;
	tcsetattr(STDIN_FILENO, TCSANOW, &term);
}

/*
 * INFO: Beware the fmt parameter, since it may break. */
static void dvd(const char *fmt, const char *txt)
{
	u8    colour = 0;
	int   x, dx = -1;
	int   y, dy = 1;
	_Bool tmpx, tmpy;
	cursor_pos(&y, &x);
	fprintf(stderr, "\033[?25l"); // Hide cursor

	ushort w, h;
	{
		struct winsize ws;
		ioctl(STDERR_FILENO, TIOCGWINSZ, &ws);
		w = ws.ws_col - 2;
		h = ws.ws_row;
	}

	while (1) {
		/* test for collisions. */
		tmpx = (1 < x) & (x < w);
		tmpy = (1 < y) & (y < h);

		/* flip direction if that axis has collided. */
		dx     = tmpx ? dx : -dx;
		dy     = tmpy ? dy : -dy;
		colour = (colour + !(tmpx & tmpy)) & 7; // increment colour on collisions

		/* Write the message */
		fprintf(stderr,
			fmt,
			y, x,
			colour, txt);

		/* Increment the co-ordinate, and ensure it remains within bounds. */
		x += dx;
		y += dy;

		usleep(50 * 1000);
	}
}

int main(int argc, char **argv)
{
	/* Perform slightly different actions, depending on arg0. */
	if (!argc) return 1;
	if (!strncmp(*argv, owo, 4)) dvd(formats[0], owo);
	if (!strncmp(*argv, uwu, 4)) dvd(formats[1], uwu);
	if (!strncmp(*argv, ovo, 4)) dvd(formats[2], ovo);
	return 1;
}
