var tourney;

var undefined;

function getWindowHeight () {
  if (window.innerHeight) 
    return window.innerHeight - 18;
  else if (document.documentElement && document.documentElement.clientHeight) 
    return document.documentElement.clientHeight;
  else if (document.body && document.body.clientHeight) 
    return document.body.clientHeight;
  return 640;
  };
  
function getWindowWidth () {
  if (window.innerWidth) 
    return window.innerWidth - 18;
  else if (document.documentElement && document.documentElement.clientWidth) 
    return document.documentElement.clientWidth;
  else if (document.body && document.body.clientWidth) 
    return document.body.clientWidth;
  return 640;
  };
  
// ScoreBoardPlayer

function ScoreBoardPlayer (sb, data, id) {
  this.sb = sb;
  this.id = id;
  this.data = data;
  this.current_x = 0;
  this.current_y = 0;
  this.delta_x = 0;
  this.delta_y = 0;
  this.target_x = 0;
  this.target_y = 0;
  this.max_speed_x = 100;
  this.max_speed_y = 100;
  this.visible = false;
  }

ScoreBoardPlayer.prototype.Accelerate = function(dir) {
  var range = this['target_'+dir] - this['current_'+dir];
  var speed = Math.abs(this['delta_'+dir]);
  if (speed != 0) {
    var stopping_distance = (speed+1) * (speed + 2) / 2;
    // overshot or about to overshoot
    if (compare(range,0) != compare(this['delta_'+dir],0) 
      || Math.abs(range) < stopping_distance) {
      if (this['delta_'+dir] != 0)
	this['delta_'+dir] -= this['delta_'+dir] / Math.abs(this['delta_'+dir]);
      }
    // not at maximum warp
    else if (range != 0 && speed < this['max_speed_'+dir]) {
      this['delta_'+dir] += range / Math.abs(range);
      }
    }
  // get moving
  else if (range != 0) {
    this['delta_'+dir] += range / Math.abs(range);
    }
  }

