<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Multichain</title>
<script src="/socket.io/socket.io.js"></script>
<script src="/common.js"></script>
<script>
"use strict";

var COLOURS=["#00F","#F00","#0CC"];

var socket=io(),me=[-1,""],userlist=[],rooms={},visibleroomuidiv=null;
socket.on("disconnect",function(){
	location.reload();
});
socket.on("message",function(msg){
	alert("Message:\n\n"+msg);
});
socket.on("me",updateme);
socket.on("userlist",updateuserlist);
socket.on("room",joinroom);
socket.on("roomdestroy",roomdestroy);
socket.on("roominvite",roominvite);
socket.on("roomjoin",roomadduser);
socket.on("roomchat",roomchat);
socket.on("roomcreategame",roomcreategame)
socket.on("roomgameturn",roomgameturn);
socket.on("roomgameplace",roomgameplace);
socket.on("roomgamewin",roomgamewin);

if(location.hostname=="localhost"&&Math.random()<.25)localStorage.setItem("nickname","aaa");

if(localStorage.getItem("nickname"))socket.emit("setnick",localStorage.getItem("nickname"));
else socket.emit("me",updateme);
socket.emit("userlist",updateuserlist);

function updateme(_me){
	me=_me;
	document.getElementById("nickname").innerHTML=me[1];
	localStorage.setItem("nickname",me[1]);
}

function updateuserlist(_ul){
	userlist=_ul;
	var elem=document.getElementById("userlist"),l=elem.children;
	var i,div;
	for(i=l.length-1;i>=0;i--)elem.removeChild(l[i]);
	var input=document.getElementById("roomuserinput"),selectedlist=input.value.trim().split(/\s+/g);
	for(i=0;i<userlist.length;i++){
		if(userlist[i][1]==me[1]){
			userlist.splice(i,1);
			i--;
			continue;
		}
		div=document.createElement("div");
		div.setAttribute("userid",userlist[i][0]);
		div.setAttribute("username",userlist[i][1]);
		div.innerHTML=userlist[i][1];
		div.addEventListener("click",function(ev){
			var usernameattr=ev.target.getAttribute("username");
			if(ev.target.classList.contains("selected")){
				ev.target.classList.remove("selected");
				input.value=input.value.trim().split(/\s+/g).filter(function(n){return n!=usernameattr;}).join(" ");
			} else {
				ev.target.classList.add("selected");
				input.value=(input.value.trim()+" "+usernameattr).trim();
			}
		});
		if(selectedlist.indexOf(userlist[i][1])!=-1)div.classList.add("selected");
		elem.appendChild(div);
	}
	input.value=selectedlist.filter(function(n){
		var i;
		for(i=0;i<userlist.length;i++)if(userlist[i][1]==n)return true;
		return false;
	}).join(" ");
}

function joinroom(roomid,users){
	rooms[roomid]=new Room(roomid);
	users.forEach(function(user){
		rooms[roomid].join(user);
	});
}

function roomadduser(roomid,user){
	rooms[roomid].join(user);
}

