// GraphicsTemplate.cpp
// 
//////////////////////////////////////////////////////////////////////////////////////////
// includes 
//////////////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"


#define PI				  3.1415926
#define Base_num	      1100
#define Base_LifeTime     120
#define Base_size		  1.5

using namespace std;

//////////////////////////////////////////////////////////////////////////////////////////
// externals 
//////////////////////////////////////////////////////////////////////////////////////////
extern cRenderClass graphics;

// I define a particle struct here, it includes all elements I will use
typedef struct Particle
{
	sRGB color;
	float size;
	float direction;
	float speed;
	int lifeTime;
	int Time;
	float cur_X, cur_Y;
	
}Particle;

vector<Particle> g_particleV;
bool g_pause = false;
Particle *p1;
bool fireworkstatus = false;
bool firstfireworkstatus = false;
//When the status is true, it means the end of the fireworks explosion
bool status = true;
/*Indicates whether the firework is rising or needs to be created, 
true indicates ascent,false indicates that a rising firework object needs to be created*/
bool increasingFlag = true;
//It's used to store the position where the rising fireworks finally dissipated.
float ori_x = 0, ori_y = 0;
int time1 = 1;
//Initialize the patricle struct
void initParticle(Particle &p) {
	p.size = rand() % (int)Base_size + Base_size + 0.1;
	p.direction = 270;
	while (p.direction > 269 && p.direction < 271)
	{
		p.direction = rand() % 360;
	}

	//Convert p.direction to specific degrees.
	p.direction = PI * p.direction / 180.0;
	//adjust the elements of particle
	p.speed = ((float)(rand() % 101)) / 100.0-0.1;
	p.color.r = ((float)(rand() % 100))/110;
	p.color.g = ((float)(rand() % 100))/110;
	p.color.b = ((float)(rand() % 100))/110;
	p.Time = 0;
	p.lifeTime = rand() % Base_LifeTime + Base_LifeTime-10;
	p.cur_X = 0;
	p.cur_Y = 0;
}
//init specific values
void initfirstParticle(Particle *p) {
	p->size = rand() % (int)Base_size + Base_size + 3;
	p->direction = 90;
	p->direction = PI * p->direction / 180.0;
	p->speed =  0.50;
	p->color.r = ((float)(rand() % 101)) / 100.0;
	p->color.g = ((float)(rand() % 101)) / 100.0;
	p->color.b = ((float)(rand() % 101)) / 100.0;
	p->Time = 0;
	p->lifeTime = rand() % Base_LifeTime + Base_LifeTime + 650;
	p->cur_X = (float)(rand() % 400) + 100;
	p->cur_Y = 0;
}

//Calculate the current particle position.
void calfirstParticleCurPos(Particle &p) {
	p.cur_Y += p.speed * sin(p.direction)-0.5*0.00006*time1;
	p.lifeTime--;
	time1++;
	if(p.lifeTime == 0)
	{
		time1 = 0;
	}
}

void calParticleCurPos(Particle &p) {
	p.cur_X += p.speed * cos(p.direction);
	p.cur_Y += p.speed * sin(p.direction)-0.5*0.0065*p.Time;
	p.lifeTime--;
	p.Time++;
	if (p.lifeTime == 0)
		p.Time = 0;
}


//////////////////////////////////////////////////////////////////////////////////////////
// renderScene() - render the scene
//////////////////////////////////////////////////////////////////////////////////////////
void renderScene()
{
	// set a colour
	graphics.setColour(1.0f, 0.5f, 0.1f);

	// set pixel size..
	graphics.setPointSize(1);

	if (firstfireworkstatus)
	{
		for (auto it = g_particleV.begin(); it != g_particleV.end(); it++) {
			//Assign colors to particles
			graphics.setColour(it->color.r, it->color.g, it->color.b);
			//Assign size to particles
			graphics.setPointSize(it->size);
			graphics.drawPixel(ori_x + it->cur_X, ori_y + it->cur_Y);
		}
	}
	if (firstfireworkstatus == false && status) {
		graphics.setColour(p1->color.r, p1->color.g, p1->color.b);
		graphics.setPointSize(p1->size);
		graphics.drawPixel(p1->cur_X, p1->cur_Y);
	}
	// render the scene
	graphics.render();
}

