#include <glut.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "rng.h"

#define		WIDTH		800
#define		HEIGHT		600
#define     NWALKERS    10
#define     NSTEPS      1000

#define		PI			3.1415926535897931

//declarations
/*
void init(void);
void update(void);
float pdf(float,float);
void drawDist(void);
void motion(int,int);
void mouse(int,int,int,int);
*/

int					T=1,max=0,active=1;
int					DATA[NWALKERS][NSTEPS];

//computed consts
int					nsteps_2;

unsigned int		SEED[] = {
	0xffffffffU,	0xffffffffU, 0xffffffffU, 0xffffffffU,
	0xffffffffU,	0xffffffffU, 0xffffffffU, 0xffffffffU,
	0xffffffffU,	0xffffffffU, 0xffffffffU, 0xffffffffU,
	0xffffffffU,	0xffffffffU, 0xffffffffU, 0xffffffffU};

GLfloat				COLORS[] = {
	1.00, 0.50, 0.50,   0.50, 1.00, 0.50,   0.50, 0.50, 1.00,
	1.00, 1.00, 0.50,   1.00, 0.50, 1.00,   0.50, 1.00, 1.00,
	0.50, 0.25, 0.25,   0.25, 0.50, 0.25,   0.25, 0.25, 0.50,
	0.50, 0.50, 0.25};

void init(void) {
	int i,j,s;

	RNG_init(SEED);

	for (j=0;j<NWALKERS;j++)
		for (i=1;i<NSTEPS;i++) {
			s = DATA[j][i-1] + ((RNG_rand() < 0.5)?-1:1);
			if (s<-max) max=-s;
			if (s>max) max=s;
			DATA[j][i] = s;
		}

	j = max % 10;
	if (j>0)
		max += 10-j;

	nsteps_2=NSTEPS>>1;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0.0,NSTEPS,-max,max,-1.0,1.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();


	glClearColor(1.0,1.0,1.0,1.0);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_BLEND);
	glShadeModel(GL_FLAT);
}

void update(void) {
	if (active && T<1000)
		T++;
	glutPostRedisplay();
}

float pdf(float x, float t) {
	return exp(-(x*x)/(2*t))/sqrt(2*PI*t);
}

void drawText(int x, int y, const char* message) {
	glPushMatrix();
	glRasterPos2f((GLfloat)x,(GLfloat)y);
	while (*message)
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12,*message++);
	glPopMatrix();
}

void drawScale(void) {
	int		i;
	char	l[50];

	glColor4f(0.4,0.4,0.4,0.5);
	for (i=10;i<max;i+=10) {
		glBegin(GL_LINE_STRIP);
			glVertex2f(0,i);
			glVertex2f(10,i);
		glEnd();

		glBegin(GL_LINE_STRIP);
			glVertex2f(0,-i);
			glVertex2f(10,-i);
		glEnd();

		sprintf(l,"%d",i);
		drawText(15,i,l);
		sprintf(l,"%d",-i);
		drawText(15,-i,l);
	}
}

void drawErrorBars(void) {
	int t;
	float e;
	glColor4f(0.4,0.4,0.4,0.5);
	for (t=0;t<NSTEPS;t+=100) {
		e = 1.5*sqrt(t);
		glBegin(GL_LINE_STRIP);
			glVertex2f(t-5,e);
			glVertex2f(t+5,e);
		glEnd();
		glBegin(GL_LINE_STRIP);
			glVertex2f(t-5,-e);
			glVertex2f(t+5,-e);
		glEnd();
		glBegin(GL_LINE_STRIP);
			glVertex2f(t,e);
			glVertex2f(t,-e);
		glEnd();
	}
}

void drawDist(void) {
	int i;

	glPushMatrix();
	glScaled(NSTEPS,1,1);
	glColor4f(1.0,0.5,0.5,0.8);

	//fill area
	glBegin(GL_TRIANGLE_STRIP);
	for (i=-max;i<max;i++) {
		glVertex2f(pdf(i,T),i);
		glVertex2f(0.0,i);
	}
	glEnd();

	glColor4f(0.5,0.5,1.0,1.0);
	//restroke the edge
	glBegin(GL_LINE_STRIP);
	for (i=-max;i<max;i++)
		glVertex2f(pdf(i,T),i);
	glEnd();

	glPopMatrix();
}

void draw(void) {
	int		i,j;
	char	ts[255];

	glClear(GL_COLOR_BUFFER_BIT | GL_ACCUM_BUFFER_BIT);

	drawErrorBars();
	drawScale();

	for (j=0;j<NWALKERS;j++) {
		glBegin(GL_LINE_STRIP);
		glColor3f(0.5,0.5,1.0);
		for (i=0;i<T;i++)
			glVertex2f(i,DATA[j][i]);
		glEnd();
	}

	glLineWidth(3.0);
	glColor3f(0.0,0.0,0.0);
	glBegin(GL_LINE_STRIP);
		glVertex2f(i,-max);
		glVertex2f(i,max);
	glEnd();
	glLineWidth(1.0);

	sprintf(ts,"t=%d",T);
	drawText(T+10,max-5,ts);
	drawDist();

	glutSwapBuffers();
}

void motion(int x, int y) {
	if (x<1)		x = 1;
	if (x>WIDTH)	x = WIDTH;
	T=(int)((float)x*(float)NSTEPS/(float)WIDTH);
}


void mouse(int button, int state, int x, int y) {
	if (button==GLUT_LEFT_BUTTON) {
		active = (state==GLUT_UP);
		motion(x,y);
	}
}

int main(int argc, char **argv) {
	glutInit(&argc,argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_ACCUM);
	glutInitWindowSize(WIDTH,HEIGHT);
	glutCreateWindow("1D Random Walkers");

	glutMotionFunc(motion);
	glutMouseFunc(mouse);
	glutIdleFunc(update);
	glutDisplayFunc(draw);
	init();
	glutMainLoop();
	return 0;
}
