#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

#include <OpenGL/gl.h>
#include <GLUT/glut.h>

#define SLEEP_TICKS 16

#define POINTS 100

static float data[POINTS*2];
static float *points = data;
static float *prev = data + POINTS;

static void
diffuse(float diff, float evap)
{
	float diff_nrm = diff;
	float diff_cmp = 1.0 - diff_nrm;
	
	float prev = points[0];
	float curr = points[0];
	float next = points[1];

	points[0] = (curr*diff_nrm + ((prev + next)*0.5)*diff_cmp)*evap;

	for(int i=1; i<(POINTS - 1); ++i){
		prev = curr;
		curr = next;
		next = points[i + 1];

		points[i] = (curr*diff_nrm + ((prev + next)*0.5)*diff_cmp)*evap;
	}
	
	prev = curr;
	curr = next;
	points[POINTS - 1] = (curr*diff_nrm + ((prev + next)*0.5)*diff_cmp)*evap;	
}

static void
vertlet(void)
{
	for(int i=0; i<POINTS; ++i)
		prev[i] = 2.0*points[i] - prev[i];
	
	float *temp = points;
	points = prev;
	prev = temp;
}

static void
display(void)
{
	glClear(GL_COLOR_BUFFER_BIT);
	
	diffuse(0.8, 0.995);
	vertlet();
	
	glBegin(GL_LINE_STRIP); {
		for(int i=0; i<POINTS; ++i) glVertex2f(i, points[i]);
	} glEnd();
	
	glutSwapBuffers();
}

static void
timer(int value)
{
	glutTimerFunc(SLEEP_TICKS, timer, 0);
		
	glutPostRedisplay();
}

static void
idle(void)
{
	glutPostRedisplay();
}


static void
init_gl(void)
{
	glClearColor(1.0, 1.0, 1.0, 0.0);
	glColor3f(0.0, 0.0, 0.0);
	
	glLoadIdentity();
	glOrtho(-1.0, POINTS, -2.0, 2.0, -1.0, 1.0);
}


static void
init_damp(void)
{
	for(int i=0; i<POINTS; ++i){
		points[i] = 0.0;
		prev[i] = 0.0;
	}

	for(int i=20; i<50; ++i){
		points[i] = 1.0;
		prev[i] = 1.0;
	}

	for(int i=70; i<90; ++i){
		points[i] = -1.0;
		prev[i] = -1.0;
	}
}

int
main(int argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
	
	glutInitWindowSize(640, 480);
	glutCreateWindow("Wave Demo");
	
	init_gl();
	init_damp();
	
	glutDisplayFunc(display);
	glutTimerFunc(SLEEP_TICKS, timer, 0);
	//glutIdleFunc(idle);
	
	glutMainLoop();
}
