/*

Scrolling Example
Relminator 2010 
Richard Eric M. Lope BSN RN

http://rel.betterwebber.com


Notes:
*/


#include <nds.h>
#include <stdio.h>


#include "gl2d.h"


// our map dimensions
#define  MAP_WIDTH 32
#define  MAP_HEIGHT 32


/*
	I'm using the struct of player from the
	Animate simple man/woman exmple in the 
	"/examples" folder
*/

enum PlayerState {P_RIGHT = 0, P_UP = 1, P_DOWN = 2, P_LEFT = 3};

typedef struct Player
{

	int x;
	int y;

	int gfx_frame;

	int state;
	int anim_frame;

}Player;



typedef struct Level
{
	int width;
	int height;
	
	int camera_x;
	int camera_y;
	
	int tile_x;
	int tile_y;
	
	int pixel_x;
	int pixel_y;
	
}Level;


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

    MAIN 

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


void AnimatePlayer(Player *p);
void DrawMap(Level *lvl, unsigned short map[MAP_WIDTH][MAP_HEIGHT], glImage *tiles);
void CameraUpdate(Level *lvl, Player *p);
void InitMap( unsigned short map[MAP_WIDTH][MAP_HEIGHT] );


// GRIT auto-genrated arrays of images
#include "tiles.h"
#include "crono.h"

// Texture Packer auto-generated UV coords
#include "uvcoord_crono.h"

// this imageset would use our texture packer generated coords so it's kinda
// safe and easy to use 
// CRONO_NUM_IMAGES is a value from "uvcoord_crono.h"
glImage  crono_images[CRONO_NUM_IMAGES];

// this tileset won't make use of our texture packer generated coords
// messy, manual and prone to errors
glImage  tiles_images[8*8];  //BMP is 128x128 and tiles are 16x16 so.. (128/16) * (128 /16) = 8 * 8


unsigned short level_map[MAP_WIDTH][MAP_HEIGHT];


int main(int argv, char *argc[])
{

	
	Player crono;
	Level lvl;
	
	crono.x = 16 * 5;
	crono.y = 16 * 5;
	crono.state = P_RIGHT;
	crono.gfx_frame = 18;
	
	lvl.width = MAP_WIDTH;
	lvl.height = MAP_HEIGHT;
	
	InitMap(level_map);
	
	videoSetMode(MODE_5_3D);
	
	
	consoleDemoInit();
	
	iprintf("\x1b[1;1HSCROLLING TEST");
	iprintf("\x1b[3;1HArrow Keys to move");
	
	iprintf("\x1b[6;1HRelminator");
	iprintf("\x1b[7;1Hhttp://rel.betterwebber.com");
	
	
	// Initialize GL in 3d mode
	glInitScreen();
	
	
	// set  Bank A to texture (128 kb)
	vramSetBankA(VRAM_A_TEXTURE);
	

	// Our texture handles for enemies and tiles
	int crono_textureID;  
	int tiles_textureID;  
	
	// load paletted 8 bit bitmap with color index 0 as the transparent color
	glGenTextures(1, &crono_textureID);
	glBindTexture(0, crono_textureID);
	glTexImage2D( 0, 0, GL_RGB256, TEXTURE_SIZE_256 , TEXTURE_SIZE_128, 0, GL_TEXTURE_WRAP_S|GL_TEXTURE_WRAP_T|TEXGEN_OFF|GL_TEXTURE_COLOR0_TRANSPARENT, (u8*)cronoBitmap );

	glGenTextures(1, &tiles_textureID);
	glBindTexture(0, tiles_textureID);
	glTexImage2D( 0, 0, GL_RGB256, TEXTURE_SIZE_128 , TEXTURE_SIZE_128, 0, GL_TEXTURE_WRAP_S|GL_TEXTURE_WRAP_T|TEXGEN_OFF|GL_TEXTURE_COLOR0_TRANSPARENT, (u8*)tilesBitmap );

	// load the palette for our 256 color BMP
	gluTexLoadPal(cronoPal, 512,GL_RGB256);

	// initialize sprites easily by using the UV coords generated by the texture packer
	glInitSpriteset(crono_images, CRONO_NUM_IMAGES, crono_texcoords, crono_textureID);

	// initialize rigid 16 x 16 tileset without the use of texture packer
	// very messy and prone to bugs
	glInitTileset(tiles_images, 30, 16, 16,
                  128, 128, tiles_textureID);

			
	int frame = 0;
	int key;
	
	
	while(1)
	{
		// increment frame counter and rotation offsets
	
		frame++;
	
		scanKeys();
		key = keysHeld();
		
		if (key & KEY_RIGHT)
		{
			crono.x++;
			crono.state = P_RIGHT;
		}
		
		if (key & KEY_LEFT)
		{
			crono.x--;
			crono.state = P_LEFT;
		}
		
		if (key & KEY_UP)
		{
			crono.y--;
			crono.state = P_UP;
		}
		
		if (key & KEY_DOWN)
		{
			crono.y++;
			crono.state = P_DOWN;
		}
			
		AnimatePlayer(&crono);
		
		
		CameraUpdate(&lvl, &crono);
		
		
		// draw 3d here
		
		// start of 2d drawing mode
		glStart2D();
		
			// needs to be called before texturing
			// or sprite gets blended by last glcolor call
			glColor(RGB15(31,31,31)); 
			DrawMap(&lvl, level_map, tiles_images);
			
			if (crono.state < P_LEFT)
				glSpriteRotate(crono.x - lvl.camera_x, crono.y - lvl.camera_y, 0,GL_FLIP_NONE , &crono_images[crono.gfx_frame]);
			else
				glSpriteRotate(crono.x - lvl.camera_x, crono.y - lvl.camera_y, 0,GL_FLIP_H , &crono_images[crono.gfx_frame]);
			
			 
		glEnd2D();  // end 2d mode
		
		// back to 3d mode again
		
		// draw some 3d here
		
		glFlush(0);

		swiWaitForVBlank();
	}

	return 0;
	
}


