#include <fstream>
#include <sstream>
#include <list>
#include <map>
#include <string>
#include <cmath>
#include <windows.h>
#include <GL/GL.h>
#include <GL/GLU.h>
#include <SDL/SDL.h>
#include "SDL_framerate.h"
using namespace std;

const double pi = 3.14159265359;
int proj_w = 640;
int proj_h = 480;
map<string, double> prop;
SDL_Surface* screen;

struct LineSequence
{
	double x1, x2, y1, y2;
	LineSequence(double x, double y, double angle) :
		x1(x-sin(angle)*prop["trail_width"]),
		x2(x+sin(angle)*prop["trail_width"]),
		y1(y+cos(angle)*prop["trail_width"]),
		y2(y-cos(angle)*prop["trail_width"]) {}
};

int main(int argc, char* argv[])
{
	// Init
	SDL_Init(SDL_INIT_VIDEO);

	// Conf
	prop["screen_width"] = 640;
	prop["screen_height"] = 480;
	prop["trail_length"] = 50;
	prop["trail_width"] = 20;
	prop["trail_speed"] = 3;
	prop["trail_edges"] = 1;
	prop["trail_fill"] = 1;

	// Read conf
	ifstream f("conf.txt");
	if (f.is_open()) {
		string propstr;
		int pos;
		while (f.peek() != EOF) {
			getline(f, propstr);
			if ((pos = propstr.find_first_of(' ')) != string::npos) {
				stringstream s(propstr.substr(pos+1));
				double x;
				s >> x;
				prop[propstr.substr(0, pos)] = x;
			}
		}
		f.close();
	}

	// Set OpenGL attributes
	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	// Create screen & set caption
	SDL_WM_SetCaption("Trail Test", "");
	screen = SDL_SetVideoMode((int)prop["screen_width"], (int)prop["screen_height"], 32, SDL_OPENGL);

	// Setup OpenGL viewport
	glViewport(0, 0, screen->w, screen->h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, proj_w, 0, proj_h, 1.0, -100.0);
	
	// Setup OpenGL stuff
	glClearColor(0, 0, 0, 0);
	glLineWidth(2);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	// FPS manager stuff
	FPSmanager* fpsmgr = new FPSmanager;
	SDL_initFramerate(fpsmgr);
	SDL_setFramerate(fpsmgr, 60);

	// Line stuff
	double dx = proj_w/2, dy = proj_h/2, px, py, angle, dist;
	list<LineSequence> line_seq;

	bool run = true;
	int mx, my, frames = 0, time = SDL_GetTicks() + 1000;
	SDL_Event event;

	while (run) {
		// Check for events if we need to quit
		while (SDL_PollEvent(&event)) {
			switch (event.type) {
				case SDL_QUIT:
					run = 0;
					break;
				case SDL_KEYDOWN:
					if (event.key.keysym.sym == SDLK_ESCAPE)
						run = 0;
					break;
			}
		}

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

		// Get mouse location and translate the location from screen coordinates into projection coordinates
		SDL_GetMouseState(&mx, &my);
		px = (double)mx/screen->w*proj_w;
		py = (double)(screen->h-my)/screen->h*proj_h;

		angle = atan2(py-dy, px-dx);
		dist = sqrt(pow(abs(px-dx),2)+pow(abs(py-dy),2));
		if (dist > 1) {
			dx += cos(angle)*prop["trail_speed"];
			dy += sin(angle)*prop["trail_speed"];
			line_seq.push_front(LineSequence(dx, dy, angle));
			if (line_seq.size() > prop["trail_length"])
				line_seq.pop_back();
		}
		else if (line_seq.size())
			line_seq.pop_back();

		LineSequence current(dx, dy, angle);

		if (prop["trail_fill"]) {
			glEnable(GL_DEPTH_TEST);
			glBegin(GL_QUAD_STRIP);
			{
				int j = line_seq.size();
				for (list<LineSequence>::iterator i = line_seq.begin(); i != line_seq.end(); i++, j--) {
					double a = (double)j/line_seq.size();
					glColor4d(1, a, 0, a);
					glVertex2d(i->x1, i->y1);
					glVertex2d(i->x2, i->y2);
				}
			}
			glEnd();
		}

		glDisable(GL_DEPTH_TEST);
		glBegin(GL_LINES);
		{
			glColor3d(1, 1, 1);
			glVertex2d(current.x1, current.y1);
			glVertex2d(current.x2, current.y2);
		}
		glEnd();

		if (prop["trail_edges"]) {
			glBegin(GL_LINE_STRIP);
			{
				int j = 0;
				for (list<LineSequence>::reverse_iterator i = line_seq.rbegin(); i != line_seq.rend(); i++, j++) {
					double a = (double)j/line_seq.size();
					glColor4d(1, 1, 1, a);
					glVertex2d(i->x1, i->y1);
				}
			}
			glEnd();
			glBegin(GL_LINE_STRIP);
			{
				int j = 0;
				for (list<LineSequence>::reverse_iterator i = line_seq.rbegin(); i != line_seq.rend(); i++, j++) {
					double a = (double)j/line_seq.size();
					glColor4d(1, 1, 1, a);
					glVertex2d(i->x2, i->y2);
				}
			}
			glEnd();
		}

		// Flip
		SDL_GL_SwapBuffers();

		if (time <= SDL_GetTicks()) {
			stringstream s;
			s << "Trail Test - FPS: " << frames;
			SDL_WM_SetCaption(s.str().c_str(), "");
			frames = 0;
			time = SDL_GetTicks() + 1000;
		}
		else
			frames++;

		// Delay if needed to keep the framerate constant
		SDL_framerateDelay(fpsmgr);
	}

	delete fpsmgr;
	SDL_Quit();

	return 0;
}