function Room(_roomid){
	if(!(this instanceof Room))return new Room(_roomid)
	this.id=_roomid;
	this.userlist=[];
	this.bd=null;
	this.onturn=0;
	this.cellsz=40;

	this.canplace=false;
	this.lastplacepos=-1;

	this.listdiv=document.createElement("div");
	this.listdiv.classList.add("room");
	this.listdiv.innerHTML="<div style='float:right;cursor:default' onclick='dodestroyroom(\""+this.id+"\")'>X</div><b>Room:</b>";
	this.listdiv.addEventListener("click",function(ev){
		if(ev.target!=this.listdiv)return;
		this.becomeVisible();
	}.bind(this));
	document.getElementById("roomlistcontainer").appendChild(this.listdiv);

	var e,table,tbody,input;
	this.uidiv=document.createElement("div");
	this.uidiv.classList.add("roomui");
	this.uidiv.classList.add("invisible");

	e=document.createElement("div");
	e.classList.add("uiclosebtn");
	e.innerHTML="x";
	e.addEventListener("click",function(){
		this.uidiv.classList.add("invisible");
		visibleroomuidiv=null;
		document.getElementById("roomuisplash").classList.add("invisible");
	}.bind(this));
	this.uidiv.appendChild(e);

	e=document.createElement("div");
	e.classList.add("chatdiv");
	e.addEventListener("click",function(){
		this.chatinput.focus();
	}.bind(this));
	table=document.createElement("table");
	table.classList.add("chatlogtable");
	tbody=document.createElement("tbody");
	tbody.classList.add("chatlogtbody");
	table.appendChild(tbody);
	e.appendChild(table);
	this.chatinput=document.createElement("input");
	this.chatinput.type="text";
	this.chatinput.classList.add("chatinput");
	this.chatinput.setAttribute("placeholder","Type something...");
	this.chatinput.addEventListener("keypress",function(ev){
		if(ev.keyCode!=13&&ev.which!=13)return;
		var msg=ev.target.value;
		ev.target.value="";
		if(msg.length==0)return;
		socket.emit("roomchat",this.id,msg);
	}.bind(this));
	e.appendChild(this.chatinput);
	this.uidiv.appendChild(e);

	this.gamediv=document.createElement("div");
	this.gamediv.classList.add("gamediv");
	this.gamediv.classList.add("invisible"); //no game running yet
	this.cvs=document.createElement("canvas");
	this.ctx=this.cvs.getContext("2d");
	this.cvs.classList.add("gamecvs");
	this.cvs.width=this.cellsz*W+1;
	this.cvs.height=this.cellsz*H+1;
	this.cvs.addEventListener("click",function(ev){
		this.chatinput.focus();
		if(!this.canplace)return;
		if(ev.target!=this.cvs)return;
		var bbox=ev.target.getBoundingClientRect();
		var x=~~((ev.clientX-bbox.left)/this.cellsz),
		    y=~~((ev.clientY-bbox.top)/this.cellsz),
		    idx=W*y+x;
		if(x<0||y<0||x>=W||y>=H)return;
		socket.emit("roomgameplace",this.id,idx);
	}.bind(this));
	//drawboard(this.ctx,this.cellsz,this.bd,0,this.lastplacepos);
	this.gamediv.appendChild(this.cvs);
	this.uidiv.appendChild(this.gamediv);

	this.newgamebtn=document.createElement("input");
	this.newgamebtn.type="button";
	this.newgamebtn.value="Start new game!";
	this.newgamebtn.addEventListener("click",function(){
		socket.emit("roomcreategame",this.id); //just a request
	}.bind(this));
	this.uidiv.appendChild(this.newgamebtn);

	this.statusdiv=document.createElement("div");
	this.statusdiv.classList.add("gamestatusdiv");
	this.uidiv.appendChild(this.statusdiv);

	document.body.appendChild(this.uidiv);

	setTimeout(this.becomeVisible.bind(this),1);
}
Room.prototype.becomeVisible=function(){
	if(visibleroomuidiv)visibleroomuidiv.classList.add("invisible");
	else document.getElementById("roomuisplash").classList.remove("invisible");
	visibleroomuidiv=this.uidiv;
	this.uidiv.classList.remove("invisible");
	this.chatinput.focus();
};
Room.prototype.join=function(user){
	this.listdiv.innerHTML+=" "+user[1];
	this.userlist.push(user.slice());
};
Room.prototype.destroy=function(){
	if(visibleroomuidiv==this.uidiv){
		visibleroomuidiv=null;
		document.getElementById("roomuisplash").classList.add("invisible");
	}
	this.uidiv.parentElement.removeChild(this.uidiv);
	this.listdiv.parentElement.removeChild(this.listdiv);
};
Room.prototype.chat=function(by,msg,isme){
	var tr,td,div;
	tr=document.createElement("tr");
	td=document.createElement("td");
	div=document.createElement("div");
	div.classList.add("chatitem");
	div.innerHTML="<i>"+by[1]+"</i>: "+msg;
	td.appendChild(div);
	tr.appendChild(td);
	this.uidiv.getElementsByClassName("chatlogtbody")[0].appendChild(tr);
	tr.scrollIntoView();
	if(!isme)sounddong();
};
Room.prototype.creategame=function(){
	this.newgamebtn.classList.add("invisible");
	this.gamediv.classList.remove("invisible");
	this.bd=emptyboard();
	drawboard(this.ctx,this.cellsz,this.bd,0,this.lastplacepos);
	this.statusdiv.innerHTML="Waiting for player 1...";
};
Room.prototype.myturn=function(ot){
	this.canplace=true;
	this.onturn=ot;
	this.statusdiv.innerHTML="Your turn!";
};
Room.prototype.place=function(pos,pidx,isme){
	//console.log("place: onturn "+this.onturn+" -> "+pidx);
	this.onturn=pidx;
	this.canplace=false;
	this.lastplacepos=pos;
	queueapplymove(this.id,this.ctx,this.cellsz,pos,pidx,this.lastplacepos);
	queueapplymove(this.id,function(){
		//console.log("place qam_func: onturn "+this.onturn+" -> "+(this.onturn+1)%this.userlist.length);
		if(!this.canplace){
			this.onturn=(this.onturn+1)%this.userlist.length;
			this.statusdiv.innerHTML="Waiting for player "+(this.onturn+1)+"...";
		}
		drawboard(this.ctx,this.cellsz,this.bd,this.onturn,this.lastplacepos);
	}.bind(this));
	if(!isme)sounddong();
};
Room.prototype.gotwin=function(pidx){
	queueapplymove(this.id,function(){
		this.onturn=pidx;
		this.canplace=false;
		drawboard(this.ctx,this.cellsz,this.bd,this.onturn,this.lastplacepos);
		this.statusdiv.innerHTML="Player "+(this.onturn+1)+" won!";
		this.newgamebtn.classList.remove("invisible");
	}.bind(this));
};

