/*
   tube.c
   (c)2001 Dan Potter

   Trimmed down version of "Tube" level engine for Tryptonite
*/

/* static char id[] = "TRYP $Id: tube.c,v 1.1 2000/12/29 03:37:20 bard Exp $"; */


//#include <math.h>
#include "launcher.h"
#include "3dutils.h"
#include "pcx.h"

/* Number of steps in a tube ring: increase for more polygons */
#define STEPS 12

/* Number of tube rings per frame */
#define RINGS 15

/* Size of tube (1.0f is normal) */
#define TUBE_SIZE 2.0f
#define LAST_TUBE_SIZE 2.0f

/* Texture offsets */
static uint32 txr_tube = 0;

typedef struct {
	float a, r, g, b;
} palcol_t;

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

/* Player positions */
static point_t plr_pos;
static point_t plr_vel;
static float plr_spin;

/* Tube position parameters */
static point_t view;
static float view_xr, view_yr, view_zr;
static float txr_u_offs = 0.0f, txr_v_offs = 0.0f;


/**************************************************************************/
/* Tube path generation (uses bspline module) */

/* Position info */
static int control_pos = 1;
static float track_pos = 0;
static int cir_pos;

/* Path points (distance between points is 1.0f) */
#define PATH_PNTS 6
static point_t path_pnts[PATH_PNTS] = {
	{  0.0f,  0.0f, 0.1f },	{  0.0f,  0.0f, 1.1f },
	{  0.0f,  0.0f, 0.1f },	{  0.0f,  0.0f, 1.1f },
	{  0.0f,  0.0f, 0.1f },	{  0.0f,  0.0f, 1.1f }
};

/* Ring points (bspline t-distance between points is 1/16.0f) */
static point_t ring_pnts[RINGS];

/* Generate the ring points for a given frame */
static void circles_gen_ring_pnts() {
	float	t;
	int	rpi, cpi;
	
	/* Generate the co-efficients for the first points */
	bspline_coeff(path_pnts+control_pos);
	
	t = track_pos; cpi = control_pos;
	for (rpi=0; rpi<RINGS; rpi++) {
		/* Check for new co-efficients needed */
		while (t >= 1.0f) {
			t -= 1.0f;
			cpi++;
			if (cpi >= (PATH_PNTS - 3)) cpi = 1;
			bspline_coeff(path_pnts+cpi);
		}
		
		/* Generate a point */
		bspline_get_point(t, ring_pnts+rpi);
		ring_pnts[rpi].z = 1.5f + rpi/5.0f;
		
		/* Move forwards */
		t += 1.0f / 16.0f;
	}
}

/* "Move forward" along the track */
static void circles_move_fwd() {
	/* Move up in the B-spline curve */
	track_pos += plr_vel.z / 16.0f;
	
	/* If we went over, scoot up in the control point array */
	while (track_pos >= 1.0f) {
		track_pos -= 1.0f;
		control_pos++;
		if (control_pos >= (PATH_PNTS - 3)) control_pos = 1;
	}
}

/* Initialize circle creation (per frame) */
static void circles_new_frame() {
	/* Initialize circle position */
	cir_pos = 1;

	/* Tunnel movement */
	circles_move_fwd();
	
	/* Generate ring data */
	circles_gen_ring_pnts();
}

/* Create the next circle; save the previous one in cir_lx and cir_ly */
static void circles_next() {
	cir_pos++;
}


/**************************************************************************/
/* Tube polygon generation */

