/* Think of this as *pinky to mouth* MIIINI-STARS... Just like the
   original demo, but 1/8th the size! */

#include "launcher.h"


static int good_to_go = 0;
static uint8 lcd_disp[48*32/8];
static uint8 lcd_addr;
static inline void lcd_point(int x, int y) {
	if (x < 0 || y < 0 || x > 47 || y > 31) return;
	lcd_disp[(y*48+x)/8] |= 0x80 >> (x&7);
}

void lcd_setup() {
	memset(lcd_disp, 0, sizeof(lcd_disp));
	maple->vmu_draw_lcd(lcd_addr, lcd_disp);
}

/*************************************************************************/
/* These following bits of code were taken verbatim from Stars... I think 
   this is probably the fiftieth such application of this code (at the
   least =). I'll refrain from making the obvious comment. */

#include "stars_sintab.h"
#define ssin(angle) stars_sintab[(angle) & 255]
#define scos(angle) stars_sintab[((angle) + 64) & 255]

/* Draws a nifty 3D cube on the screen */

#define NUM_LINES 12
int cube_points[] = {
	-1, -1, -1,	1, -1, -1,	/* Front plane */
	1, -1, -1,	1, 1, -1,
	1, 1, -1,	-1, 1, -1,
	-1, 1, -1,	-1, -1, -1,
	
	-1, -1, 1,	1, -1, 1,	/* Back plane */
	1, -1, 1,	1, 1, 1,
	1, 1, 1,	-1, 1, 1,
	-1, 1, 1,	-1, -1, 1,
	
	-1, -1, -1,	-1, -1, 1,	/* Side connections */
	1, -1, -1,	1, -1, 1,
	1, 1, -1,	1, 1, 1,
	-1, 1, -1,	-1, 1, 1
};

/* Simple(?) Brensenham line routine */
#define abs(x) (x<0?-x:x)
void draw_line(int x1, int y1, int x2, int y2) {
	int x=x1, y=y1, d=0, dx=x2-x1, dy=y2-y1, xinc=1, yinc=1;

	if (dx == 0) {
		if (y2 < y1) {
			int t = y1;
			y1 = y2; y2 = t;
		}
		for (y=y1; y<=y2; y++) {
			lcd_point(x1, y);
		}
		return;
	}
	if (dy == 0) {
		if (x2 < x1) {
			int t = x1;
			x1 = x2; x2 = t;
		}
		for (x=x1; x<=x2; x++) {
			lcd_point(x, y1);
		}
		return;
	}


	if (abs(dx) < abs(dy)) {	/* Line is y-major */
		int xf = x * 256;
		int xstep = dx * 256 / dy;
		if (dy < 0) { yinc = -1; xstep = -xstep; }
		while (y != y2) {
			lcd_point(x, y);
			xf += xstep;
			x = xf / 256;
			y += yinc;
		}
	} else {
		int yf = y * 256;
		int ystep = dy * 256 / dx;
		if (dx < 0) { xinc = -1; ystep = -ystep; }
		while (x != x2) {
			lcd_point(x, y);
			yf += ystep;
			y = yf / 256;
			x += xinc;
		}
	}
}