ScoreBoardPlayer.prototype.Render = function(lastp, outofthemoney, withcontainer) {
  p = this.data;
  var sb = this.sb;
  if (withcontainer) this.visible = false;
  var dp = sb.dp;
  var r0 = sb.r0;
  var r1 = sb.r1;
  var seed = dp.seeds[p.id-1];
  var html = '';
  var config = tourney.config;
  {
    var crank, lrank, spread;
    if (sb.is_capped) {
      crank = PlayerRoundCappedRank(p, r0);
      lrank = r0 > 0 ? PlayerRoundCappedRank(p, r0 - 1) : seed;
      spread = PlayerRoundCappedSpread(p, r0);
      }
    else {
      crank = PlayerRoundRank(p, r0);
      lrank = r0 > 0 ? PlayerRoundRank(p, r0 - 1) : seed;
      spread = PlayerRoundSpread(p, r0);
      }
    var is_in_money = InTheMoney(sb, p) ? ' money' : '';
    var is_out_of_money = outofthemoney ? ' nomoney' : '';
    var wins = PlayerRoundWins(p, r0);
    var losses = PlayerRoundLosses(p, r0);
    var is_block_leader = '';
    if (sb.first_rank == 1 && !defined(lastp)) {
//    html += '<div class="wlb'+is_in_money+is_out_of_money+'"><div class=w>P<br>R<br>I<br>Z<br>E</div></div>';
//    is_block_leader = 1;
      }
    else if (!is_in_money) {
      var lastw = defined(lastp) ? PlayerRoundWins(lastp, r0) : -1;
      if (lastw != wins 
	|| InTheMoney(sb, lastp)
	) {
//	html += '<div class="wlb'+is_in_money+is_out_of_money+'"><div class=w>' + UtilityFormatHTMLHalfInteger(wins).replace(/(\d|&frac12;)/g, '$&<br>').replace(/<br>$/,'') + '</div></div>';
//	is_block_leader = 1;
	}
      }
    if (is_block_leader) is_block_leader = " leader";
    if (withcontainer) html += '<div id="'+this.id+'" class="sbp'+is_in_money+is_out_of_money+is_block_leader+'" style="position:absolute;left:0;top:0;display:none">';
    html += '<table cellspacing=0 cellpadding=0><tr><td><div class=me>';
    html += "<div class=rank>"
      + UtilityOrdinal(crank)
      + "</div>";
    if (defined(PlayerScore(p, r0))) html += "<div class=old>was<br>" + UtilityOrdinal(lrank) + "</div>\n";
    if (sb.thai_points) html += "<div class=\"handicap\"><span class=label>HP</span><br><span class=value>" + (2*wins+((p.etc.handicap && p.etc.handicap[0])||0)) + "</span></div>\n";
    var currency_symbol = config.currency_symbol || '$';
    if (is_in_money) 
      html += "<div class=money>"+currency_symbol+"</div>";
    html += "<div class=wl>" + UtilityFormatHTMLHalfInteger(wins)
	+ "&ndash;"
	+ UtilityFormatHTMLHalfInteger (losses) 
	+ (config.no_scores ? '' : ' ' + UtilityFormatHTMLSignedInteger(spread))
      + "</div>";
  }
  html += FormatPlayerPhotoName(this,{'id':this.id+'_mi','show_id':1});
  if (config.rating_system != 'none') { 
    var oldr = p.rating
    var newr = PlayerNewRating(p, r0);
    var delta = UtilityFormatHTMLSignedInteger(newr-oldr);
    html += "<div class=newr>"+newr+"</div>\n";
    if (oldr) {
      html += "<div class=oldr>="+oldr+"<br>"+delta+"</div>\n";
      }
    else {
      html += "<div class=oldr>was<br>unrated</div>\n";
      }
  }
  html += "</div></td>\n"; // me
  html += "<td><div class=opp>\n"; // opp
  { // last game
    var last = '';
    var oppid = PlayerOpponentID(p, r0);
    if (oppid) {
      var op = PlayerOpponent(p, r0, dp);
      var opp = sb.pmap[op.id];
//    if (!opp) { console.log('cannot find '+op.id+' in pmap'); }
      var os = PlayerScore(op, r0);
      if (defined(os)) {
	var ms = PlayerScore(p, r0);
	if (config.no_scores) {
	  last += ms > os ? 'W' : ms == os ? 'T' : 'L';
	  }
	else {
	  var spread = UtilityFormatHTMLSignedInteger(ms - os);
	  last += "<div class=gs>"+ms+"&minus;"+os+"="+spread+"</div>";
	  }
	}
      else {
	last += "<div class=gsn>no score yet</div>";
	}
      var first = PlayerFirst(p, r0);
      first = first == 1 ? '1st' : first == 2 ? '2nd' : '';
      var board = RenderBoard(sb, PlayerBoard(p, r0));

      last += "<div class=where>"+first+board+" vs.</div>\n";
      last += "<div class=hs>" 
	+ FormatPlayerPhotoName(opp,{'id':this.id+'_li'}) 
	+ "</div>";
      }
    else if (r0 >= 0) { // bye
      var ms = PlayerScore(p,r0);
      ms = defined(ms) ?  ms >= 0 ? '+' + ms : ms : '';
      last += "<div class=bye>bye "+ms+"</div>";
      }
    if (last) {
      html += "<div class=last><div class=title>Last:</div>"+last+"</div>\n";
      }
  }
  { // next game
    var next = '';
    var oppid = PlayerOpponentID(p,r0+1);
    if (oppid) {
      var op = PlayerOpponent(p,r0+1, dp);
      var opp = sb.pmap[op.id];
      var first = PlayerFirst(p,r0+1);
      var repeats = PlayerCountRoundRepeats(p,op, r0+1);
      var board = RenderBoard(sb,PlayerBoard(p,r0+1));
      first = first == 1 ? '1st' : first == 2 ? '2nd' : '';
      if (repeats > 1) {
	next += "<div class=repeats>"
	  +(repeats==2?"repeat":repeats+"peat")
	  +"</div>";
	}
      next += "<div class=where>"+first+board+" vs.</div>\n";
      next += "<div class=hs>" 
	+FormatPlayerPhotoName(opp,{'id':this.id+'_ni'})
	+ "</div>";
      }
    else if (defined(oppid)) {
      next += "<div class=bye>bye</div>";
      }
    if (next) {
      html += "<div class=next><div class=title>Next:</div>"+next+"</div>\n";
      }
  }
  { // record
    var record = '';
    var nrounds = PlayerCountScores(p);
    if (nrounds > r1) nrounds = r1;
    if (nrounds > 1) {
      var maxw = 12;
      if (nrounds > maxw) {
	maxw = Math.floor((nrounds+maxw-1)/maxw);
	maxw = Math.floor((nrounds+maxw-1)/maxw); // doubled line, sic
	var ar0 = 0;
	record += '<div class=rdss>';
	while (ar0 < nrounds) {
	  var lastr0 = ar0 + maxw-1;
	  if (lastr0 > nrounds-1) lastr0 = nrounds-1;
	  record += '<div class=rds>';
	  for (var i0=ar0; i0<=lastr0; i0++) {
	    record += this.RenderRoundRecord(i0);
	    }
	  record += '</div>';
	  ar0 = lastr0 + 1;
	  }
	record += '</div>';
	}
      else {
	for (var ar0=0; ar0<nrounds; ar0++) {
          record += this.RenderRoundRecord(ar0);
	  }
	}
      }
    if (record) {
      html += "<div class=record><div class=title>Rec<br>ord</div>"+record+"</div>\n";
      }
    }
  html += '</div></td></tr></table>'; // opp
  if (withcontainer) html += '</div>'; // sbp
  return html;
  }

