function $(x){return document.getElementById(x)}

var WIDTH=32,HEIGHT=32;
var MAP_MAGIC = 0x032f24c2;

function rot(x,n) { return (x<<n) | (x>>(32-n)) & 0xffffffff; }
function hash(a,b)
{
  var c=MAP_MAGIC;
  a += 0xb2ae9013;
  b += 0x2cd8e04a;
  c ^= b; c -= rot(b,14);
  a ^= c; a -= rot(c,11);
  b ^= a; b -= rot(a,25);
  c ^= b; c -= rot(b,16);
  a ^= c; a -= rot(c,4);
  b ^= a; b -= rot(a,14);
  c ^= b; c -= rot(b,24);

  return (c&65535)/65535;
}

var tile_lut = [];
function build_tile_lut()
{
  var i;
  var tile_lut_r = {};
  var N=0;
  for(i=0;i<256;i++) {
    var j=i;
    // edges override corners
    if(j&1) { j|=0x10; j|=0x20; }
    if(j&2) { j|=0x10; j|=0x40; }
    if(j&4) { j|=0x20; j|=0x80; }
    if(j&8) { j|=0x40; j|=0x80; }
    if(!tile_lut_r[j]) { 
      tile_lut_r[j] = N;
      tile_lut[i]=N;
      N++;
    } else {
      tile_lut[i] = tile_lut_r[j];
    }
  } 
}


function mapinterp(map, x,y, u,s,x0,y0)
{
  map[x+y*33] = u+s*(hash(x+x0,y+y0)-0.5)*0.01;
}

var SQRT2 = Math.sqrt(2);

function clamp(tile,x,y)
{
  tile[x+y*33] = Math.max(0,Math.min(6.99,tile[x+y*33]*7));
}

function gen_subdivide(tile,x0,y0,scale,x,y)
{
  if(scale <= 1) { return; }
  var hscale = scale>>1;
  var cx = x0+hscale,
      cy = y0+hscale;

  // generate five points
  // a 1 b
  // 3 5 4
  // c 2 d
  var a = tile[x0+y0*33];
  var b = tile[(x0+scale)+y0*33];
  var c = tile[x0+(y0+scale)*33];
  var d = tile[(x0+scale)+(y0+scale)*33];
  mapinterp(tile, x0+hscale,y0, (a+b)/2,scale, x,y);
  mapinterp(tile, x0+hscale,y0+scale, (c+d)/2,scale, x,y);
  mapinterp(tile, x0,y0+hscale, (a+c)/2,scale, x,y);
  mapinterp(tile, x0+scale,y0+hscale, (b+d)/2,scale, x,y);
  mapinterp(tile, x0+hscale,y0+hscale, (a+b+c+d)/4,SQRT2*scale, x,y);
  gen_subdivide(tile, x0,y0,hscale, x,y);
  gen_subdivide(tile, x0+hscale,y0,hscale, x,y);
  gen_subdivide(tile, x0,y0+hscale,hscale, x,y);
  gen_subdivide(tile, x0+hscale,y0+hscale,hscale, x,y);
  // normalize and clamp
  clamp(tile, x0+hscale,y0);
  clamp(tile, x0+hscale,y0+scale);
  clamp(tile, x0,y0+hscale);
  clamp(tile, x0+scale,y0+hscale);
  clamp(tile, x0+hscale,y0+hscale);
}

function gen_tile(tile,x,y)
{
  tile[0] = hash(x,y);
  tile[32] = hash(x+32,y);
  tile[32*33] = hash(x,32+y);
  tile[32*33+32] = hash(x+32,y+32);
  gen_subdivide(tile,0,0,32,x,y);
  clamp(tile, 0,0);
  clamp(tile, 0,32);
  clamp(tile, 32,0);
  clamp(tile, 32,32);
}

var scr = [];
function gen_screen()
{
  var tbl = document.createElement('table');
  var thead = document.createElement('thead');
  var tbody = document.createElement('tbody');
  tbl.appendChild(thead);
  var y,idx;
  for(y=0,idx=0;y<HEIGHT;y++) {
    var tr = document.createElement('tr');
    for(var x=0;x<WIDTH;x++) {
      var td = document.createElement('td');
      td.width = '16px';
      td.height = '16px';
      td.style.background = 'url("tiles.gif") 0 0 no-repeat';
      scr[idx++] = td;
      tr.appendChild(td);
    }
    tbody.appendChild(tr);
  }
  tbl.cellSpacing = '0px';
  tbl.cellPadding = '0px';
  tbl.appendChild(tbody);
  return tbl;
}

var world = {}
function get_tile(x,y)
{
  if(!world[y]) { world[y] = {}; }
  if(world[y][x]) { return world[y][x]; }
  world[y][x] = [];
  gen_tile(world[y][x], x<<5, y<<5);
  return world[y][x];
}