void AnimatePlayer(Player *p)
{

	const int FRAMES_PER_ANIMATION = 6;

	static int frame = 0;
	frame++;
	
	if ((frame & 7) == 0)
	{ 
		p->anim_frame++;
		if (p->anim_frame >= (FRAMES_PER_ANIMATION))
			p->anim_frame = 0;
	}
	
	// P_RIGHT, P_UP and P_DOWN is calculated normally.
	// P_LEFT is P_RIGHT flipped.
	switch (p->state)
	{
		case P_RIGHT:
		case P_UP:
		case P_DOWN:
			p->gfx_frame = p->anim_frame + p->state * FRAMES_PER_ANIMATION;
			break;
		case P_LEFT:
			p->gfx_frame = p->anim_frame + P_RIGHT * FRAMES_PER_ANIMATION;
			break;
		default:
			p->gfx_frame = p->anim_frame + p->state * FRAMES_PER_ANIMATION;
	}
		
}


void DrawMap(Level *lvl, unsigned short map[MAP_WIDTH][MAP_HEIGHT], glImage *tiles)
{
	const int TILE_SIZE = 16;
 
	const int SCREEN_TILE_X = SCREEN_WIDTH / TILE_SIZE;
	const int SCREEN_TILE_Y = SCREEN_HEIGHT / TILE_SIZE;
	
	int x, y;
	int tile_x, tile_y;
	int screen_x, screen_y;
	int i;
	
	for (y = 0; y <= SCREEN_TILE_Y; y++)
	{
		for (x = 0; x <= SCREEN_TILE_X; x++) 
		{
			tile_x = lvl->tile_x + x;
			tile_y = lvl->tile_y + y;
			i = map[tile_x][tile_y];
			screen_x = (x * TILE_SIZE) - lvl->pixel_x;      //Calculate where to put a
            screen_y = (y * TILE_SIZE) - lvl->pixel_y;      //particular tile
                
			glSprite(screen_x, screen_y, GL_FLIP_NONE , &tiles[i]);
		}
	}
	
	
}

void CameraUpdate(Level *lvl, Player *p)
{

	// set constants for middle of screen
	const int SCREEN_MID_WIDTH = SCREEN_WIDTH / 2;
	const int SCREEN_MID_HEIGHT = SCREEN_HEIGHT / 2;

	const int TILE_SIZE = 16;
 
	// update the camera
	lvl->camera_x = p->x - SCREEN_MID_WIDTH;
	lvl->camera_y = p->y - SCREEN_MID_HEIGHT;
	
	// limit camera X values
	if ( lvl->camera_x < 0 ) lvl->camera_x = 0;
	if ( lvl->camera_x > ((lvl->width-2) * TILE_SIZE ) - SCREEN_WIDTH )
	{
		lvl->camera_x = ((lvl->width-2) * TILE_SIZE ) - SCREEN_WIDTH;
	}
	
	// limit camera Y values
	if ( lvl->camera_y < 0 ) lvl->camera_y = 0;
	if ( lvl->camera_y > ((lvl->height-2) * TILE_SIZE ) - SCREEN_HEIGHT )
	{
		lvl->camera_y = ((lvl->height-2) * TILE_SIZE ) - SCREEN_HEIGHT;
	}
	
	// calculate level starting tiles
	lvl->tile_x = lvl->camera_x / TILE_SIZE; 
	lvl->tile_y = lvl->camera_y / TILE_SIZE; 
	
	// calculate tile pixel offsets
	// Only works with power of 2 tilesize
	// use "%" for non-power of 2 sizes
	lvl->pixel_x = lvl->camera_x & (TILE_SIZE - 1);
	lvl->pixel_y = lvl->camera_y & (TILE_SIZE - 1);
	
}


void InitMap( unsigned short map[MAP_WIDTH][MAP_HEIGHT] )
{

	int x, y;
	
	for (y = 0; y < MAP_HEIGHT; y++)
	{
		for (x = 0; x < MAP_WIDTH; x++) 
		{
			map[x][y] = rand() & 63;
		}
	}

}