ScoreBoardPlayer.prototype.RenderRoundRecord = function (r0) {
  var record = ''; 
  var dp = this.sb.dp;

  var ms = this.data.scores[r0];
  record += "<div class=rd>";
  if (this.data.pairings[r0]) {
    var os = dp.players[this.data.pairings[r0]].scores[r0];
    if (defined(os)) {
      record += ms > os ? "<div class=win>W</div>"
	: ms < os ? "<div class=loss>L</div>"
	: "<div class=tie>T</div>";

      }
    else {
      record += "<div class=unknown>?</div>";
      }
    }
  else if (defined(ms)) { record += ms > 0 ? "<div class=bye>B</div>"
      : ms < 0 ? "<div class=forfeit>F</div>"
      : "<div class=missed>&ndash;</div>";
    }
  var p12 = PlayerFirst(p, r0);
  if (p12 && (""+p12).match(/^[12]$/)) {
    record += "<div class=p"+p12+">"+p12+"</div>";
    }
  else {
    record += "<div class=p0>&ndash;</div>";
    }
  record += "</div>";
  return record;
  }

ScoreBoardPlayer.prototype.Rerender = function (lastp, outofthemoney) {
  var ref = document.getElementById(this.id);
  ref.innerHTML = this.Render(lastp, outofthemoney, false);
  if (InTheMoney(this.sb, this.data)) ref.className = 'sbp money';
  else if (outofthemoney) ref.className = 'sbp nomoney';
  else ref.className = 'sbp';
  this.Resize();
  }

ScoreBoardPlayer.prototype.Resize = function () {
  var aspect = tourney.config.player_photo_aspect_ratio || 1;
  var ref = document.getElementById(this.id);
  if (!ref) {
    return;
    }
  ref.style.height = this.sb.cell_height + 'px';
  ref.style.width = this.sb.cell_width + 'px';
  this.SetPhotoSize('_mi',   this.sb.photo_size  , aspect * this.sb.photo_size  );
  this.SetPhotoSize('_mi_f', this.sb.photo_size/3, undefined);
  this.SetPhotoSize('_li',   this.sb.photo_size/2, aspect * this.sb.photo_size/2);
  this.SetPhotoSize('_li_f', this.sb.photo_size/6, undefined);
  this.SetPhotoSize('_ni',   this.sb.photo_size/2, aspect * this.sb.photo_size/2);
  this.SetPhotoSize('_ni_f', this.sb.photo_size/6, undefined);
  }