function roominvite(roomid,by){
	if(!confirm("User '"+by[1]+"' sent you an invite to join their room. Accept?")){
		socket.emit("inviteaccept",roomid,true);
		return;
	}
	socket.emit("inviteaccept",roomid);
}

function roomdestroy(roomid){
	rooms[roomid].destroy();
	delete rooms[roomid];
}

function roomchat(roomid,by,msg,isme){
	rooms[roomid].chat(by,msg,isme);
}

function roomcreategame(roomid){
	rooms[roomid].creategame();
}

function roomgameturn(roomid,ot){
	rooms[roomid].myturn(ot);
}

function roomgameplace(roomid,pos,pidx,isme){
	rooms[roomid].place(pos,pidx,isme);
}

function roomgamewin(roomid,pidx){
	rooms[roomid].gotwin(pidx);
}


function changenick(){
	var newnick=prompt("New nickname?");
	if(!newnick)return;
	if(!validatenick(newnick)){
		alert("That's an invalid nick! (regex [a-zA-Z0-9_-]{3,})");
		return;
	}
	socket.emit("setnick",newnick);
}

function createroom(){
	var users=document.getElementById("roomuserinput").value.trim().split(/\s+/g).map(function(n){
		var i;
		for(i=0;i<userlist.length;i++)if(userlist[i][1]==n)return userlist[i][0];
		return -1;
	}).filter(function(id){return id!=-1;});
	socket.emit("createroom",users);
}

function dodestroyroom(roomid){
	socket.emit("roomdestroy",roomid);
}

function drawboard(ctx,cellsz,bd,clr,lastplacepos){
	var x,y,i,angle,radius;
	ctx.clearRect(0,0,W*cellsz+1,H*cellsz+1);
	ctx.strokeStyle=COLOURS[+clr];
	ctx.beginPath();
	for(y=0;y<H;y++){
		for(x=0;x<W;x++){
			ctx.moveTo(x*cellsz+.5,y*cellsz+.5);
			ctx.lineTo((x+1)*cellsz+.5,y*cellsz+.5);
			ctx.lineTo((x+1)*cellsz+.5,(y+1)*cellsz+.5);
			ctx.lineTo(x*cellsz+.5,(y+1)*cellsz+.5);
			ctx.lineTo(x*cellsz+.5,y*cellsz+.5);
		}
	}
	ctx.stroke();
	if(lastplacepos!=-1){
		x=lastplacepos%W;
		y=~~(lastplacepos/W);
		ctx.fillStyle="rgba(0,0,0,0.2)";
		ctx.fillRect(x*cellsz,y*cellsz,cellsz,cellsz);
	}
	for(y=0;y<H;y++){
		for(x=0;x<W;x++){
			ctx.fillStyle=COLOURS[bd[y][x].c];
			angle=1/bd[y][x].n*2*Math.PI;
			radius=bd[y][x].n==1?0:.15;
			for(i=0;i<bd[y][x].n;i++){
				ctx.beginPath();
				ctx.arc(
					(x+.5+radius*Math.cos(angle*i))*cellsz,
					(y+.5+radius*Math.sin(angle*i))*cellsz,
					cellsz*.2,0,2*Math.PI,1);
				ctx.fill();
			}
		}
	}
}

