var SUDOKU = {
  Autotag: '<div type="sudoku" sudoku="_________________________________________________________________________________" cur="_________________________________________________________________________________"></div>',
  Size: 3,
  Level: 2,
  Gid: null,
  T0: 0,
  T_offset: 0,
  AsyncErr: null,
  BoardDiv: null,
  EtTagId: null,
  Clipboard: null,

  nv: function (inp, div_id){
	var elem = document.getElementById(div_id);
	var cur = elem.getAttribute('cur');
	var ini = elem.getAttribute('sudoku');
	var DD = ini.length;
	var D = (DD < 81 ? 4 : (DD < 256 ? 9 : 16));
	var nd_re = D=="9" ? /[^1-9x-z]/g : /[^\da-fA-Fx-z]/g;
	var v=inp.value.replace(nd_re, '');
	l=v.length;
	inp.style.fontSize = (l<=1 ? 24 : (l==2 ? 12 : 10));
	inp.value=v;
	if(l!=1) v = '_';
	var ij=D*parseInt(inp.name.substr(1,1))+parseInt(inp.name.substr(2,1));
	if(ij==0){
		cur = v+cur.substr(1);
	}else if(ij==(DD-1)){
		cur = cur.substring(0,DD-1)+v;
	}else{
		cur = cur.substring(0,ij)+v+cur.substr(ij+1);
	}
	elem.setAttribute('cur', cur);
	SUDOKU.update_pr(div_id, ini, cur);
	//SUDOKU.debug_mesg();
  },

  get_et: function (t0) {
	var ds = TIME.now()/1000 - t0 - SUDOKU.T_offset;
	var et = '';
	if (ds>86400) et = Math.floor(ds/86400)+'d ';
	if (ds>3600) et += Math.floor(ds%86400/3600)+'h ';
	if (ds>60) et += Math.floor(ds%3600/60)+"' ";
	et += Math.floor(ds%60)+'"';
	return et;
  },

  update_et: function () {
	DYN.ih(SUDOKU.EtTagId, SUDOKU.get_et(SUDOKU.T0));
	self.setTimeout('SUDOKU.update_et()', 1000);
  },

  count_: function (g){
	var l = g.length, c = 0;
	for(var i=0; i<l; i++){ if (g.substr(i,1) == '_') c++ }
	return c;
  },

  update_pr: function (div_id, ini, cur){
	var t_b=SUDOKU.count_(ini);
	DYN.ih(div_id+'_pr', (t_b-SUDOKU.count_(cur))+'/'+t_b);
  },

  render: function (div_id) {
	var elem = document.getElementById(div_id);
	var ini = elem.getAttribute('sudoku');
	var cur = elem.getAttribute('cur');
	var len = ini.length;
	var size = (len > 81) ? 4 : ((len > 16) ? 3 : 2);
	var D=size*size;
	var ih='<table align="center" border="1" cellpadding="0" cellspacing="0">';
	for (var i=0; i<D; i++) {
		ih+='<tr class="s">';
		for (var j=0; j<D ; j++) {
			var ij=D*i+j;
			var xi=ini.substr(ij,1);
			var xc=cur.substr(ij,1);
			var bg=SUDOKU.box_bg(i,j,size);
			if (xi=="_") {
				if (xc=="_") xc=""; else xc='value="'+xc+'"';
				ih+='<td class="c'+bg+'"><input type=text name="c'+i+j+'" onchange="SUDOKU.nv(this,\''+div_id+'\');" maxlength=4 class="a'+bg+'" '+xc+'/></td>';
			}else{
				ih+='<td class="c'+bg+'">'+xi+'</td>';
			}
		}
		ih+='</tr>';
	}
	ih+='</table><div align="center">Progress: <span id="'+ div_id +'_pr"></span> &nbsp; Time played: <span id="'+ div_id + '_et"></span> &nbsp; <input type=button onclick="SUDOKU.check(\''+div_id+'\')"value ="Check"/> &nbsp; <input type=button onclick="SUDOKU.dump(\''+div_id+'\')" value="Data"/></div><div id="'+div_id+'_cur" align="center"></div>';
	DYN.ih(div_id, ih);
	SUDOKU.update_pr(div_id, ini, cur);
	SUDOKU.EtTagId = div_id+'_et';
	if (SUDOKU.T0 == 0) {
		SUDOKU.T0 = TIME.now()/1000;
		SUDOKU.update_et();
	}
  },

  check: function(div_id) {
	var cur = document.getElementById(div_id).getAttribute('cur');
	var len = cur.length;
	var size = len < 81 ? 2 : len < 256 ? 3 : 4;
	var dd = size*size;
	var mesg = "";
	for(var i=0; i<len; i+=dd) {
		var block = cur.substr(i, dd);
		mesg += SUDOKU._check(block, 'Row '+(i/dd+1));
	}
	for(var i=0; i<dd; i++) {
		var block = "";
		for(var j=i; j<len; j+=dd) {
			block += cur.substr(j, 1);
		}
		mesg += SUDOKU._check(block, 'Column '+(i-0+1));
	}
	for(var m=0; m<dd; m+=size) {
		for(var n=0; n<dd; n+=size) {
			var block = "";
			for(var i=0; i<size; i++) {
				var j = (n-0+i)*dd -0 + m;
				block += cur.substr(j, size)
			}
			mesg += SUDOKU._check(block, 'Square '+(m/size+1)+', '+(n/size+1));
		}
	}
	var tag_id = div_id+'_cur';
	if (mesg.length > 0) {
		DYN.ih(tag_id, mesg);
	} else if (cur.indexOf('_') >= 0) {
		DYN.ih(tag_id, 'Ok so far.');
	} else {
		DYN.ih(tag_id, 'Solved');
	}
  },

  _check: function (str, where) {
	var mesg = "";
	for (var n=1; n<=9; n++) {
		if (str.indexOf(n) != str.lastIndexOf(n)) {
			mesg += 'Too many '+n+'. ';
		}
	}
	if (mesg.length > 0) {
		return where+": "+mesg+"<br/>";
	} else {
		return "";
	}
  },

  dump: function (div_id) {
	var elem = document.getElementById(div_id);
	var data = STR.prf('<div type=sudoku sudoku="%s" cur="%s"></div>', elem.getAttribute('sudoku'), elem.getAttribute('cur'));
	if (SUDOKU.Clipboard == null) {
		lt_re = new RegExp('<', 'g');
		DYN.ih(div_id+'_cur', data.replace(lt_re, '&lt;'));
	} else {
		SUDOKU.Clipboard.value += data;
	}
  },

  box_bg: function (i,j,size){
	var m=Math.floor(i/size)%2;
	var n=Math.floor(j/size)%2;
	if ((m==0&&n==0)||(m==1&&n==1))return(2);
	return(1);
  },

  get_game: function (gid){
	var url = 'rpc.perl?F=sudoku';
	if (gid) { url += '&g='+gid }
	ASYNC.get(url, SUDOKU.on_get_game, SUDOKU.AsyncErr);
  },

  on_get_game: function (xml){
	SUDOKU.Size  = DYN.xml_value(xml, 'size');
	SUDOKU.Level = DYN.xml_value(xml, 'level');
	SUDOKU.Gid   = DYN.xml_value(xml, 'gameid');
	var ini = DYN.xml_value(xml,'puzzle');
	var cur = DYN.xml_value(xml,'current');
	var t0  = DYN.xml_value(xml,'t0');
	var elem = document.getElementById(SUDOKU.BoardDiv);
	elem.setAttribute('sudoku', ini);
	elem.setAttribute('cur', cur);
	elem.setAttribute('t0', t0);
	SUDOKU.T0 = t0;
	SUDOKU.render(SUDOKU.BoardDiv);
	if (t0 > 0) {
		SUDOKU.update_et();
		DYN.ih('lh','Size '+SUDOKU.Size+' Level '+SUDOKU.Level);
	}
	SUDOKU.auto_save();
  },
 
  save_game: function (){
	var elem = document.getElementById(SUDOKU.BoardDiv);
	var cur = elem.getAttribute('cur');
	if(!cur || cur.length==0)return;
	ASYNC.post('rpc.perl', 'F=sudoku&save='+SUDOKU.Gid+'&c="'+cur+'"', SUDOKU.on_save_game, SUDOKU.AsyncErr);
  },

  on_save_game: function (xml){
	DYN.ih('info', DYN.xml_value(xml,'save'));
  },

  auto_save: function (){
	SUDOKU.save_game();
	self.setTimeout('SUDOKU.auto_save()',300000);
  },

  new_game: function (){
	SUDOKU.Level=document.getElementById('lv').value;
	SUDOKU.Size=document.getElementById('sz').value;
	ASYNC.post('rpc.perl', 'F=sudoku&new='+SUDOKU.Level+'&s='+SUDOKU.Size, SUDOKU.on_new_game, SUDOKU.AsyncErr);
  },

  on_new_game: function (xml){
	SUDOKU.T_offset=TIME.now()/1000-DYN.xml_value(xml,'t0');
	SUDOKU.on_get_game(xml);
	DYN.ih('info','New game created.'+(SUDOKU.Size==3?"":" Puzzles that are not 9x9 are experiemental only."));
  },

  debug_mesg: function (){
	DYN.ih("db1",'<p>Level='+SUDOKU.Level+':'+document.getElementById(SUDOKU.BoardDiv).getAttribute('cur')+'</p>');
  },

  list_unfinished: function (){
	ASYNC.get('rpc.perl?F=sudoku&list=unfinished', SUDOKU.on_list_unfinished, SUDOKU.AsyncErr);
  },

  on_list_unfinished: function(xml){
	var list='<h3>Unfinished games</h3><table><tr style="background:black;color:white"><td>Game</td><td>Size</td><td>Level</td><td>Progress</td><td align="right">Time</td></tr>';
	var i,c=DYN.xml_value(xml,'count');
	var games=xml.getElementsByTagName('game');
	for(i=0;i<c;i++){
		var o=games[i];
		var gid=DYN.xml_value(o,'gameid');
		if(gid==SUDOKU.Gid)continue;
		var size=DYN.xml_value(o,'size');
		var level=DYN.xml_value(o,'level');
		var e=SUDOKU.get_et(DYN.xml_value(o,'t0'));
		var pr=DYN.xml_value(o,'progress');
		list+='<tr><td><a href="#" onclick="SUDOKU.get_game('+gid+')">'+gid+'</a><td>'+size+'</td><td>'+level+'</td><td align="right">'+pr+'</td><td align="right">'+e+'</td></tr>';
	}
	list+='</table>';
	DYN.ih('info',list);
  },

  reset_game: function (){
	var elem = document.getElementById(SUDOKU.BoardDiv);
	elem.setAttribute('cur', elem.getAttribute('sudoku'));
	SUDOKU.render(SUDOKU.BoardDiv)
  },

  score: function (){
	ASYNC.get('rpc.perl?F=sudoku&list=finished', SUDOKU.on_score, SUDOKU.AsyncErr);
  },

  on_score: function (xml){
	var e=DYN.xml_value(xml,"sorry");
	if(e.length>0){DYN.ih("err",e);return}
	var x='<table><tr style="background:black;color:white"><td>Game</td><td>Size</td><td>Level</td><td>Blanks</td><td align="right">Time</td><td>Points</td></tr>';
	var i,c=DYN.xml_value(xml,'count');
	var games=xml.getElementsByTagName('game');
	for(i=0;i<c;i++){
		var o=games[i];
		var id=DYN.xml_value(o,'gameid');
		var s=DYN.xml_value(o,'size');
		var l=DYN.xml_value(o,'level');
		var b=DYN.xml_value(o,'blanks');
		var t=DYN.xml_value(o,'time');
		var sc=DYN.xml_value(o,'points');
		x+='<tr><td><a href="#" onclick="SUDOKU.get_game('+id+')">'+id+'</td><td>'+s+'</td><td>'+l+'</td><td>'+b+'</td><td align="right">'+t+'"</td><td align="right">'+sc+'</td></tr>';
	}
	x+='<tr><td/><td/><td/><td colspan=2><b>Total score</b></td><td align="right">'+DYN.xml_value(xml,"score")+'</table>';
	DYN.ih('info',x);
  },

  high_scores: function (){
	ASYNC.get('rpc.perl?F=sudoku&list=highscores', SUDOKU.on_high_scores, SUDOKU.AsyncErr);
  },

  on_high_scores: function (xml){
	var i,os=xml.getElementsByTagName('player');
	var x='<h3>Top Scores</h3><table><tr style="background:black;color:white"><td>Player</td><td align="right" width=50> Score </td><td> Puzzles solved</td></tr>';
	for(i=0;i<os.length;i++){
		var o=os[i];
		var uid=DYN.xml_value(o,'uid'),n=DYN.xml_value(o,'name');
		var s=DYN.xml_value(o,'score'),c=DYN.xml_value(o,'games');
		x+='<tr><td>'+n+'</td><td align="right">'+s+'</td><td align="right">'+c+'</td></tr>';
	}
	x+='</table>';
	DYN.ih('info',x);
  },

  help: function (){
	DYN.ih('info','Hint: you can place up to 4 digits in one cell.  Cells with more than one digit will not be saved to the server.');
  }
};