ScoreBoardPlayer.prototype.SetPhotoSize = function (subid, width, height) {
  var ref = document.getElementById(this.id + subid);
  if (ref) {
    if (width) ref.style.width = Math.round(width) + 'px';
    if (height) ref.style.height = Math.round(height) + 'px';
    }
  }

ScoreBoardPlayer.prototype.SetPosition = function (po, animated) {
  var sb = this.sb;
  var pi = po - sb.offset;
  if (pi < 0 || pi >= sb.rows * sb.columns) {
    this.SetVisible(false);
    return;
    }
  var i = pi % sb.columns;
  var j = Math.floor(pi/sb.columns);
  var ref = document.getElementById(this.id);
  if (animated) {
    this.target_x = i * sb.cell_hspacing;
    this.target_y = j * sb.cell_vspacing + 20;
//  console.log(po,pi,this.target_x,this.target_y,this.visible);
    }
  else {
    this.current_x = i * sb.cell_hspacing;
    this.current_y = j * sb.cell_vspacing + 20;
    this.delta_x = 0;
    this.delta_y = 0;
    }
  ref.style.left = this.current_x + 'px';
  ref.style.top = this.current_y + 'px';
  this.SetVisible(true);
  }

ScoreBoardPlayer.prototype.SetTarget = function (x, y) {
  this.target_x = x;
  this.target_y = y;
  }

ScoreBoardPlayer.prototype.SetVisible = function (bool) {
  if (bool == this.visible) return;
  this.visible = bool;
  var ref = document.getElementById(this.id);
  if (this.visible) {
    this.current_x = this.target_x;
    this.current_y = this.target_y;
    ref.style.left = this.current_x + 'px';
    ref.style.top = this.current_y + 'px';
    ref.style.display = 'block';
    }
  else {
    ref.style.display = 'none';
    }
  }

ScoreBoardPlayer.prototype.Tick = function () {
  var ref = document.getElementById(this.id);
  if (this.delta_x) {
    this.current_x += this.delta_x;
    ref.style.left = this.current_x + 'px';
    }
  if (this.delta_y) {
    this.current_y += this.delta_y;
    ref.style.top = this.current_y + 'px';
    }
  this.Accelerate('x');
  this.Accelerate('y');
  return this.delta_x || this.delta_y;
  }

// TSH::Command::ScoreBoard

var the_sb;
var thai_name_cache = {};

ScoreBoard.prototype.AdjustColumns = function(delta) {
  this.columns += delta;
  if (this.columns < 1) this.columns = 1;
  this.Resize();
  this.Update(true);
  }

ScoreBoard.prototype.AdjustOffset = function(delta) {
  this.offset += delta;
  if (this.offset < 0) this.offset = 0;
  this.Update(true);
//this.Resize();
//this.Render();
  }

ScoreBoard.prototype.AdjustRound = function(delta, no_fetch) {
  var prev_ref = document.getElementById('sbctl_previous_round');
  var next_ref = document.getElementById('sbctl_next_round');
  if (this.viewing_live) {
    if (delta < 0) {
      this.viewing_live = false;
      this.real_r1--;
      if (next_ref) next_ref.style.display = 'inline';
      }
    this.r1 = this.real_r1;
    this.r0 = this.r1 - 1;
    }
  else {
    this.r1 += delta;
    if (this.r1 < 0) this.r1 = 0;
    else if (this.r1 >= this.real_r1) {
      this.r1 = this.real_r1;
      this.viewing_live = true;
      if (next_ref) next_ref.style.display = 'none';
      }
    this.r0 = this.r1 - 1;
    }
  if (prev_ref) prev_ref.style.display = this.r1 > 0 ? 'inline' : 'none';
  if (!no_fetch) this.Fetch(true, false);
  }

ScoreBoard.prototype.AdjustRows = function(delta) {
  this.rows += delta;
  if (this.rows < 1) this.rows = 1;
  this.Resize();
  this.Update(true);
  }

ScoreBoard.prototype.AdjustScroll = function(delta) {
  this.scroll_rate += delta;
  if (this.scroll_rate < 0) this.scroll_rate = 0;
  this.scroll_count = this.scroll_rate;
  }