function stabiliseanims(bd){
	var anims=[],stage;
	var newbd;
	var x,y,nnei,quo;
	do {
		stage=[];
		newbd=bdcopy(bd);
		for(y=0;y<H;y++){
			for(x=0;x<W;x++){
				nnei=(y>0)+(x>0)+(y<H-1)+(x<W-1);
				if(bd[y][x].n>=nnei){
					quo=~~(bd[y][x].n/nnei);
					newbd[y][x].n-=quo*nnei;
					if(y>0)  {
						newbd[y-1][x].n+=quo; newbd[y-1][x].c=bd[y][x].c;
						stage.push([W*y+x,W*(y-1)+x]);
					}
					if(x>0)  {
						newbd[y][x-1].n+=quo; newbd[y][x-1].c=bd[y][x].c;
						stage.push([W*y+x,W*y+x-1]);
					}
					if(y<H-1){
						newbd[y+1][x].n+=quo; newbd[y+1][x].c=bd[y][x].c;
						stage.push([W*y+x,W*(y+1)+x]);
					}
					if(x<W-1){
						newbd[y][x+1].n+=quo; newbd[y][x+1].c=bd[y][x].c;
						stage.push([W*y+x,W*y+x+1]);
					}
				}
			}
		}
		bd=newbd;
		anims.push([stage,bdcopy(bd)]);
		if(checkwin(bd)!=-1)break;
	} while(stage.length);
	return anims;
}

var applymove_queue={}; //objects of room ids
var applymove_busy={};

function applymove(ctx,cellsz,idx,c,lastplacepos,aqid){
	var bd=rooms[aqid].bd,onturn=rooms[aqid].onturn;
	applymove_busy[aqid]=true;
	bd[~~(idx/W)][idx%W].n++;
	bd[~~(idx/W)][idx%W].c=c;
	drawboard(ctx,cellsz,bd,onturn,lastplacepos);
	var anims=stabiliseanims(bd);
	console.log(anims);
	anims.forEach(function(pair,i){
		var stage=pair[0],newbd=pair[1];
		var finalstage=i==anims.length-1;
		setTimeout(function(){
			console.log("anim stage",stage);
			var time=0;
			var interval=setInterval(function(){
				var i,fx,fy,tx,ty;
				for(i=0;i<stage.length;i++){
					fx=stage[i][0]%W; fy=~~(stage[i][0]/W);
					bd[fy][fx].n--;
				}
				drawboard(ctx,cellsz,bd,onturn,lastplacepos);
				for(i=0;i<stage.length;i++){
					fx=stage[i][0]%W; fy=~~(stage[i][0]/W);
					tx=stage[i][1]%W; ty=~~(stage[i][1]/W);
					ctx.fillStyle=COLOURS[bd[fy][fx].c];
					ctx.beginPath();
					ctx.arc(
						(fx*(1-time/10)+tx*time/10+.5)*cellsz,
						(fy*(1-time/10)+ty*time/10+.5)*cellsz,
						cellsz*.2,0,2*Math.PI,1);
					ctx.fill();
				}
				if(time==10){
					clearInterval(interval);
					bd=rooms[aqid].bd=newbd;
					drawboard(ctx,cellsz,bd,onturn,lastplacepos);
					if(finalstage){
						applymove_busy[aqid]=false;
						while(applymove_queue[aqid].length&&typeof applymove_queue[aqid][0]=="function"){
							applymove_queue[aqid][0]();
							applymove_queue[aqid].shift();
						}
						if(applymove_queue[aqid].length){
							setTimeout(function(){
								applymove.apply(null,applymove_queue[aqid][0]);
								applymove_queue[aqid].shift();
							},50);
						}
					}
				}
				time++;
			},20);
		},350*i+1);
	});
}

function queueapplymove(id,ctx,cellsz,idx,c,lastplacepos){
	if(!applymove_queue[id])applymove_queue[id]=[];
	if(!applymove_busy[id])applymove_busy[id]=false;
	if(applymove_busy[id]){
		if(typeof ctx=="function")applymove_queue[id].push(ctx);
		else applymove_queue[id].push([ctx,cellsz,idx,c,lastplacepos,id]);
	} else {
		if(typeof ctx=="function")ctx();
		else applymove(ctx,cellsz,idx,c,lastplacepos,id);
	}
}


