
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <malloc.h>
#include <string.h>

#include <sys/mman.h>

#include <linux/input.h>

#define SIZE 1280*800
#define BPP 4

struct rgba {
	unsigned char r, g, b, a;
};

int getinputdevice() {
	int fd = open("/dev/input/event4", O_RDONLY);
	return fd;
};

char* allocscreen() {
	char* buf = (char *)malloc(SIZE*BPP);

	if (!buf) {
		fprintf(stderr, "screen allocation failed (in allocscreen)\n"); 	
	};

	return buf;
};	

char* allocframebuffer(int fd) {
	char* buf = mmap(0, SIZE*BPP, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
};

void fillscreen(char* buf, char r, char g, char b) {
	int i;
	for (i = 0; i < SIZE; i++) {
		buf[ i*BPP + 0 ] = b & 0xff;
		buf[ i*BPP + 1 ] = g & 0xff;
		buf[ i*BPP + 2 ] = r & 0xff;
		buf[ i*BPP + 3 ] = 0;
	};
};

void memblit(char* to, char* from, int size) {
	memcpy(to, from, size*BPP);
};

void fdblit(int fd, char* from, int size) {
	write(fd, from, size*BPP);
};

void memblend(char* to, char* from, int size, float newopac) {
	int i;

	struct rgba* rto;
	struct rgba* rfrom;
	struct rgba rsave;

	for (i = 0; i<SIZE; i++) {
		rto = (struct rgba*) to + i;
		rfrom = (struct rgba*) from + i;
		//*rto = *rfrom; 
		//rsave = *rto;

		(*rto).r = (unsigned char)(( (((*rfrom).r * newopac) + ((*rto).r * (1 - newopac)))) ) & 0xff;
		(*rto).g = (unsigned char)(( (((*rfrom).g * newopac) + ((*rto).g * (1 - newopac)))) ) & 0xff;
		(*rto).b = (unsigned char)(( (((*rfrom).b * newopac) + ((*rto).b * (1 - newopac)))) ) & 0xff;
	};
};

void memfade(char* to, char* from, int size, int numsteps, int step) {
	int i;

	struct rgba* rto;
	struct rgba* rfrom;
	struct rgba rsave;

	for (i = 0; i<SIZE; i++) {
		(*rto).r = (unsigned char)( ((*rfrom).r-(*rto).r / numsteps) * step);
		(*rto).g = (unsigned char)( ((*rfrom).g-(*rto).g / numsteps) * step);
		(*rto).b = (unsigned char)( ((*rfrom).b-(*rto).b / numsteps) * step);
	};
};

int getkey(fd) {
	struct input_event ev;
	while (1) {
		read(fd, &ev, sizeof(struct input_event));
		if ((ev.type == EV_KEY) & (ev.value == 0)) return ev.code;
	};
};

main() {

	int fd = open("/dev/fb0", O_RDWR);
	int input = getinputdevice();

	char* screens[3];
	int curscreen = 0;
	int screenmax = 0;
	
	char* front = allocframebuffer(fd);
	char* offs = allocscreen();
	char* last = NULL;
	char* black = allocscreen();
	fillscreen(black, 0, 0, 0);

	char* screenbufs = (char *)malloc(SIZE*BPP*3);
	screens[0] = screenbufs;
	screens[1] = screenbufs + SIZE*BPP;
	screens[2] = screenbufs + SIZE*BPP*2;
//	screens[0] = allocscreen();
//	screens[1] = allocscreen();
//	screens[2] = allocscreen();
	//**screens = (char *)malloc(SIZE*BPP*3);
	screenmax = 2;
	fillscreen(screens[0], 255, 0, 0);
	fillscreen(screens[1], 0, 255, 0);
	fillscreen(screens[2], 0, 0, 255);

	memblend(offs, screens[curscreen], SIZE, 1);
	memblit(front, offs, SIZE);

	int key;
	while(1) {
		//memblit(offs, screens[curscreen], SIZE);
		last = screens[curscreen];

		key = getkey(input);
		if (key == KEY_Q) break;
		if (key == KEY_LEFT) {
			if (curscreen == 0) {curscreen = screenmax; } else { curscreen--; };
		};
		if (key == KEY_RIGHT) {
			if (curscreen == screenmax) { curscreen = 0; } else { curscreen++; };
		};

		float shft = 0;
		for (shft = 0; shft < 1; shft+= 0.05) {
			usleep(1);	
//			memblit(offs, black, SIZE);
//			memblend(offs, last, SIZE, 1-shft);
			memblit(offs, last, SIZE);
			memblend(offs, screens[curscreen], SIZE, shft);
			memblit(front, offs, SIZE);
		};
		//memblit(front, screens[curscreen], SIZE);
	};

	close(fd);
}