ScoreBoard.prototype.Fetch = function(is_update, no_fetch) {
  error('checking for updates');
  if (no_fetch) {
    error('not fetching');
    }
  else {
    var content = this.pfu.FetchCached();
    var newt;
    if (content) eval(content); else {
      error('null content returned');
      return false;
      }
    if (newt) tourney = newt; else {
      error('server reply did not contain tournament data');
      return false;
      }
    error('processing new data');
    }

  var dp = GetDivisionByName(this.dname);
  this.dp = dp;
  if (!dp) { alert('Cannot find division "'+this.dname+'".'); return; }
  DivisionSynch(dp, tourney);
  this.real_r1 = DivisionMostScores(dp);
  this.AdjustRound(0, true);
  var r0 = this.r0;
  this.has_classes = DivisionClasses(dp);
  this.c_no_boards = (tourney.config.no_boards || '');
  this.is_capped = tourney.config.standings_spread_cap;
  this.thai_points = tourney.config.thai_points;
  this.c_has_tables = DivisionHasTables(dp, tourney.config);

  // ComputeRanks, ComputeRatings, ComputeSeeds are done in saveJSON.pm
  var ps = DivisionPlayers(dp);
  PlayerSpliceInactive(ps, 0, 0);
  if (this.is_capped) {
    ps = PlayerSortByCappedStanding(r0, ps);
    }
  else {
//  console.log(r0);
    ps = PlayerSortByStanding(r0, ps);
//  for (var i=0; i<ps.length;i++) { var p = ps[i];
//    console.log([p.name, p.rwins[r0], p.rlosses[r0], p.rspread[r0]].join(', '));
//    }
    }
  if (is_update) {
    for (var po=0; po<ps.length; po++) {
      var p = ps[po];
      if (this.ps[po].data.id != p.id) {
	var j;
//	console.log('looking for '+p.id);
	for (j=po+1; j<ps.length; j++) {
//	  console.log('trying '+this.ps[j].data.id);
	  if (this.ps[j] && this.ps[j].data.id == p.id) {
	    this.ps.splice(po, 0, this.ps.splice(j,1)[0]);
//          console.log('now','ps',t1,'this.ps',t2);
	    break;
	    }
	  }
	if (j == ps.length) {
	  error("Player roster unexpectedly changed, cannot find "+p.name+" reloading.");
          this.StorePlayers(ps);
	  return true;
	  }
        }
      this.ps[po].data = p;
      var out_of_the_money = po + 1 >= dp.first_out_of_the_money;
      this.ps[po].Rerender(po ? ps[po-1] : undefined, out_of_the_money);
      }
//  var t1 =[];for (var i=0;i<ps.length;i++) { t1.push(ps[i].id); }
//  var t2 =[];for (var i=0;i<ps.length;i++) { t2.push(this.ps[i].data.id); }
//  console.log('ps',t1,'this.ps',t2);
    this.Update(true);
    }
  else 
    this.StorePlayers(ps);
  this.RerenderNote();
  this.pmap = []; // maps player id to SBP structure
  for (var i=0; i<this.ps.length; i++) {
    this.pmap[this.ps[i].data.id] = this.ps[i];
    }
  error('data loaded');
  return true;
  }

ScoreBoard.prototype.RerenderNote = function() {
  var note = '';
  var team_count = 0;
  for (var team in this.dp.team_wins) { team_count++; }
  if (team_count == 2) {
    note += '. Teams: ';
    for (var team in this.dp.team_wins) {
      note += ' ' + team + ' ' + this.dp.team_wins[team];
      }
    }
  var ref = document.getElementById('sbctl_note');
  if (ref) {
    ref.innerHTML = note;
    }
  }

function FormatPlayerPhotoName(pp, optionsp) {
  if (defined(pp)) {
    return HeadShot(pp, optionsp.id) + TagName(pp.sb, pp.data, optionsp);
    }
  else {
    return '<div class=nohead>p</div>';
    }
  }