//////////////////////////////////////////////////////////////////////////////////////////
// update() - update function
//////////////////////////////////////////////////////////////////////////////////////////
void update()
{
	//'bool status = false' means that the exploding fireworks are over at this time
	//allocate dynamic memory to the deleted particles
	if (!firstfireworkstatus && status &&increasingFlag)
	{
		//The life of the particles in the process of launching fireworks is over, and the fireworks have not yet bloomed

		/*For ascending particles,
		the program first creates the global variable Particle * p1 in the header file.
		In the program, when it is necessary to allocate memory for the rising particles,
		use p1 = new particles to allocate memory space for p1.
		When the life of the rising particle is over, it immediately releases its memory by deleting p1.*/

		if (p1->lifeTime <= 0 && firstfireworkstatus == false)
		{
			firstfireworkstatus = true;//End of particle ascent
			ori_x = p1->cur_X;
			ori_y = p1->cur_Y;
			delete p1;
			status = false;
			increasingFlag = false;
		}
		else
			calfirstParticleCurPos(*p1);

	}
	if (fireworkstatus && firstfireworkstatus == false && increasingFlag == false)
	{
		p1 = new Particle;
		initfirstParticle(p1);

		status = true;
		increasingFlag = true;
	}

	if (firstfireworkstatus)
	{
		for (auto it = g_particleV.begin(); it != g_particleV.end(); ) {
			//When the particle exceeds the boundary or the particle's lifeTime ends
			if (it->cur_X + ori_x > SCREEN_WIDTH || it->cur_Y + ori_y > SCREEN_HEIGHT 
				|| it->lifeTime <= 0) {
				if (!g_pause) {
					it = g_particleV.erase(it);
					if (g_particleV.empty())
					{
						fireworkstatus = true;
						firstfireworkstatus = false;
						srand(time(NULL));
						/*It defines N particles in the loop, calls initParticle to initialize the parameters,
						and then calls the vector method to load into g_particleV. 
						In the library function, when pushing the object,
						if the data in the vector is full, 
						the program will call the _Reserve function and _Reallocate function to reallocate the space of the vector, 
						and then allocate the extra space to the new object.*/
						int n = rand() % Base_num + Base_num;
						for (int i = 0; i < n; i++) {
							Particle p;
							initParticle(p);
							g_particleV.push_back(p);
						}
						renderScene();
						break;
					}

				}
				else
					//The system suspends and releases the particle memory
					it = g_particleV.erase(it);
			}
			else {
				calParticleCurPos(*it);
				it++;
			}
		}
	}
	// always re-render the scene..
	renderScene();
}

//Yi Li's firework code, press 'END' to pause the game and press 'HOME'to continue.
void processSpecialKeys(int key, int x, int y)
{
	switch (key)
	{
	case GLUT_KEY_END:
		g_pause = true;
		break;
	case GLUT_KEY_HOME:
	{
		g_pause = false;
		g_particleV.resize(rand() % Base_num + Base_num);
	}
	break;
	}
}

//////////////////////////////////////////////////////////////////////////////////////////
// _tmain() - program entry point
//////////////////////////////////////////////////////////////////////////////////////////
int _tmain(int argc, _TCHAR* argv[])
{
	// init glut stuff..
	graphics.create(argc, argv);

	// good place for one-off initialisations and objects creation..
	p1 = new Particle;
	initfirstParticle(p1);

	/*When the program is running,
	the system calls the STL library vector <T> in C ++
	to define a global variable g_particleV to store a certain number of Particle objects.
	The program uses vectors to dynamically store particles.
	In the main fuction,
	I will use the rand () function to randomly generate multiple particles when the fireworks bloom*/

	srand(time(NULL));
	int n = rand() % Base_num + Base_num;

	for (int i = 0; i < n; i++) {
		Particle p;
		initParticle(p);
		g_particleV.push_back(p);
	}
	glutSpecialFunc(processSpecialKeys);

	// enter game loop..
	graphics.loop();

	return 0;
}