var sounddong__audioelem=null;
function sounddong(){
	if(sounddong__audioelem==null)sounddong__audioelem=document.getElementById("dongaudioelem");
	sounddong__audioelem.currentTime=0;
	sounddong__audioelem.play();
}
</script>
<style>
body{
	background-color:#fff;
	margin:0;
	font-family:Monaco,Menlo,"Courier New",Monospace;
	font-size:14px;
}
header{
	background-color:#fee;
}
header h1{
	margin-top:0;
	margin-bottom:0;
	padding:10px;
	padding-left:20px;
	width:350px;
}
div#headerbottom{
	height:10px;
	background: #fee;
	background: -moz-linear-gradient(top, #fee 0%, #fff 100%);
	background: -webkit-gradient(left top, left bottom, color-stop(0%, #fee), color-stop(100%, #fff));
	background: -webkit-linear-gradient(top, #fee 0%, #fff 100%);
	background: -o-linear-gradient(top, #fee 0%, #fff 100%);
	background: -ms-linear-gradient(top, #fee 0%, #fff 100%);
	background: linear-gradient(to bottom, #fee 0%, #fff 100%);
}

div#nickbar{
	float:right;
	margin-top:15px;
	font-size:10px;
}
span#nickname{
	font-weight:bold;
}


div#body{
	margin:5px;
}

div#userlistcontainer{
	width:190px;
}
div#userlist{
	width:150px;
	height:200px;
	border:1px #eee solid;
	overflow-y:scroll;
	-moz-user-select:none;
	-webkit-user-select:none;
	-ms-user-select:none;
}
div#userlist > div{
	cursor:pointer;
	padding:1px;
}
div#userlist > div:hover{
	background-color:#edd;
}
div#userlist > div.selected{
	background-color:#dcc;
	border:1px #abb solid;
	padding:0;
}

input#roomuserinput{
	width:180px;
}


div.room{
	background-color:#edd;
	-webkit-box-shadow:0px 0px 2px 3px #edd;
	-moz-box-shadow:0px 0px 2px 3px #edd;
	box-shadow:0px 0px 2px 3px #edd;
	padding:10px;
	margin:10px;
	width:300px;
	cursor:pointer;
}

div.roomui{
	position:absolute;
	top:50%;
	margin-top:-300px;
	left:50%;
	margin-left:-350px;
	width:700px;
	height:600px;
	padding:10px;
	background-color:#edd;
	border-radius:5px;
}

div.uiclosebtn{
	float:right;
	margin:5px;
	margin-left:10px;
	cursor:pointer;
}

div.chatdiv{
	float:right;
	width:300px;
	height:600px;
}
table.chatlogtable{
	display:block;
	width:100%;
	height:560px;
	border:1px black solid;
	overflow-y:scroll;
}
div.chatitem{
	width:300px;
	word-wrap:break-word;
}
input.chatinput{
	width:300px;
}

div.gamestatusdiv{
	font-style:italic;
	font-weight:bold;
	font-size:12px;
}


.invisible{
	display:none;
}


div#roomuisplash{
	position:absolute;
	left:0;
	top:0;
	width:100%;
	height:100%;
	background-color:rgba(0,0,0,0.4);
}
</style>
</head>
<body>
<div id="roomuisplash" class="invisible" onclick="visibleroomuidiv.classList.add('invisible');visibleroomuidiv=null;event.target.classList.add('invisible')"></div>
<header>
	<div id="nickbar">Nickname: <span id="nickname"></span> <input type="button" onclick="changenick()" value="Change"></div>
	<h1>Multichain</h1>
	<div id="headerbottom"></div>
</header>
<div id="body">
	<div id="createroomcontainer" style="float:right">
		<b>Online users:</b>
		<div id="userlist"></div>
		<input type="text" id="roomuserinput" disabled> <br>
		<input type="button" onclick="createroom()" value="Create room">
	</div>
	<div id="roomlistcontainer"></div>
</div>

<audio id="dongaudioelem" preload="auto">
	<source src="dong.ogg">
	<source src="dong.mp3">
</audio>
</body>
</html>