function GetDivisionByName(dname) {
  for (var dnum = 0; dnum < tourney.divisions.length; dnum++) {
    if (tourney.divisions[dnum].name == dname) return tourney.divisions[dnum];
    }
  }

function HeadShot (pp, id) {
  var p = pp.data;
  var config = tourney.config;
  if (config.player_photos) {
    var s = '<div class=head><img class=head src="'
      + p.photo
      + '" alt="[head shot]" id="'
      + id
      + '">';
    if (config.scoreboard_teams && PlayerTeam(p)) {
      if (PlayerTeam(p).length == 1) {
	s += '<span class=team><span class=label>'+PlayerTeam(p).toUpperCase()+'</span></span>';
	}
      else {
	s += '<span class=team><img src="http://www.worldplayerschampionship.com/images/flags/'
	  + PlayerTeam(p).toLowerCase()
	  + '.gif" id="'+id+'_f" alt=""></span>';
        }
      }
    s += "</div>";
    return s;
    }
  else {
    return "&nbsp;";
    }
  }

function InTheMoney (sb, p) {
  var dp = sb.dp;
  var r0 = sb.r0;
  var config = tourney.config;
  var crank;
  if (sb.is_capped) {
    crank = PlayerRoundCappedRank(p, r0);
    }
  else {
    crank = PlayerRoundRank(p, r0);
    }
  var is_in_money = 0;
  var prize_bands = config.prize_bands;
  if (prize_bands) {
    var prize_band = prize_bands[dp.name];
    if (prize_band) {
      if (crank <= prize_band[prize_band.length-1]) {
	is_in_money = 1;
	}
      }
    }
  return is_in_money;
  }

function KeepLoadingScoreBoard(argh) {
  the_sb = new ScoreBoard(argh);
  the_sb.Render();
  the_sb.Resize();
  the_sb.AdjustOffset(0);
  window.onresize = function () { the_sb.Resize(); the_sb.Update(true); }
  var ticker = function () {
    if (!document.getElementById(the_sb.id)) return;
    window.setTimeout(ticker, 50);
    the_sb.Tick();
    }
  ticker();
  }

function ScoreBoard(argh) {
  this.pfu = new PoslFetchURL(argh.url);
  this.columns = argh.columns;
  this.dname = argh.dname;
  this.dp = undefined;
  this.id = argh.id;
  this.offset = argh.offset;
  this.r1 = 0;
  this.real_r1 = 0;
  this.reload_rate = 20 * (argh.refresh || 10);
  this.reload_count = 0;
  this.rows = argh.rows;
  this.scroll_count = 0;
  this.scroll_rate = 0;
  this.url = argh.url;
  this.viewing_live = true;
  this.Fetch(false, false);
  }

ScoreBoard.prototype.Render = function () {
//alert('SB.Render');
  var html = '';
  var ref = document.getElementById(this.id);
  if (!ref) { alert('Cannot find element "'+this.id+'".'); return; }
  html += '<table class=scoreboard><tr><td>';
  html += this.RenderTable();
  html += '</td></tr></table>';
  html += '<div class=sbctl>tsh scoreboard controls: <a href="#" onclick="the_sb.AdjustRows(-1);return false">fewer</a> <a href="#" onclick="the_sb.AdjustRows(1);return false">more</a> rows <a href="#" onclick="the_sb.AdjustColumns(-1);return false">fewer</a> <a href="#" onclick="the_sb.AdjustColumns(1);return false">more</a> columns <a href="#" onclick="the_sb.AdjustOffset(-1);return false">higher</a> <a href="#" onclick="the_sb.AdjustOffset(1);return false">lower</a> ranks <a href="#" onclick="the_sb.AdjustScroll(-1);return false">slower</a> <a href="#" onclick="the_sb.AdjustScroll(1);return false">faster</a> scroll <a href="#" onclick="the_sb.AdjustRound(-1, false);return false" id=sbctl_previous_round>previous</a> <a href="#" onclick="the_sb.AdjustRound(1, false);return false" id=sbctl_next_round style="display:none">next</a> round <a href="#" onclick="the_sb.reload_count = 0;the_sb.Fetch(true, false);return false">refresh now</a> <span id=error></span><span class=note id=sbctl_note>&nbsp;</span></div>';
  ref.innerHTML = html;
  }

