class Plane
{
	constructor(verts, colors)
	{
		this.verts = [
			verts[0],
			verts[1],
			verts[2],
			verts[3]
		];
		
		var newVerts = vertsToGLpoints(this.verts);
		
		this.vertices = [
			newVerts[0][0],newVerts[0][1],newVerts[0][2],
			newVerts[1][0],newVerts[1][1],newVerts[1][2],
			newVerts[2][0],newVerts[2][1],newVerts[2][2],
			newVerts[3][0],newVerts[3][1],newVerts[3][2]
		];
		
		this.colors = [
			colors[0][0],colors[0][1],colors[0][2],
			colors[1][0],colors[1][1],colors[1][2],
			colors[2][0],colors[2][1],colors[2][2],
			colors[3][0],colors[3][1],colors[3][2]
		];
		
		this.indices = [
			3,2,1,
			3,1,0
		];
		
		this.vertex_buffer = gl.createBuffer();
		
		this.Index_Buffer = gl.createBuffer();
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.Index_Buffer);
		gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.indices), gl.STATIC_DRAW);
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
		
		this.color_buffer = gl.createBuffer ();
	}
	
	updateVerts()
	{
		var newVerts = vertsToGLpoints(this.verts);
		
		this.vertices = [
			newVerts[0][0],newVerts[0][1],newVerts[0][2],
			newVerts[1][0],newVerts[1][1],newVerts[1][2],
			newVerts[2][0],newVerts[2][1],newVerts[2][2],
			newVerts[3][0],newVerts[3][1],newVerts[3][2]
		];
	}
	
	draw()
	{
		gl.bindBuffer(gl.ARRAY_BUFFER, this.vertex_buffer);
		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.vertices), gl.STATIC_DRAW);
		gl.bindBuffer(gl.ARRAY_BUFFER, null);
		
		gl.bindBuffer(gl.ARRAY_BUFFER, this.color_buffer);
		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.colors), gl.STATIC_DRAW);
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
		
		gl.bindBuffer(gl.ARRAY_BUFFER, this.vertex_buffer);
		
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.Index_Buffer);
		var coord = gl.getAttribLocation(shaderProgram, "coordinates");
		gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
		gl.enableVertexAttribArray(coord);
		
		gl.bindBuffer(gl.ARRAY_BUFFER, this.color_buffer);
		var color = gl.getAttribLocation(shaderProgram, "color");
		gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ;
		gl.enableVertexAttribArray(color);
		
		gl.drawElements(gl.TRIANGLES, this.indices.length, gl.UNSIGNED_SHORT, 0);
	}
}

class Cube
{
	constructor(pos, size, color)
	{
		this.pos = pos;
		this.size = size;
		this.color = color;
		
		this.planes = [];
		//back
		this.planes.push(
			new Plane(
				[
					[this.pos[0] + (this.size[0] * 0.5), this.pos[1] + (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)],
					[this.pos[0] + (this.size[0] * 0.5), this.pos[1] - (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)],
					[this.pos[0] - (this.size[0] * 0.5), this.pos[1] - (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)],
					[this.pos[0] - (this.size[0] * 0.5), this.pos[1] + (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)]
				],
				this.color
			)
		);
		//front
		this.planes.push(
			new Plane(
				[
					[this.pos[0] - this.size[0] * 0.5, this.pos[1] + this.size[1] * 0.5, this.pos[2] - this.size[2] * 0.5],
					[this.pos[0] - this.size[0] * 0.5, this.pos[1] - this.size[1] * 0.5, this.pos[2] - this.size[2] * 0.5],
					[this.pos[0] + this.size[0] * 0.5, this.pos[1] - this.size[1] * 0.5, this.pos[2] - this.size[2] * 0.5],
					[this.pos[0] + this.size[0] * 0.5, this.pos[1] + this.size[1] * 0.5, this.pos[2] - this.size[2] * 0.5]
				],
				this.color
			)
		);
		//right
		this.planes.push(
			new Plane(
				[
					[this.pos[0] + (this.size[0] * 0.5), this.pos[1] + (this.size[1] * 0.5), this.pos[2] - (this.size[2] * 0.5)],
					[this.pos[0] + (this.size[0] * 0.5), this.pos[1] - (this.size[1] * 0.5), this.pos[2] - (this.size[2] * 0.5)],
					[this.pos[0] + (this.size[0] * 0.5), this.pos[1] - (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)],
					[this.pos[0] + (this.size[0] * 0.5), this.pos[1] + (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)]
				],
				this.color
			)
		);
		//left
		this.planes.push(
			new Plane(
				[
					[this.pos[0] - (this.size[0] * 0.5), this.pos[1] + (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)],
					[this.pos[0] - (this.size[0] * 0.5), this.pos[1] - (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)],
					[this.pos[0] - (this.size[0] * 0.5), this.pos[1] - (this.size[1] * 0.5), this.pos[2] - (this.size[2] * 0.5)],
					[this.pos[0] - (this.size[0] * 0.5), this.pos[1] + (this.size[1] * 0.5), this.pos[2] - (this.size[2] * 0.5)]
				],
				this.color
			)
		);
		//bottom
		this.planes.push(
			new Plane(
				[
					[this.pos[0] + (this.size[0] * 0.5), this.pos[1] - (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)],
					[this.pos[0] + (this.size[0] * 0.5), this.pos[1] - (this.size[1] * 0.5), this.pos[2] - (this.size[2] * 0.5)],
					[this.pos[0] - (this.size[0] * 0.5), this.pos[1] - (this.size[1] * 0.5), this.pos[2] - (this.size[2] * 0.5)],
					[this.pos[0] - (this.size[0] * 0.5), this.pos[1] - (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)]
				],
				this.color
			)
		);
		//top
		this.planes.push(
			new Plane(
				[
					[this.pos[0] + (this.size[0] * 0.5), this.pos[1] + (this.size[1] * 0.5), this.pos[2] - (this.size[2] * 0.5)],
					[this.pos[0] + (this.size[0] * 0.5), this.pos[1] + (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)],
					[this.pos[0] - (this.size[0] * 0.5), this.pos[1] + (this.size[1] * 0.5), this.pos[2] + (this.size[2] * 0.5)],
					[this.pos[0] - (this.size[0] * 0.5), this.pos[1] + (this.size[1] * 0.5), this.pos[2] - (this.size[2] * 0.5)]
				],
				this.color
			)
		);
	}
	
