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

#include <sys/mman.h>

#include <linux/input.h>
#include <linux/fb.h>

//#define PIXELFORMAT_SWAPBGR
#define PIXELFORMAT_SWAPRGB_ROP_FAST

#define SIZE 640*480 
#define BPP 2 

#define INPUT "/dev/input/event0"

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

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

void fillscreen(unsigned char* buf, unsigned int r, unsigned int g, unsigned int b, unsigned int a) {
	int i;
	for (i = 0; i < SIZE; i++) {
#ifdef PIXELFORMAT_BGRA_32
		buf[ i*BPP + 0 ] = b & 0xff;
		buf[ i*BPP + 1 ] = g & 0xff;
		buf[ i*BPP + 2 ] = r & 0xff;
		buf[ i*BPP + 3 ] = 0;
#endif
#ifdef PIXELFORMAT_SWAPRGB
	        buf[ i*BPP + 1] = ((r & 0x1F) << 3) | ((g & 0x38) >> 3);
                buf[ i*BPP + 0] = ((b & 0x1F) << 3) | ((g & 7));
#endif
	};
};

void blend(unsigned char* dest, unsigned char* src1, unsigned char* src2, unsigned int size, unsigned char alpha) {

#ifdef PIXELFORMAT_BGRA_32
	struct rgba* rto, rfrom;
	int i;

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

		(*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;
#endif

#ifdef PIXELFORMAT_SWAPRGB_ROP_FAST
	unsigned int *pdest, *psrc1, *psrc2;
	unsigned int j, k;
	unsigned int a, b;

	/* initiaize the pointers */
	pdest = (unsigned int *)dest;
	psrc1 = (unsigned int *)src1;
	psrc2 = (unsigned int *)src2;	

	for (j = 0; j<(SIZE>>5); j++) {                                         
            /* this loop should unroll */                                          
	    for (k = 0; k<32; k++) {
	    
		a = (*psrc1 | (*psrc1 << 16)) & 0x07e0f81f;
		b = (*psrc2 | (*psrc2 << 16)) & 0x07e0f81f;
		b = b + a - (((b * alpha) >> 5) & 0x07e0f81f);
		*dest = (b & 0x07e0f81f) | (((b & 0x07e0f81f) >> 16) & 0xffff);
		
		pdest++;
		psrc1++;
		psrc2++;
	    };
	};
#endif
};


struct input_event ev;

int main() {
	int fd = open("/dev/fb0", O_RDWR);
	int input = open(INPUT, O_RDONLY);

	int flipped = 0;

	struct fb_var_screeninfo var;
	struct fb_var_screeninfo varfront;
	struct fb_var_screeninfo varback;
	ioctl(fd, FBIOGET_VSCREENINFO, &var);
	varfront = var;
	varback = var;

	fprintf(stderr, "res: %dx%d\tvres: %dx%d\toffset: %d,%d\n", var.xres, var.yres, var.xres_virtual, var.yres_virtual, var.xoffset, var.yoffset);

	unsigned char* screens[3];
	int curscreen = 0;
	int screenmax = 0;

	/* normal, back is alloc'd */
/*
	unsigned char* front = allocframbuffer(fd);	
	unsigned char* back = allocscreen();
*/
	
	/* msmfb, forward order */
/*	
	varfront.yoffset = 0;                                                    
        varback.yoffset = 640;  

	unsigned char* fb = allocframebuffer(fd);
	unsigned char* front = fb);
	unsigned char* back = fb + SIZE*BPP;
	unsigned char* offs = allocscreen();
*/
	
	/* msmfb, reverse order */

	varback.yoffset = 0;
	varfront.yoffset = 640;

	unsigned char* fb = allocframebuffer(fd);
	unsigned char* back = fb;
	unsigned char* front = fb + SIZE*BPP;
//	unsigned char* offs = allocscreen();

	fprintf(stderr, "front: %08x\n", (int) front);
	fprintf(stderr, "back: %08x\n", (int) back);

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

	/* clear the whole framebuffer for now */
	memset(fb, 0, SIZE*BPP*2);

	/* clear the front buffer, preferably in vblank */
	/* show the front buffer */
//	memset(front, 0, SIZE*BPP);	
//	memset(front, 63, SIZE*BPP);
//	ioctl(fd, FBIOPUT_VSCREENINFO, &varfront);

	/* clear the back buffer and prepare the animation */
	/* show the back buffer */
//	memset(back, 0, SIZE*BPP);
//	ioctl(fd, FBIOPUT_VSCREENINFO, &varback);
	
	/* now paint the front buffer with the current screen */
	/* show the front buffer */
	memcpy(front, screens[curscreen], SIZE*BPP);
//	ioctl(fd, FBIOPUT_VSCREENINFO, &varfront);

	int key;
	
	unsigned char* last = NULL;
	
	while(1) {
		last = screens[curscreen];

		fprintf(stderr, "last: %d\n", (unsigned int)last);

		/* select the current screen */

	        while (1) {                                                             
	                read(input, &ev, sizeof(struct input_event));
//			fprintf(stderr, "ev.type: %d\n", ev.type);
        	        if ((ev.type == EV_KEY) & !ev.value) break;
                };  
                
		fprintf(stderr, "ev.code: %d\n", ev.code);

		if (ev.code == KEY_Q) break;
		if (ev.code == KEY_LEFT) {
			if (curscreen == 0) {curscreen = screenmax; } else { curscreen--; };
		};
		if (ev.code == KEY_RIGHT) {
			if (curscreen == screenmax) { curscreen = 0; } else { curscreen++; };
		};
		
//		fprintf(stderr, "curscreen: %d\n", curscreen);

		/* front buffer is showing */
		/* prepare the animation in the back buffer */

#if 1 

		unsigned char alpha;
		for (alpha = 0; alpha < 32; alpha++) {
			usleep(10);	

//			ioctl(fd, FBIOPUT_VSCREENINFO, &varfront);
//			memset(back, 2*alpha, SIZE*BPP);
//			ioctl(fd, FBIOPUT_VSCREENINFO, &varback);

//			blend(back, screens[curscreen], last, SIZE, alpha);
			memcpy(back, screens[curscreen], SIZE*BPP);
//			ioctl(fd, FBIOPUT_VSCREENINFO, &varfront);
			ioctl(fd, FBIOPUT_VSCREENINFO, &varback);

//			memcpy(front, back, SIZE*BPP);

//			memcpy(back, screens[curscreen], SIZE*BPP);
//			memset(back, curscreen*64, SIZE*BPP);

			/* show the back buffer, frame complete */
//			ioctl(fd, FBIOPUT_VSCREENINFO, &varback);
			/* copy back buffer to front buffer */
			/* show the front buffer, to prepare for next frame */
			
//			memcpy(front, back, SIZE*BPP);
//			ioctl(fd, FBIOPUT_VSCREENINFO, &varfront);
		};
		

#else

		if (curscreen == 0) {
			memset(back, 63, SIZE*BPP);
//			memcpy(back, screens[curscreen], SIZE*BPP);
		} else if (curscreen == 1) {
			memset(back, 127, SIZE*BPP);
//			memcpy(back, screens[curscreen], SIZE*BPP);
		} else {
			memset(back, 255, SIZE*BPP);
//			memcpy(back, screens[curscreen], SIZE*BPP);
		};	

//		ioctl(fd, FBIOPUT_VSCREENINFO, &var);
		ioctl(fd, FBIOPUT_VSCREENINFO, &varback);
#endif

	};

	close(fd);
}