int rotang = 0;
#define zkonst 128
#define mult 64
void cube_draw() {
	int cp = 0;
	int x1, y1, z1, x2, y2, z2, tx, ty, tz;
	int x1t, y1t, x2t, y2t, ra, ra2;

	ra = rotang * 256 / 512;
	ra2 = rotang % 256;

	for (cp=0; cp<(NUM_LINES*6); cp+=6) {	
		x1 = cube_points[cp+0] * mult;
		y1 = cube_points[cp+1] * mult;
		z1 = cube_points[cp+2] * mult;
		x2 = cube_points[cp+3] * mult;
		y2 = cube_points[cp+4] * mult;
		z2 = cube_points[cp+5] * mult;

		tx = (scos(ra)*x1 - ssin(ra)*y1) / 32768;
		ty = (scos(ra)*y1 + ssin(ra)*x1) / 32768;
		x1 = tx; y1 = ty;
		
		tz = (scos(ra2)*z1 - ssin(ra2)*y1) / 32768;
		ty = (scos(ra2)*y1 + ssin(ra2)*z1) / 32768;
		y1 = ty; z1 = tz;

		tx = (scos(ra)*x1 - ssin(ra)*z1) / 32768;
		tz = (scos(ra)*z1 + ssin(ra)*x1) / 32768;
		x1 = tx; z1 = tz;
		
		tx = (scos(ra)*x2 - ssin(ra)*y2) / 32768;
		ty = (scos(ra)*y2 + ssin(ra)*x2) / 32768;
		x2 = tx; y2 = ty;
		
		tz = (scos(ra2)*z2 - ssin(ra2)*y2) / 32768;
		ty = (scos(ra2)*y2 + ssin(ra2)*z2) / 32768;
		y2 = ty; z2 = tz;
		
		tx = (scos(ra)*x2 - ssin(ra)*z2) / 32768;
		tz = (scos(ra)*z2 + ssin(ra)*x2) / 32768;
		x2 = tx; z2 = tz;

		z1 += ssin(ra2) * 128 / 32768;
		z2 += ssin(ra2) * 128 / 32768;
		
		x1t = (x1*zkonst) / (zkonst+z1+mult*3);
		y1t = (y1*zkonst) / (zkonst+z1+mult*3);
		x2t = (x2*zkonst) / (zkonst+z2+mult*3);
		y2t = (y2*zkonst) / (zkonst+z2+mult*3);

		x1t = x1t/2;
		y1t = y1t/2;
		x2t = x2t/2;
		y2t = y2t/2;
		draw_line(x1t+24, y1t+16, x2t+24, y2t+16);
	}
}

void cube_one_frame() {
	cube_draw();
	rotang = (rotang+2) % 512;
}

/*************************************************************************/
/* 3D star code, written a long time ago =) */
#define NS 16
int	star_x[NS], star_y[NS], star_z[NS];

/* init stars so that they will be anywhere on the screen, but will start out
   as a single dot in the middle because of the Z coord.

   This routine has been mathmatically optimized to produce the best possible
   spread of stars on the given starfield parameters by solving the
   perspective equation for X and Y. */

#define zk 64
#define MAXX ((48*(zk+48))/zk)
#define MAXY ((32*(zk+32))/zk)

void stars_init() {
	int i;

	for (i=0; i<NS; i++) {
		star_x[i]=randnum(MAXX)-(MAXX/2);
		star_y[i]=randnum(MAXY)-(MAXY/2);
		star_z[i]=randnum(48);
	}
}

void stars_one_frame() {
	int	i;
	float	x1, y1, xn, yn, zn;

	/* Calculate each star's position and plot it on screen */
	for (i=0; i<NS; i++) {
		/* Calculate star perspective */
		xn = star_x[i]; yn = star_y[i]; zn = star_z[i];
		x1 = xn*zk / (zn + zk);
		y1 = yn*zk / (zn + zk);

		if (x1>-24 && y1>-16 && x1<24 && y1<16) {
			lcd_point(x1+24, y1+16);
		} else {
			star_z[i] = 48;
		}

		/* Move star's Z coord to show motion inward */
		star_z[i]-=2;
	}
}

/*************************************************************************/

void vmu_init() {
	lcd_addr = maple->first_vmu();

	if (!lcd_addr) {
		printf("launcher: No LCD present, disabling animations\r\n");
		good_to_go = 0;
		return;
	}
	good_to_go = 1;

	lcd_setup();
	stars_init();
}

void vmu_frame() {
	if (!good_to_go)
		return;
	memset(lcd_disp, 0, sizeof(lcd_disp));
	cube_one_frame();
	stars_one_frame();
	maple->vmu_draw_lcd(lcd_addr, lcd_disp);
}