/* Polygonize the current tube ring (using circle parameters above) */
static void polyize_circle(float bright) {
	vertex_ot_t	vert;
	int		theta, pntcnt;
	palcol_t	pals[2];
	float		cir_x, cir_y, cir_z;
	float		cir_lx, cir_ly, cir_lz;
	
	/* memcpy(pals, palette + (palpos+(cir_pos-1)*16) % 256, sizeof(palcol_t));
	memcpy(pals+1, palette + (palpos+(cir_pos)*16) % 256, sizeof(palcol_t)); */
	
	pals[0].r = (1.0f - (cir_pos-1)/15.0f) * bright;
	pals[0].g = (1.0f - (cir_pos-1)/15.0f) * bright;
	pals[0].b = (1.0f - (cir_pos-1)/15.0f) * bright;
	pals[1].r = (1.0f - cir_pos/15.0f) * bright;
	pals[1].g = (1.0f - cir_pos/15.0f) * bright;
	pals[1].b = (1.0f - cir_pos/15.0f) * bright;
	/*pals[0].r = 1.0f;
	pals[0].g = 1.0f;
	pals[0].b = 1.0f;
	pals[1].r = 1.0f;
	pals[1].g = 1.0f;
	pals[1].b = 1.0f;*/
	
	cir_lx = ring_pnts[cir_pos-1].x;
	cir_ly = ring_pnts[cir_pos-1].y;
	cir_lz = ring_pnts[cir_pos-1].z;
	cir_x = ring_pnts[cir_pos].x;
	cir_y = ring_pnts[cir_pos].y;
	cir_z = ring_pnts[cir_pos].z;
	
	/* Use STEPS steps (STEPS polys per segment) */
	for (theta=0; theta<1024; theta+=(1024/STEPS)) {
		vert.flags = TA_VERTEX_NORMAL;
		vert.x = fcos(theta)*TUBE_SIZE+cir_x;
		vert.y = fsin(theta)*TUBE_SIZE+cir_y;
		vert.z = cir_z;
		vert.u = 0.0f + txr_u_offs; vert.v = 1.0f + txr_v_offs;
		vert.a = pals[1].a; vert.r = pals[1].r; vert.g = pals[1].g; vert.b = pals[1].b;
		vert.oa = vert.or = vert.og = vert.ob = 0.0f;
		pc_append(&vert);

		vert.x = fcos(theta)*LAST_TUBE_SIZE+cir_lx;
		vert.y = fsin(theta)*LAST_TUBE_SIZE+cir_ly;
		vert.z = cir_lz;
		vert.u = 0.0f + txr_u_offs; vert.v = 0.0f + txr_v_offs;
		vert.a = pals[0].a; vert.r = pals[0].r; vert.g = pals[0].g; vert.b = pals[0].b;
		pc_append(&vert);
		
		vert.x = fcos(theta+(1024/STEPS))*TUBE_SIZE+cir_x;
		vert.y = fsin(theta+(1024/STEPS))*TUBE_SIZE+cir_y;
		vert.z = cir_z;
		vert.u = 1.0f + txr_u_offs; vert.v = 1.0f + txr_v_offs;
		vert.a = pals[1].a; vert.r = pals[1].r; vert.g = pals[1].g; vert.b = pals[1].b;
		pc_append(&vert);
		
		vert.x = fcos(theta+(1024/STEPS))*LAST_TUBE_SIZE+cir_lx;
		vert.y = fsin(theta+(1024/STEPS))*LAST_TUBE_SIZE+cir_ly;
		vert.z = cir_lz;
		vert.flags = TA_VERTEX_EOL;
		vert.u = 1.0f + txr_u_offs; vert.v = 0.0f + txr_v_offs;
		vert.a = pals[0].a; vert.r = pals[0].r; vert.g = pals[0].g; vert.b = pals[0].b;
		pc_append(&vert);

		/* pntcnt += 4; */
	}
}

/**************************************************************************/
/* Main tube draw routine -- call once per frame */
float tube_constrict_phase = 0.0f;
static void tube_draw(float bright) {
	int		i;
	poly_hdr_t	poly;

	/* Send polygon header to the TA using store queues */
	ta->poly_hdr_txr(&poly, TA_OPAQUE, TA_RGB565_TWID, 128, 128, txr_tube, TA_BILINEAR_FILTER);
	ta->commit_poly_hdr(&poly);

	/* Initialize circle generation routine */
	circles_new_frame();

	/* Center the view on the first circle and add player position */
	view.x = -ring_pnts[cir_pos].x;
	view.y = -ring_pnts[cir_pos].y;
	view.z = -0.2f;

	/* Draw RINGS segments (count points) */
	/* pntcnt = 0; */
	pc_reset(sizeof(vertex_ot_t));
	for (i=0; i<RINGS-2; i++) {
		/* LAST_TUBE_SIZE = TUBE_SIZE;
		TUBE_SIZE = sin(tube_constrict_phase + i * 2 * 3.1415 / RINGS) / 2.0f + 1.5f; */
		circles_next();
		polyize_circle(bright);
	}

	// tube_constrict_phase += 0.1f;

	/* Setup transformation matrix (math is "executed" in reverse order) */
	td_reset();
	td_project();
	/*td_translate(0.0f, 0.4f * TUBE_SIZE, 0.0f);*/
	td_rotate(view_xr, view_yr, view_zr);
	td_translate(view.x, view.y, view.z);

	/* Transform and output all triangles */
	pc_transform_all();
	pc_submit_all();

	//printf("pc: %d\r\n", pntcnt);
	
	/* Rotational texture motion */
	txr_u_offs += plr_spin;
	if (txr_u_offs >= 1.0f)
		txr_u_offs -= ((int)txr_u_offs);
	
	/* Velocity texture motion */
	txr_v_offs = plr_pos.z;
	if (txr_v_offs > 1.0f)
		txr_v_offs -= ((int)txr_v_offs);
}