function RenderBoard (sb, b) {
  var board = '';
  if (b) {
    if (sb.c_has_tables) {
      if (sb.c_has_tables) board = DivisionBoardTable(sb.dp, b, tourney.config);
      }
    else if (!sb.c_no_boards) {
      board = b;
      }
    if ((board+"").length) board = " \@"+board;
    }
  return board;
  }

ScoreBoard.prototype.RenderTable = function () {
  var dp = this.dp;
  var html = '';
  html += '<div id=sbs>';

  for (var po = 0; po < this.ps.length; po++) {
    var sbp = this.ps[po];
    var p = sbp.data;
    var out_of_the_money = po + 1 >= dp.first_out_of_the_money;
    html += sbp.Render(po ? this.ps[po-1].data : undefined, out_of_the_money, true);
    }
  html += '</div><br clear=all>';
  if (0) html += '\
<script language="JavaScript" type="text/javascript"><!--\
  function fix_sizes () {\
  var p = document.getElementById(\'sbs\').firstChild\
  var maxh, minh;\
  maxh = minh = p.offsetHeight;\
  while (p = p.nextSibling) {\
if ((p.className != \'sbp\' && p.className != \'sbp money\') { continue) // ; }\
    if (maxh < p.offsetHeight) { maxh = p.offsetHeight; }\
    if (minh > p.offsetHeight) { minh = p.offsetHeight; }\
    }\
  p = document.getElementById(\'sbs\').firstChild;\
  p.style.height = maxh;\
  while (p = p.nextSibling) {\
if ((p.className != \'sbp\' && p.className != \'sbp money\') { continue) // ; }\
    p.style.height = maxh;\
    }\
  }\
  setTimeout(\'fix_sizes()\', 1000);\
--></script>\
';
  return html;
  }

ScoreBoard.prototype.Resize = function () {
  var winHeight = getWindowHeight();
  var winWidth = getWindowWidth();
  this.cell_hspacing = Math.floor(winWidth / this.columns);
  this.cell_vspacing= Math.floor((winHeight-20) / this.rows);
  this.cell_width = this.cell_hspacing - 5;
  this.cell_height = this.cell_vspacing - 5;
  this.photo_size = Math.max(24, Math.min(Math.round(this.cell_width-112), this.cell_height-112));
  if (this.ps) {
    for (var i=0; i<this.ps.length;i++) {
      this.ps[i].Resize();
      }
    }
  }

ScoreBoard.prototype.StorePlayers = function (ps) {
  this.ps = [];
  for (var i=0; i<ps.length; i++) {
    this.ps.push(new ScoreBoardPlayer(this, ps[i], this.id + '_' +ps[i].id));
    }
  }