function update_screen_tile(X,Y)
{
  X >>= 5; Y >>= 5;
  var tile = get_tile(X, Y);
  var tileup = get_tile(X, Y-1);
  var tileleft = get_tile(X-1, Y);
  var idx;
  var tileidx;
  X <<= 5; Y <<= 5;
  // plz to optimize this
  for(y=0,idx=0;y<HEIGHT;y++) {
    for(x=0,tileidx=y*33;x<WIDTH;x++,tileidx++) {
      var l,u,lu,ru,ld;
      var c = tile[tileidx]|0;
      var r = tile[1+tileidx]|0;
      var d = tile[33+tileidx]|0;
      var rd = tile[tileidx+34]|0;
      ru = tile[tileidx-32]|0;
      ld = tile[tileidx+32]|0;
      if(x == 0) {
        l = tileleft[31+tileidx]|0;
        lu = tileleft[tileidx-2]|0;
        ld = tileleft[tileidx+64]|0;
      } else {
        l = tile[tileidx-1]|0;
      }
      if(y == 0) {
        u = tileup[x+31*33]|0;
        lu = tileup[x-1+31*33]|0;
        ru = tileup[x+1+31*33]|0;
      } else {
        u = tile[tileidx-33]|0;
      }
      if(x>0 && y>0)
        lu = tile[tileidx-34]|0;
      else if(x == 0 && y == 0)
        lu = c;

      var t = ((u>c ? 1:0)|
               (l>c ? 2:0)|
               (r>c ? 4:0)|
               (d>c ? 8:0)|
               (lu>c ? 16:0)|
               (ru>c ? 32:0)|
               (ld>c ? 64:0)|
               (rd>c ? 128:0));
      var charidx = c*94 + tile_lut[t]*2 + (hash(X+x,Y+y) > 0.5 ? 1:0);
      var _x = -16*(charidx&15), _y = -16*(charidx>>4);
      scr[idx++].style.backgroundPosition = _x+"px "+_y+"px";
    }
  }
}

function world_height(x,y)
{
  var tile = get_tile(x>>5,y>>5);
  return tile[(x&31) + (y&31)*33];
}

var user_avatar = document.createElement('img');
user_avatar.src = 'Knight.png';
var user_x=false;
var user_y=false;
var user_swim = 2 - 0.00;
var user_climb = 6 + 0.01;
function move_avatar(x,y)
{
  var height = world_height(x,y);
  if(height < user_swim) { return; } // below sea level?
  if(height > user_climb) { return; } // mountain?
//  var idx1 = (user_x&31) + (user_y&31)*WIDTH;
  var idx2 = (x&31) + (y&31)*WIDTH;
  if(user_avatar.parentNode) {
    user_avatar.parentNode.removeChild(user_avatar);
  }
  scr[idx2].appendChild(user_avatar);
  if(user_x===false || user_y===false || (x>>5) != (user_x>>5) || (y>>5) != (user_y >> 5)) {
    update_screen_tile(x,y);
  }
  user_x = x;
  user_y = y;

  update_status(x+", "+y+", "+(height*100|0));
}

function update_status(msg, node)
{
  var stat = $(node || 'status');
  while(stat.childNodes[0]) { stat.removeChild(stat.childNodes[0]); }
  var s = $('status');
  stat.appendChild(document.createTextNode(msg));
}

function recv_update(txt)
{
  update_status("got update: "+txt);
}

function xmlhttp_handler() {
  if(this.readyState == 4 && this.status == 200) {
    recv_update(this.responseText);
//    return startrequestloop();
  } else if (this.readyState == 4 && this.status != 200) {
    update_status("hurk. "+this.status, "banner");
  } else {
    update_status("rs="+this.readyState+" status="+this.status, "banner");
  }
}

var sessiontoken;
function startrequestloop()
{
  var client = new XMLHttpRequest();
  client.onreadystatechange = xmlhttp_handler;
  client.open("GET", "/s/rcv/"+sessiontoken);
  client.send(false);
}

function start()
{
  sessiontoken = "foo";
  load(16,16);
}

function load(x,y)
{
  var maptable = $('map');
  build_tile_lut();
  while(maptable.childNodes[0]) { maptable.removeChild(maptable.childNodes[0]); }
  maptable.appendChild(gen_screen());
  move_avatar(x,y);
}

var keymap_move = {
  // wasd... but what should be the diagonals?
  'w':[0,-1],
  'a':[-1,0],
  's':[0,1],
  'd':[1,0],

  // nethack keys
  'h':[-1,0],
  'j':[0,1],
  'k':[0,-1],
  'l':[1,0],
  'b':[-1,1],
  'y':[-1,-1],
  'n':[1,1],
  'u':[1,-1]
};

window.onkeypress = function(e)
{
  var k;
  if(!sessiontoken) { return; }
  if(window.event) { k=e.keyCode; }
  else { k=e.which; }
  k = String.fromCharCode(k);
  if(keymap_move[k]) {
    move_avatar(user_x+keymap_move[k][0], user_y+keymap_move[k][1]);
  }
}

function teleport()
{
  move_avatar($('x').value-0,$('y').value-0)
}