	moveTo(pos)
	{
		for(var i = 0; i < this.planes.length; i++)
		{
			for(var j = 0; j < 4; j++)
			{
				for(var k = 0; k < 3; k++)
				{
					this.planes[i].verts[j][k] = this.planes[i].verts[j][k] - this.pos[k] + pos[k];
				}
			}
			this.planes[i].updateVerts();
		}
		
		this.pos = pos;
	}
	
	rotateOneY(angle)
	{
		angle *= 0.01745329252;
		for(var i = 0; i < this.planes.length; i++)
		{
			for(var j = 0; j < 4; j++)
			{
				var vert0 = this.planes[i].verts[j][0];
				var vert2 = this.planes[i].verts[j][2];
				this.planes[i].verts[j][0] = (vert0 - this.pos[0]) * Math.cos(angle) + (vert2 - this.pos[2]) * Math.sin(angle) + this.pos[0];
				this.planes[i].verts[j][2] = (vert2 - this.pos[2]) * Math.cos(angle) - (vert0 - this.pos[0]) * Math.sin(angle) + this.pos[2];
			}
			this.planes[i].updateVerts();
		}
	}
	rotateAllY(angle)
	{
		angle *= 0.01745329252;
		for(var i = 0; i < this.planes.length; i++)
		{
			for(var j = 0; j < 4; j++)
			{
				var vert0 = this.planes[i].verts[j][0];
				var vert2 = this.planes[i].verts[j][2];
				this.planes[i].verts[j][0] = vert0 * Math.cos(angle) + vert2 * Math.sin(angle);
				this.planes[i].verts[j][2] = vert2 * Math.cos(angle) - vert0 * Math.sin(angle);
			}
			this.planes[i].updateVerts();
		}
	}
	
	rotateAllZ(angle)
	{
		angle *= 0.01745329252;
		for(var i = 0; i < this.planes.length; i++)
		{
			for(var j = 0; j < 4; j++)
			{
				var vert0 = this.planes[i].verts[j][0];
				var vert1 = this.planes[i].verts[j][1];
				this.planes[i].verts[j][0] = vert0 * Math.cos(angle) - vert1 * Math.sin(angle);
				this.planes[i].verts[j][1] = vert1 * Math.cos(angle) + vert0 * Math.sin(angle);
			}
			this.planes[i].updateVerts();
		}
	}
	
	changeColor(colors)
	{
		for(var i = 0; i < this.planes.length; i++)
		{
			this.planes[i].colors = [
				colors[0],colors[1],colors[2],
				colors[3],colors[4],colors[5],
				colors[6],colors[7],colors[8],
				colors[9],colors[10],colors[11]
			];
		}
	}
	
	draw()
	{
		for(var i = 0; i < this.planes.length; i++)
		{
			this.planes[i].draw();
		}
	}
}

var gl;
var canvas;
var scene = [];
function start()
{
	var audio = new Audio('NeonWaves.wav');
	audio.play();
	
	canvas = document.getElementById('glCanvas');
	gl = canvas.getContext('webgl');
	makeShader();
	
	for(var i = -22.0; i <= 22; i += 2.0)
	{
	for (var j = -22.0; j <= 22; j += 2.0)
	{
	var cube = new Cube(
		[i, j, 0.0],
		[1.0, 1.0, 1.0],
		[
			[1, 0, 0],
			[1, 0, 0],
			[1, 0, 0],
			[1, 0, 0]
		]
	);
	scene.push(cube);
	}
	}
	
	draw();
}