function TagName(sb, p, optionsp) {
  if (!optionsp) optionsp = {};
  var name = PlayerName(p);
  var is_chinese = PlayerTeam(p).match(/^(?:MYS|SGP)$/) && name.match(/ .* /);
  var is_thai = p.xthai;
  var rv;
  if (!defined(is_thai)) {
    if (name.match(/Charnwit$/))
      is_thai = 1;
    else if (PlayerTeam(p) == 'THA') {
      rv = name.match(/^(.*), (.*)$/);
      if (rv.length == 3) {
	if (thai_name_cache[rv[2]] && thai_name_cache[rv[2]] != name) {
	  is_thai = 0;
	  }
	else {
	  thai_name_cache[rv[2]] = name;
	  is_thai = 1;
	  }
	}
      else { is_thai = 0; }
      }
    else { is_thai = 0; }
    p.xthai = is_thai;
    }

  rv = name.match(/^(.*), (.*)$/);
  if (rv) {
    var given = rv[2];
    var surname = rv[1];
    if (is_thai) {
      surname = '';
      }
    else if (is_chinese) {
      if (name == 'Wee, Ming Hui Hubert') {
	given = 'Wee Ming';
	surname = 'Hui Hubert';
        }
      else {
	given = rv[1];
	surname = rv[2];
        }
      }
    rv = surname.match(/^(.*)-(.*)$/);
    if (rv) {
      var surname1 = rv[1];
      var surname2 = rv[2];
      if (surname2.length < surname1.length + given.length) {
	given = given+' '+surname1 + "-";
	surname = surname2;
	}
      }
    if (optionsp.show_id) {
      var classn = sb.has_classes ? '/' + PlayerClass(p) : '';
      if (surname.length > given.length) {
	given += " (#" + p.id + classn + ")";
	}
      else {
	surname += " (#" + p.id + classn + ")";
	}
      }
    name = "<div class=name>"
      + "<span class=given>" + given + "</span>"
      + "<span class=surname>" + surname + "</span>"
      + "</div>";
    }
  else { // school scrabble tagging
    var names = name.split(/\s+/);
    var split = 0;
    var half = names.join(' ').length/2;
    for (var i = 0; i < names.length; i++) {
      if (names.slice(0,i+1).join(' ').length > half) {
	if (Math.abs(names.slice(0,i+1).join(' ').length-half) > 
	  Math.abs(names.slice(0,i).join(' ').length-half)) {
	  split = i;
	  }
	else {
	  split = i+1;
	  }
	break;
        }
      }
    if (names.length> 1 && split == 0) { split = 1; }
    name = "<div class=name><span class=given>"
      + names.slice(0,split).join(' ')
      + "</span><span class=surname>"
      + names.slice(split).join(' ')
      + "</span></div>";
    }
  return name;
  }

// to be called every 0.05 s
ScoreBoard.prototype.Tick = function () {
  var active = 0;
  for (var i=0; i<this.ps.length; i++) {
    if (this.ps[i].Tick()) active = 1;
    }
  if (this.reload_rate) {
    this.reload_count++;
//  error(this.reload_count);
    if (0 == this.reload_count % 20) {
      error('...'+(this.reload_rate-this.reload_count)/20);
      }
    if (this.reload_count >= this.reload_rate) {
      this.reload_count = 0;
      this.Fetch(true, false);
      }
    }
  if ((!active) && this.scroll_rate) {
    if (this.scroll_count++ >= 40 / this.scroll_rate) {
      this.scroll_count = 0;
      if (this.offset + this.rows * this.columns >= this.ps.length) {
	this.offset = 0;
        this.AdjustOffset(0);
        }
      else {
        this.AdjustOffset(1);
        }
      }
    }
  }

ScoreBoard.prototype.Update = function(animated) {
//console.log('update');
  if (!defined(this.ps)) return;
  for (var i = 0; i < this.ps.length; i++) {
    this.ps[i].SetPosition(i, animated);
    }
  }

// TSH::Division

function DivisionClasses (dp, c) {
  var old = dp.classes;
  if (defined(c)) dp.classes = c;
  return old;
  }

// not currently used
//
// function DivisionComputeRanks(dp, sr0) {
//   var sorted = PlayerSortByStanding(sr0, DivisionPlayers(dp))
//   PlayerSpliceInactive(sorted, 1, sr0);
//   var lastw = -1;
//   var lastl = -1;
//   var lasts = 0;
//   var rank = 0;
//   for (var i=0; i<sorted.length; i++) {
//     var p = sorted[i];
//     var wins = PlayerRoundWins(p,sr0);
//     var losses = PlayerRoundLosses(p,sr0);
//     var spread = PlayerRoundSpread(p,sr0);
//     if (wins != lastw || spread != lasts || losses != lastl) {
//       lastw = wins;
//       lastl = losses;
//       lasts = spread;
//       rank = i+1;
//       }
//     PlayerRoundRank(p, sr0, rank);
//     }
//   }

function DivisionCountPlayers(dp) {
  return dp.players.length - 1;
  }

function DivisionMaxRound0(dp,round0) {
  var old = dp.maxr;
  if (defined(round0)) dp.maxr = round0;
  return old;
  }

function DivisionMostScores(dp) {
  return dp.maxs + 1;
  }

// TSH::Utility


