/*===========================================
        GRRLIB (GX Version)
        Example code by Xane

        This example shows a basic particle
        engine creating a Smokebomb.
============================================*/
#include <grrlib.h>

#include <stdlib.h>
#include <wiiuse/wpad.h>
#include <math.h>
#include <ogc/lwp_watchdog.h>
#include <vector>

// Include Graphics
#include "GFX/RGFX_Background.h"
#include "GFX/RGFX_Crosshair.h"
#include "GFX/RGFX_Smoke.h"
#include "GFX/RGFX_Font.h"


// Define Effects
#define EFFECT_SMOKEBOMB    1

// Random Number (0 - 1) in float
#define RANDOM   ((((float)(rand() % 12))/12)-0.5)

using std::vector;
Mtx GXmodelView2D;

// Basic structure to hold particle data
typedef struct Particle {
	u8 id;
	float x, y;
	float sx, sy;
	u16 rot;
	u8 frame, framecnt, framedelay;
	u8 red, green, blue;
	float scale, alpha;
	float sscale, salpha;
	float scolor;
	GRRLIB_texImg *tex;
} Particle;

// Vector used as a container to iterate through all members of GRRLIB_texImg
static vector<GRRLIB_texImg *> TextureList;
static vector<Particle *> ParticleList;
static vector<Particle *> ParticleListTmp;

// Declare static functions
static void ExitGame();
static void createEffect( u8 id, int _x, int _y );
static void createParticle( u8 _id, int _x, int _y, float _scale, float _alpha, u8 _red, u8 _green, u8 _blue );
static bool updateParticle( Particle *part );
static u8 CalculateFrameRate();

// Initialize general variables
extern GXRModeObj *rmode;
ir_t P1Mote;
short WinW;
short WinH;
int P1MX, P1MY;

// Prepare Graphics
GRRLIB_texImg *GFX_Background;
GRRLIB_texImg *GFX_Crosshair;
GRRLIB_texImg *GFX_Smoke;
GRRLIB_texImg *GFX_Font;



int main() {
    u32 WPADKeyDown;
    u32 WPADKeyHeld;

    u8 FPS = 0;
    u32 ParticleCnt = 0;

	// Init GRRLIB & WiiUse
    GRRLIB_Init();
    WinW = rmode->fbWidth;
	WinH = rmode->efbHeight;
    WPAD_Init();
    WPAD_SetIdleTimeout( 60*10 );
	WPAD_SetDataFormat( WPAD_CHAN_0, WPAD_FMT_BTNS_ACC_IR );

	// Load textures
	GFX_Background = GRRLIB_LoadTextureJPG(RGFX_Background);
	GFX_Crosshair  = GRRLIB_LoadTexturePNG(RGFX_Crosshair);
	GFX_Smoke      = GRRLIB_LoadTexturePNG(RGFX_Smoke);
	GFX_Font       = GRRLIB_LoadTexturePNG(RGFX_Font);
	GRRLIB_InitTileSet( GFX_Font, 8, 16, 32 );

	// Set handles
	GRRLIB_SetMidHandle( GFX_Crosshair, true );
	GRRLIB_SetMidHandle( GFX_Smoke, true );

	// Feed the vector with the textures
	TextureList.push_back( GFX_Background );
	TextureList.push_back( GFX_Crosshair );
	TextureList.push_back( GFX_Smoke );
	TextureList.push_back( GFX_Font );

    while (true) {
        WPAD_ScanPads();
        WPADKeyDown = WPAD_ButtonsDown(WPAD_CHAN_0);
        WPADKeyHeld = WPAD_ButtonsHeld(WPAD_CHAN_0);
        WPAD_SetVRes(WPAD_CHAN_0, WinW, WinH);
		WPAD_IR(WPAD_CHAN_0, &P1Mote);

		// Resetting Vars
		GRRLIB_SetBlend( GRRLIB_BLEND_ALPHA );
		ParticleCnt = 0;

		// WiiMote IR Viewport correction
		P1MX = P1Mote.sx - 150;
		P1MY = P1Mote.sy - 150;

		// Drawing Background
		GRRLIB_DrawImg( 0, 0, GFX_Background, 0, 1, 1, GRRLIB_GetColor(255, 255, 255, 255) );

		// Add any pending objects into the main container
		if (ParticleListTmp.size()) {
			for(u32 i = 0; i<ParticleListTmp.size();i++) {
				ParticleList.push_back(ParticleListTmp[i]);
			}
			ParticleListTmp.clear();
		}

		// Update and draw all particles
		for (vector<Particle *>::iterator PartIter = ParticleList.begin(); PartIter != ParticleList.end();) {
			if (updateParticle((*PartIter)) == true) {
				GRRLIB_DrawImg( (*PartIter)->x, (*PartIter)->y, (*PartIter)->tex, (*PartIter)->rot, (*PartIter)->scale, (*PartIter)->scale, GRRLIB_GetColor( (*PartIter)->red, (*PartIter)->green, (*PartIter)->blue, GRRLIB_ClampVar8((*PartIter)->alpha*255) ) );
			} else {
				free( (*PartIter) );
				ParticleList.erase(PartIter);
				continue;
			}
			ParticleCnt += 1;
			PartIter++;
		}

		// Draw Crosshair
		GRRLIB_DrawImg( P1MX, P1MY, GFX_Crosshair, 0, 1, 1, GRRLIB_GetColor(255, 255, 255, 255) );

		// Draw Text
		GRRLIB_Rectangle( 28, 28, 280, 20, GRRLIB_GetColor(0, 0, 0, 160), 1 );
		GRRLIB_Printf   ( 32, 32, GFX_Font, 0xFFFFFFFF, 1, "Point your WiiMote on the screen." );
		GRRLIB_Rectangle( 28, 48, 200, 16, GRRLIB_GetColor(0, 0, 0, 160), 1 );
		GRRLIB_Printf   ( 32, 48, GFX_Font, 0xFFFFFFFF, 1, "Number of Particle: %d", ParticleCnt );
		GRRLIB_Rectangle( 28, 64, 64, 16, GRRLIB_GetColor(0, 0, 0, 160), 1 );
		GRRLIB_Printf   ( 32, 64, GFX_Font, 0xFFFFFFFF, 1, "FPS: %d", FPS );

		// Renders the Scene
        GRRLIB_Render();
        FPS = CalculateFrameRate();

        if (WPADKeyDown & WPAD_BUTTON_B) {
			createEffect( EFFECT_SMOKEBOMB, P1MX, P1MY );
		}
        if (WPADKeyDown & WPAD_BUTTON_HOME) {
            ExitGame();
        }
    }
    ExitGame();
    return 0;
}

