<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8"/>
    <title>3D Music sequencer</title>
    </head>
    <style>
      body {
        background-color: #111;
        font-family: sans-serif;
        color: #aaa;
        font-size: 1em;
        text-align: center;
      }
      #container {
        width: 100%;
		margin: 0 auto;
        margin-top: 1em;
		display: flex;
		justify-content: center;
      }
      canvas {
        background-color: #000;
		border-radius: 10px;
		box-shadow: 0 0 20px white;
      }
	  #instructions {
		margin: 0 auto;
		width: 200px;
		text-align: left;
		display: block;
		margin-top: 10px;
	  }
    </style>
  </head>
  <body>
    <div id="container">
      <canvas id="GameCanvas" width="800" height="600"></canvas>
    </div>
	<div id="instructions">
		Instructions:<br>
		<ul>
			<li><b>W/S/A/D</b>: Movement</li>
			<li><b>Mouse</b>: Look around</li>
			<li><b>Space</b>: Play/Pause</li>
			<li><b>Q/E</b>: Change Instrument</li>
		</ul>
	</div>
    <script>
		var canvas = document.getElementById('GameCanvas');
		var context = canvas.getContext('2d');
		
		var lastTime;
		function main() {
			var now = Date.now();
			var dt = (now - lastTime) / 1000.0;

			update(dt);
			render();

			lastTime = now;
			requestAnimFrame(main);
		};

		resources.load(['assets/sprites.png','assets/sprites.json','assets/instruments.json']);
		resources.onReady(init);

		var player;
		var keysDown = {};
		var sounds = [];
		var bgMusic;
		var instrumentIndex = 0;
		var instruments;

		function init() {
			reset();
			lastTime = Date.now();
			main();

			document.addEventListener('keydown', function(e) {
				keysDown[e.keyCode] = true;
			});

			document.addEventListener('keyup', function(e) {
				delete keysDown[e.keyCode];
			});

			//Load instruments
			instruments = JSON.parse(resources.get('assets/instruments.json'));
			bgMusic = new Audio('assets/bg.mp3');
			bgMusic.volume = 0.4;
			bgMusic.play();
			for (var i = 0; i < instruments.length; i++)
			{
				sounds[i] = new Audio('assets/' + instruments[i] + '.mp3');
				sounds[i].volume = 0.4;
			}
		}

		function update(dt) {
			updateEntities(dt);
			checkCollisions();
		}

		function updateEntities(dt) {
			player.update(dt);
		}

		function checkCollisions() {
			player.checkCollisions();
		}

		function render() {
			context.fillStyle = "#000";
			context.fillRect(0, 0, canvas.width, canvas.height);

			renderEntity(player);
			for (var i = 0; i < player.bullets.length; i++) {
				renderEntity(player.bullets[i]);
			}
		}

		function renderEntity(entity) {
			context.save();
			context.translate(entity.x, entity.y);
			context.scale(entity.scale, entity.scale);
			entity.sprite.render(context);
			context.restore();
		}

		function reset() {
			player = new Player();
		}

		// The browser API key obtained from the Google API Console.
		// Replace with your own browser API key, or your own key.
		var developerKey = 'AIzaSyCZJlH2vD3WlK_oVJ4yh1sHv8tWLhTKbS4';

		// The Client ID obtained from the Google API Console. Replace with your own Client ID.
		var clientId = "1013620148359-vb9m9d0jii2i2j6vjpgv58l1ebhc5b6a.apps.googleusercontent.com"

		// Scope to use to access user's photos.
		var scope = ['https://www.googleapis.com/auth/drive.readonly'];

		var pickerApiLoaded = false;
		var oauthToken;

		// Use the API Loader script to load google.picker and gapi.auth.
		function onApiLoad() {
			gapi.load('auth', {'callback': onAuthApiLoad});
			gapi.load('picker', {'callback': onPickerApiLoad});
		}

		function onAuthApiLoad() {
			window.gapi.auth.authorize(
				{
					'client_id': clientId,
					'scope': scope,
					'immediate': false
				},
				handleAuthResult);
		}

		function onPickerApiLoad() {
			pickerApiLoaded = true;
			createPicker();
		}

		function handleAuthResult(authResult) {
			if (authResult && !authResult.error) {
				oauthToken = authResult.access_token;
				createPicker();
			}
		}

		// Create and render a Picker object for searching images.
		function createPicker() {
			if (pickerApiLoaded && oauthToken) {
				var view = new google.picker.View(google.picker.ViewId.DOCS);
				view.setMimeTypes("audio/mpeg");
				var picker = new google.picker.PickerBuilder()
					.enableFeature(google.picker.Feature.NAV_HIDDEN)
					.enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
					.setAppId(clientId)
					.setOAuthToken(oauthToken)
					.addView(view)
					.addView(new google.picker.DocsUploadView())
					.setDeveloperKey(developerKey)
					.setCallback(pickerCallback)
					.build();
				picker.setVisible(true);
			}
		}

		// A simple callback implementation.
		function pickerCallback(data) {
			if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
				var fileId = data[google.picker.Response.DOCUMENTS][0]['id'];
				gapi.client.drive.files.get({
					'fileId': fileId
				}).then(function(resp) {
					bgMusic.pause();
					bgMusic = new Audio(resp.result.webContentLink);
					bgMusic.volume = 0.4;
					bgMusic.play();
					console.log('You have selected: ' + bgMusic.src);
				});
			}
		}

		//Pause/Play
		document.onkeydown = function(evt) {
			evt = evt || window.event;
			//Space
			if (evt.keyCode == 32) {
				if (bgMusic.paused)
					bgMusic.play();
				else
					bgMusic.pause();
			}
			//Q
			if (evt.keyCode == 81) {
				if (instrumentIndex > 0)
					instrumentIndex--;
				else
					instrumentIndex = 3;
				console.log('Instrument: ' + instruments[instrumentIndex]);
			}
			//E
			if (evt.keyCode == 69) {
				if (instrumentIndex < 3)
					instrumentIndex++;
				else
					instrumentIndex = 0;
				console.log('Instrument: ' + instruments[instrumentIndex]);
			}
		};

		//Sprite
		function Sprite(url, pos, size, speed, frames, dir, once) {
			this.pos = pos;
			this.size = size;
			this.speed = typeof speed === 'number' ? speed : 0;
			this.frames = frames;
			this._index = 0;
			this.url = url;
			this.dir = dir || 'horizontal';
			this.once = once;
		};

		Sprite.prototype = {
			update: function(dt) {
				this._index += this.speed*dt;
			},

			render: function(ctx) {
				var frame;

				if(this.speed > 0) {
					var max = this.frames.length;
					var idx = Math.floor(this._index);
					frame = this.frames[idx % max];

					if(this.once && idx >= max) {
						this.done = true;
						return;
					}
				}
				else {
					frame = 0;
				}


				var x = this.pos[0];
				var y = this.pos[1];

				if(this.dir == 'vertical') {
					y += frame * this.size[1];
				}
				else {
					x += frame * this.size[0];
				}

				ctx.drawImage(resources.get(this.url),
								x, y,
								this.size[0], this.size[1],
								0, 0,
								this.size[0], this.size[1]);
			}
		};

		//Bullet
		function Bullet(pos, dir) {
			this.pos = [pos[0], pos[1]];
			this.dir = dir;
			this.sprite = new Sprite('assets/sprites.png',[0,0],[32,32],6,[0,1], 'horizontal', true);
			this.speed = 500;
			this.done = false;
		}

		Bullet.prototype.update = function(dt) {
			this.pos[0] += this.dir[0] * this.speed * dt;
			this.pos[1] += this.dir[1] * this.speed * dt;
			this.sprite.update(dt);
		};

		Bullet.prototype.render = function(ctx) {
			ctx.save();

			ctx.translate(this.pos[0], this.pos[1]);
			if (this.dir[0] < 0)
				ctx.scale(-1,1);
			this.sprite.render(ctx);

			ctx.restore();
		};

		//Player
		function Player() {
			this.pos = [400, 300];
			this.size = [32, 32];
			this.speed = 200;
			this.bullets = [];
			this.cooldown = 0;
			this.scale = 1;
			this.sprite = new Sprite('assets/sprites.png', [0, 0], this.size, 0, [0], 'horizontal', false);
			this.dir = [0, 0];
			this.frame = 0;
			this.angle = 0;
			this.speed = 0;
			this.maxspeed = 200;
			this.accel = 500;
			this.friction = 12;
			this.padding = [20, 20];
			this.bounds = [
				[this.padding[0], canvas.width - this.padding[0]],
				[this.padding[1], canvas.height - this.padding[1]]
			];
			this.invuln = 0;
		}

		Player.prototype.update = function(dt) {
			this.invuln -= dt;
			this.frame += dt * 5;
			if (this.frame >= 4)
				this.frame = 0;
			this.sprite.pos = [this.frame * this.size[0], 0];
			if (this.invuln > 0)
				this.sprite.pos = [this.frame * this.size[0], this.size[1]];

			this.speed *= Math.pow(1 - this.friction, dt);

			if (37 in keysDown) { //left
				this.dir[0] = -1;
			}
			if (39 in keysDown) { //right
				this.dir[0] = 1;
			}
			if (38 in keysDown) { //up
				this.dir[1] = -1;
			}
			if (40 in keysDown) { //down
				this.dir[1] = 1;
			}
			if (32 in keysDown) { //space
				this.cooldown -= dt;
				if (this.cooldown <= 0) {
					this.cooldown = 0.3;
					this.shoot();
				}
			}
			if (this.dir[0] || this.dir[1]) {
				this.speed += this.accel * dt;
				if (this.speed > this.maxspeed)