var deltaTime = 0;
var oldTime = 0;
var timer = 0;
var newColor = [
	1,0,0,
	1,0,0,
	1,0,0,
	1,0,0
];
var bonus = 1;
function update(time)
{
	deltaTime = time - oldTime;
	timer += deltaTime;
	oldTime = time;
	
	if(timer >= 18000 && bonus == 1)
	{
		bonus = 2;
	}
	if(timer >= 30000 && bonus == 2)
	{
		bonus = -3;
	}
	if(timer >= 42000 && bonus == -3)
	{
		bonus = 1;
	}
	if(timer >= 55000 && bonus == 1)
	{
		bonus = 0.2;
	}
	if(timer >= 60000 && bonus == 0.2)
	{
		gl.clearColor(0.0,0.0,0.0,1);
		gl.enable(gl.DEPTH_TEST); 
		gl.clear(gl.COLOR_BUFFER_BIT);
		gl.viewport(0,0,gl.drawingBufferWidth, gl.drawingBufferHeight);
		return;
	}
	
	for(var i = 0; i < scene.length; i++)
	{
		scene[i].moveTo([scene[i].pos[0],scene[i].pos[1], Math.sin(timer * 0.01745329252 + distance([0, 0, 0], scene[i].pos) * 0.5) * Math.min(100000, timer * bonus) * 0.0001]);
		console.log(timer);
		if(bonus >= 10) scene[i].rotateAllY(deltaTime * 0.012);
		scene[i].rotateAllZ(deltaTime * 0.001 * bonus * bonus);
		for(var j = 0; j < 12; j += 3)
		{
			newColor[j] = (Math.sin(timer * 0.5 * 0.01745329252 + distance([0,0,0], scene[i].pos) * 0.5) + 1) / 2.0;
			newColor[j+1] = (Math.sin(timer * 0.5 * 0.01745329252 + distance([0,0,0], scene[i].pos) * 0.5 + 2) + 1) / 2.0;
			newColor[j+2] = (Math.sin(timer * 0.5 * 0.01745329252 + distance([0,0,0], scene[i].pos) * 0.5 + 4) + 1) / 2.0;
		}
		scene[i].changeColor(newColor);
		
	}
	
	draw();
	
	requestAnimationFrame(update);
}
requestAnimationFrame(update);

var shaderProgram;
function makeShader()
{
	var vertCode =
		'attribute vec3 coordinates;' +
		'attribute vec3 color;' +
		'varying vec3 vColor;' +
		'void main(void) {' +
			' gl_Position = vec4(coordinates, 1.0);' +
			'vColor = color;' +
		'}';
	var vertShader = gl.createShader(gl.VERTEX_SHADER);
	gl.shaderSource(vertShader, vertCode);
	gl.compileShader(vertShader);
	var fragCode =
		'precision mediump float;' +
		'varying vec3 vColor;' +
		'void main(void) {' +
			'gl_FragColor = vec4(vColor, 1.);' +
		'}';
	var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
	gl.shaderSource(fragShader, fragCode);
	gl.compileShader(fragShader);
	shaderProgram = gl.createProgram();
	gl.attachShader(shaderProgram, vertShader);
	gl.attachShader(shaderProgram, fragShader);
	gl.linkProgram(shaderProgram);
	gl.useProgram(shaderProgram);
}

function draw()
{
	gl.clearColor(0.0,0.0,0.0,1);
	gl.enable(gl.DEPTH_TEST); 
	gl.clear(gl.COLOR_BUFFER_BIT);
	gl.viewport(0,0,gl.drawingBufferWidth, gl.drawingBufferHeight);
	
	for(var i = 0; i < scene.length; i++)
	{
		scene[i].draw();
	}
}

function vertsToGLpoints(verts)
{
	var newVerts = [];
	
	for(var i = 0; i < verts.length; i++)
	{
		newVerts[i] = gamepointToGLpoint(verts[i]);
	}
	return newVerts;
}

function gamepointToGLpoint(vector)
{
	var fov = 60;
	var cameraDistance = (gl.drawingBufferWidth / 2.0) / (Math.tan(fov * 0.01745329252 / 2.0));
	var cameraDistanceGamepoint = (cameraDistance / 100.0);
	
	var newVector = [];
	newVector[0] = (vector[0] * (100.0 / gl.drawingBufferWidth) / (cameraDistanceGamepoint + vector[2])) * cameraDistanceGamepoint;
	newVector[1] = (vector[1] * (100.0 / gl.drawingBufferHeight) / (cameraDistanceGamepoint + vector[2])) * cameraDistanceGamepoint;
	newVector[2] = vector[2] * (100.0 / cameraDistance);
	
	return newVector;
}

function distance(a, b)
{
	return Math.sqrt((a[0]-b[0]) * (a[0]-b[0]) + (a[1]-b[1]) * (a[1]-b[1]));
}