static void createEffect( u8 id, int _x, int _y ) {
	u8 _ColorAdd = 0;
	switch (id) {
		case EFFECT_SMOKEBOMB:
			for (u8 i = 0; i < 5; i++) {
				createParticle( 1, (_x + (RANDOM * 10)), (_y + (RANDOM * 10)), (1.4f+(RANDOM*0.20)), 1.0f, 64, 64, 64 );
			}
			for (u8 i = 0; i < 20; i++) {
				createParticle( 3, (_x + (RANDOM * 50)), (_y + (RANDOM * 50)), 1.25f, 1.5f, 92, 92, 92 );
			}
			for (u8 i = 0; i < 5; i++) {
				_ColorAdd = (RANDOM*75);
				createParticle( 2, (_x + (RANDOM * 40)), (_y + (RANDOM * 40)), (1.0f+(RANDOM*0.20)), 1.0f, 128+_ColorAdd, 128+_ColorAdd, 128+_ColorAdd );
			}
			break;
	}
}

static void createParticle( u8 _id, int _x, int _y, float _scale, float _alpha, u8 _red, u8 _green, u8 _blue ) {
	Particle *part = (struct Particle *)calloc(1, sizeof(Particle));
	part->id  = _id;
	part->x   = _x;
	part->y   = _y;
	part->rot   = rand() % 360;
	part->red   = _red;
	part->green = _green;
	part->blue  = _blue;
	part->scale = _scale;
	part->alpha = _alpha;

	part->tex = GFX_Smoke;
	part->sy = RANDOM;
	part->sx = RANDOM;
	part->sscale = 0.9985;
	part->salpha = 0.985;
	switch (part->id) {
		case 1:
			part->sy = RANDOM * 0.5;
			part->sx = RANDOM * 0.5;
			part->sscale = 0.999;
			part->salpha = 0.992;
			part->framedelay = 10;
			part->framecnt = 2;
			break;
		case 2:
			part->scolor = 0.98;
			part->salpha = 0.95;
			break;
		case 3:
			part->sy = (RANDOM * 8);
			part->sx = (RANDOM * 8);
			part->salpha = 0.85;
			part->scolor = 0.95;
			break;
	}
	ParticleListTmp.push_back( part );
}

static bool updateParticle( Particle *part ) {
	if (part->alpha < 0.05) { part->alpha -= 0.001; }
	if (part->alpha < 0.1) { part->alpha -= 0.001; }

	part->x += part->sx;
	part->y += part->sy;
	part->scale *= part->sscale;
	part->alpha *= part->salpha;
	switch (part->id) {
		case 1:
			if (part->alpha < 0.25) { part->alpha -= 0.001; }
			if (part->framecnt == 0) {
				part->framecnt = 20;
				part->red   -= 1;
				part->green -= 1;
				part->blue  -= 1;
			}
			part->framecnt -= 1;
			break;
		case 2:
		case 3:
			part->red   *= part->scolor;
			part->green *= part->scolor;
			part->blue  *= part->scolor;
			break;
	}
	if ((part->scale < 0) || (part->alpha < 0)) { return false; }
	return true;
}


static void ExitGame() {
	// Deinitialize GRRLIB & Video
	GRRLIB_Exit();

	// Free all memory used by textures.
	for (vector<GRRLIB_texImg *>::iterator TexIter = TextureList.begin(); TexIter != TextureList.end(); TexIter++) {
		free((*TexIter)->data);
		free((*TexIter));
	}
	TextureList.clear();

	// Exit application
	exit(0);
}

static u8 CalculateFrameRate() {
    static u8 frameCount = 0;
    static u32 lastTime;
    static u8 FPS = 0;
    u32 currentTime = ticks_to_millisecs(gettime());

    frameCount++;
    if(currentTime - lastTime > 1000) {
        lastTime = currentTime;
        FPS = frameCount;
        frameCount = 0;
    }
    return FPS;
}