/**************************************************************************/
/* Tube initialization: load textures */
static int t_init() {
	Image	*img;
	
	/* Load the original image */
	img = pcx_load("tubetile.pcx");
	if (img == NULL) {
		printf("Can't load tunnel texture\r\n");
		return -1;
	}

	txr_tube = ta->txr_allocate(128*128*2);
	
	/* Twiddle it into PVR mem as 64x64 */
	txr_twiddle_scale(img->pixel_data, img->width, img->height,
		txr_tube, 128, 128);
	
	/* Free the image */
	pcx_free(img);

	return 0;
}

/********************************************************************/
/* One complete frame of the tube engine */

static int framecnt = 0;
static void draw_one_frame() {
	/* Begin opaque polygons */
	ta->begin_render();

	/* Draw tube */
	tube_draw(1.0f);
	
	/* Begin translucent polygons */
	ta->commit_eol();

	/* Send dummy polygon */
	pvr_dummy_poly(TA_TRANSLUCENT);

	/* Finish up */
	ta->commit_eol();

	/*if (framecnt & 1)
		printf("%08lx/%08lx\r\n", *((volatile uint32*)0xa05f8134), *((volatile uint32*)0xa05f8138));*/

	ta->finish_frame();
	
	framecnt++;
}


/**************************************************************************/
/* Player controls */

static void plr_init() {
	plr_pos.x = 0.0f;
	plr_pos.y = 0.0f;
	plr_pos.z = 0.0f;
	plr_vel.x = 0.0f;
	plr_vel.y = 0.0f;
	plr_vel.z = 0.0f;
	
	view_xr = 0;
	view_yr = 0;
	view_zr = 0;
}

static void plr_do_motion() {
	int x, y;
	x = y = 0;

	/* If the player is pressing left, then rotate the tunnel
	   in the counter-clockwise direction. */
	/*if (!(cond->buttons & CONT_DPAD_LEFT))
		x -= 32; */
	
	/* If the player is pressing right, then rotate the tunnel
	   in the clockwise direction. */
	/* if (!(cond->buttons & CONT_DPAD_RIGHT))
		x += 32; */

	/* Calculate in analog control */
	/* x += (((int)cond->joyx) - 128) / 4; */
	
	/* Apply the spin parameter */
	/* plr_spin = (x * 0.2f) / 256.0f; */
	/*plr_vel.x = (x * 0.1f) / 256.0f;
	view_zr -= (int)(plr_vel.x*1024);*/
	/*view_zr += 1;
	while (view_zr < 0) view_zr += 1024;
	while (view_zr >= 1024) view_zr -= 1024; */
	
	/* Apply constant player velocity */
	plr_vel.z = 0.05f;
	plr_pos.z += plr_vel.z;

	plr_spin = 0.001f;
}

/**************************************************************************/
/* Main program: init and loop drawing polygons */

int tube_init() {
	printf("Initializing tube\r\n");
	pc_init();
	plr_init();
	return 0;
}

int tube_reload() {
	return t_init();
}

int tube_frame(float bright) {
	plr_do_motion();
	tube_draw(bright);
	framecnt++;
	return 0;
}

int tube_shutdown() {
	pc_shutdown();
	return 0;
}
