function createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');

	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) {
		  var v = c.substring(nameEQ.length,c.length);

		  return v;
		}
	}
	return null;
}

function eraseCookie(name) {
	createCookie(name,"",-1);
}

function StringBuilder(value) {
  this.strings = [""];
  if (value)
    this.append(value);
}

StringBuilder.prototype.append = function(v) {
  this.strings.push(v);
}

StringBuilder.prototype.appendList = function() {
  for (var i = 0; i < arguments.length; i++)
    this.strings.push(arguments[i]);
}

StringBuilder.prototype.clear = function() {
  this.strings.length = 1;
}

StringBuilder.prototype.toString = function() {
  return this.strings.join("");
}

var ParticleSystem = function() {
  return this.init();
}

function updateParticleSystem(ps) {
  for (var i = 0; i < ps.allocated.length;) {
    var p = ps.allocated[i];
    if (p.fn(p, 0.1) == false) {
      p.pdiv.style.display = "none";
      p.pdiv.style.opacity = "0";      
      if (i != ps.allocated.length - 1)
        ps.allocated[i] = ps.allocated.pop();
      else
        ps.allocated.pop();
      ps.free.push(p);
    } else {
      ++i;
    }
  }
}

ParticleSystem.prototype = {
  init: function() {
    this.free = [];
    this.allocated = [];
  },

  instantiate: function(id, fn) {
    var sb = new StringBuilder();

    for (var i = 0; i < 30; ++i) {
      sb.appendList(
        "<div id='ps", i, "' style='width:16px;height:16px;position:absolute;opacity:0;left:0px;top:0px;background:transparent url(imgs/star.png) no-repeat;'>&nbsp;</div>");
      this.free.push({id: "ps" + i});
    }    

    var ps = this;

    setTimeout(function() {
      $(id).get(0).innerHTML = sb.toString();
      setTimeout(function() {
        setInterval(function() { updateParticleSystem(ps); }, 100);

        fn();
      }, 50);
    }, 50);    
  },  

  create: function(src, x, y, fn) {
    if (this.free.length > 0) {
      var p = this.free.shift();

      p.fn = fn;
      p.x = x;
      p.y = y;

      var pdiv = document.getElementById(p.id);
      pdiv.style.left = x + "px";
      pdiv.style.top = y + "px";

      p.pdiv = pdiv;

      this.allocated.push(p);

      return p;
    }
  }
}

var Faery = function(w, h, c, b, diff, mp_seed, mp_game_type, mp_reverse) {
  return this.init(w, h, c, b, diff, mp_seed, mp_game_type, mp_reverse);
}

function makeRng(seeder) {
  var seed = seeder ? seeder : new Date().getTime();
  rnd = { seed: seed, start: seed, next: function() {
    this.seed = (this.seed * 9301 + 49297) % 233280;

    return this.seed / 233280.0;
  }};

  return rnd;
}

var g_blk = -1;

Faery.images = [ "imgs/rb.png", "imgs/yb.png", "imgs/gb.png", "imgs/cb.png", "imgs/bb.png", "imgs/pb.png", "imgs/r.png", "imgs/y.png", "imgs/g.png", "imgs/c.png", "imgs/b.png", "imgs/p.png", "imgs/w.png", "imgs/f.png", "imgs/wb.png", "imgs/fb.png"];
Faery.preload = [
  // Menu images.
  "imgs/win.png",
  "imgs/lost.png",
  // Buttons.
  "imgs/rbt.png",
  "imgs/ybt.png",
  "imgs/gbt.png",
  "imgs/cbt.png",
  "imgs/bbt.png",
  "imgs/pbt.png",
  "imgs/bt.png",
  "imgs/ee.png",
  "imgs/e.png",
  "imgs/save.png",
  "imgs/surrender.png",
  "imgs/side.png",
  // Particles.
  "imgs/star.png"
].concat(Faery.images);

Faery.prototype = {
  init: function(w, h, c, games, diff, mp_seed, mp_game_type, mp_reverse) {
    if (w > 9) {
      this.blockW = 31;
      this.blockH = 32;      
      this.offset = 0;
      Faery.imageFairies = Faery.images.length - 1;
      Faery.imageIce = Faery.images.length - 2;
    } else { 
      this.blockW = 31;
      this.blockH = 32;
      this.offset = 0;
      Faery.imageFairies = Faery.images.length - 1;
      Faery.imageIce = Faery.images.length - 2;
    }    

    this.halfW = Math.floor(this.blockW * 0.5);
    this.halfH = Math.floor(this.blockH * 0.5);

    this.w = Math.max(w, h);
    this.h = Math.min(w, h);
    this.c = c;
    this.lines = this.w + this.h - 1;
    this.lastnpc = -1;
    this.lastpc = -1;
    this.npclist = [];
    this.pclist = [];

    this.rnd = makeRng(mp_seed);
    this.turns = "";
    this.total = 0;

    // Generate field map.
    this.field = [];
    for (var i = 0, b = 1, e = (this.w + this.h - 1) * 2 - 1; i < this.lines; ++i, b += 2, e -= 2) {
      this.field[i] = [];
      for (var j = 0; j < Math.min(h * 2 - 1, Math.min(b, e)); ++j)
      {
        this.field[i][j] = this.randomColor();
        ++this.total;
      }
    }

    this.blocks = 0;

    var blk = g_blk;

    if (_mp_multiplayer) {
      if (mp_game_type == 0)
        this.type1(blk);
      else if (mp_game_type == 1)
        this.type2(blk);
      else if (mp_game_type == 2)
        this.type3(blk);
      else if (mp_game_type == 3)
        this.type4(blk);
      else if (mp_game_type == 4)
        this.type5(blk);
    }

    if (!_mp_multiplayer && diff == 1) {
      if (games != undefined || games != null) {
        if (games == 0) {

          this.type1(blk);
          
        } else if (games == 1) {    
          
          this.type2(blk);

        } else if (games == 2) {
    
          this.type3(blk);

        } else if (games == 3) {

          this.type4(blk);

        } else if (games == 4) {
         
          this.type5(blk);
        } else {
          var t = Math.floor(Math.random() * 5);
          
          if (t == 0)
            this.type1(blk);
          else if (t == 1)
            this.type2(blk);
          else if (t == 2)
            this.type3(blk);
          else if (t == 3)
            this.type4(blk);
          else 
            this.type5(blk);
        }
      }
    }

    if (mp_reverse) {
      this.field.reverse();

      for (var i = 0; i < this.field.length; ++i)
        this.field[i].reverse();
    }
  },

  type1: function(blk) {    
    this.field[7][4] = blk; this.field[7][9] = blk;
    this.field[8][5] = blk; this.field[8][10] = blk;
    this.blocks = 4;
  },

  type2: function(blk) {
    this.field[7][5] = blk; this.field[7][6] = blk; this.field[7][7] = blk; this.field[7][8] = blk;
    this.field[8][6] = blk; this.field[8][7] = blk; this.field[8][8] = blk; this.field[8][9] = blk;
    this.blocks = 8;
  },

  type3: function(blk) {
    this.field[6][4] = blk; this.field[7][6] = blk; this.field[8][8] = blk; this.field[9][8] = blk;
    this.blocks = 4;
  },

  type4: function(blk) {
    this.field[4][4] = blk; this.field[5][4] = blk; this.field[6][4] = blk;
    this.field[11][4] = blk; this.field[10][6] = blk; this.field[9][8] = blk;
    this.blocks = 6;
  },

  type5: function(blk) {    
    this.field[4][3] = blk; this.field[4][4] = blk; this.field[4][5] = blk;
    this.field[11][3] = blk; this.field[11][4] = blk; this.field[11][5] = blk;
    this.field[7][9] = blk; this.field[8][10] = blk; this.field[9][9] = blk;
    this.field[8][5] = blk; this.field[7][4] = blk; this.field[6][3] = blk;
    this.blocks = 12;
  },

  randomColor: function() { return Math.floor(this.rnd.next() * this.c); },

  randomDirection: function(distance) { 
    var angle = Math.random() * Math.PI * 2;

    return {left:Math.floor(Math.cos(angle) * distance), top: Math.floor(Math.sin(angle) * distance) };
  },

  complete: function(backid) {
    // Set the container dimensions.
    $(backid).css("width", this.w * (this.blockW + 1) - 1)
      .css("height", this.h * this.blockH);
  },
 
  instantiateDots: function(id, fn) {
    // Create dot images.
    var x = 0;
    var y = (this.h - 1) * this.blockH;
    var dx = Math.floor(this.blockW / 2) + 1;
    var dy = Math.floor(this.blockH / 2);

    var sbFront = new StringBuilder();

    var blk = g_blk;
    var w = this.w;
    var img = w > 9 ? "imgs/e.png" : "imgs/ee.png";

    for (var i = 0; i < this.field.length; ++i) {
      var cx = x;
      var cy = y;

      for (var j = 0; j < this.field[i].length; ++j) {
        if (this.field[i][j] == blk) {
          sbFront.appendList("<img class='dot' style='left:", cx, "px;top:", cy, "px;' src='", img, "' id='dot_", i, "_", j, "'/>");
        } else {
          sbFront.appendList("<img class='dot' style='left:", cx, "px;top:", cy, "px;' src='", Faery.images[this.field[i][j] + this.offset], "' id='dot_", i, "_", j, "'/>");
        }

        cx -= dx;
        cy -= dy;
      }
      
      if (i < this.w - 1)
        x += this.blockW + 1;
      else
        y -= this.blockH;
    }

    setTimeout(function() {
      $(id).get(0).innerHTML = sbFront.toString();
      setTimeout(function() {
        fn();
      }, 50);
    }, 50);
  },

  instantiateParticles: function(id, fn) {
    this.ps = new ParticleSystem();

    this.ps.instantiate(id, fn);
  },

  updateParticle: function(p, t) {
    p.life -= t;
    if (p.life <= 0)
      return false;
    
    p.x += p.dir.left * t;
    p.y += p.dir.top * t;

    p.dir.top += 40.0 * t;

    var jp = p.pdiv;

    var v = Math.floor(p.life * 10000) / 10000;
    
    jp.style.opacity = v.toString();
    jp.style.left = Math.floor(p.x) + "px";
    jp.style.top = Math.floor(p.y) + "px";
  
    return true;
  },

  hasTurns: function() {
    var a = 0;
    var b = 0;
    for (var i = 0; i < this.field.length; ++i)
      for (var j = 0; j < this.field[i].length; ++j) {
        if (this.field[i][j] == Faery.imageFairies)
          ++a;
        if (this.field[i][j] == Faery.imageIce)
          ++b;
      }

    var total = this.w * this.h + (this.w - 1) * (this.h - 1) - this.blocks;

    if (b > a && b > total / 2)
      return false;

    if (_mp_multiplayer) {
      if (a > b && a > total / 2)
      return false;
    }

    if (a + b == total)
      return false;

    return true;            
  },

  countCrystals: function(fairies) {
    var a = 0;
    var b = 0;
    for (var i = 0; i < this.field.length; ++i)
      for (var j = 0; j < this.field[i].length; ++j) {
        if (this.field[i][j] == Faery.imageFairies)
          ++a;
        if (this.field[i][j] == Faery.imageIce)
          ++b;
      }

    return fairies ? a : b; 
  },

  fillHoles: function(x, y, c, cr) {
    var bound = Faery.images.length + 1;

    var open = [{x:x, y:y}];
    var visited = [];

    var reachable = false;

    while (open.length > 0) {
      var pt = open.shift();

      var v = this.field[pt.x][pt.y];

      if (v < 0) {        
        continue;
      }

      if (v == c || v >= bound)
        continue;
      else if (v == cr + bound) {
        reachable = true;
      } else if (v == cr) {
        reachable = true;
        this.field[pt.x][pt.y] = v + bound;
      } else {
        visited.push(pt);
        this.field[pt.x][pt.y] = v + bound;
      }

      v = this.field[pt.x][pt.y - 1];
      if (v != c && v < bound)
        open.push({x: pt.x, y: pt.y - 1});
      v = this.field[pt.x][pt.y + 1];
      if (v != c && v < bound)
        open.push({x: pt.x, y: pt.y + 1});

      if (pt.x - 1 >= 0) {
        var ny = pt.x < this.w ? pt.y - 1 : pt.y + 1;
        v = this.field[pt.x - 1][ny];
        if (v != c && v < bound)
          open.push({x: pt.x - 1, y: ny});
      }

      if (pt.x + 1 < this.lines) {
        var ny = pt.x < this.w - 1 ? pt.y + 1 : pt.y - 1;
        v = this.field[pt.x + 1][ny];
        if (v != c && v < bound)
          open.push({x: pt.x + 1, y: ny});
      }
    }

    if (!reachable) {
      while (visited.length > 0) {
        var pt = visited.shift();

        this.field[pt.x][pt.y] = c;

        var dot = $("#dot_" + pt.x + "_" + pt.y);
        var dotdiv = dot.get(0);
        dotdiv.setAttribute("src", Faery.images[c]);
      }
    }
  },

  paint: function(x, y, c, cr, ocr, checkHoles) {
    var bound = Faery.images.length + 1;
    var painted = 0;
    var rep = this.field[x][y];
    var open = [{x:x, y:y}];

    this.turns += c;

    // Reset last time marks.
    var resetList;
    if (cr == Faery.imageFairies) {
      resetList = this.pclist;    
    } else {
      resetList = this.npclist; 
    }

    while (resetList.length > 0) {
      var pt = resetList.shift();
    }

    while (open.length > 0) {
      var pt = open.shift();

      var vv = this.field[pt.x][pt.y];

      if (vv < 0)
        continue;

      if (vv == rep || vv == c) {
        if (vv == c) {
          var dot = $("#dot_" + pt.x + "_" + pt.y);
          var dotdiv = dot.get(0);
          dotdiv.setAttribute("src", Faery.images[cr]);
          resetList.push(pt);

          // Add sparkles effect.
          if (this.ps) {
            var lx = parseInt(dotdiv.style.left) + this.halfW;
            var ly = parseInt(dotdiv.style.top) - this.halfH;

            for (var spark = 0; spark < 2; ++spark) {
              var p = this.ps.create("imgs/star.png", lx, ly, this.updateParticle);
  
              if (p) {
                p.pdiv.style.opacity = 1;
                p.pdiv.style.display = "block";
                p.life = 1;
                p.dir = this.randomDirection(40);
              }
            }
          }
        }

        this.field[pt.x][pt.y] += bound;

        if (this.field[pt.x][pt.y - 1] < bound)
          open.push({x: pt.x, y: pt.y - 1});
        if (this.field[pt.x][pt.y + 1] < bound)
          open.push({x: pt.x, y: pt.y + 1});

        var ny = pt.x < this.w ? pt.y - 1 : pt.y + 1;
        if (pt.x - 1 >= 0 && this.field[pt.x - 1][ny] < bound)
          open.push({x: pt.x - 1, y: ny});          

        ny = pt.x < this.w - 1 ? pt.y + 1 : pt.y - 1;
        if (pt.x + 1 < this.lines && this.field[pt.x + 1][ny] < bound)
          open.push({x: pt.x + 1, y: ny});
      }
    }

    // Paint all markings.
    for (var i = 0; i < this.field.length; ++i)
      for (var j = 0; j < this.field[i].length; ++j)
        if (this.field[i][j] >= bound)
          this.field[i][j] = cr;

    if (checkHoles) {
      for (var i = 0; i < this.field.length; ++i)
        for (var j = 0; j < this.field[i].length; ++j) {
          var v = this.field[i][j];

          if (v < 0)
            continue;

          if (v != cr && v != ocr && v < bound)
            this.fillHoles(i, j, cr, ocr);
        }

      for (var i = 0; i < this.field.length; ++i)
        for (var j = 0; j < this.field[i].length; ++j)
          if (this.field[i][j] >= bound)
            this.field[i][j] -= bound;
    }

    for (var i = 0; i < this.field.length; ++i)
      for (var j = 0; j < this.field[i].length; ++j)
        if (this.field[i][j] == cr)
          ++painted;

    if (cr == Faery.imageIce)
      this.lastnpc = resetList.length > 0 ? c : -1;
    if (cr == Faery.imageFairies)
      this.lastpc = resetList.length > 0 ? c : -1;

    return painted;
  },

  tryPaint: function(x, y, c) {
    var bound = Faery.images.length + 1;
    var rep = this.field[x][y];
    var open = [{x:x, y:y}];

    while (open.length > 0) {
      var pt = open.shift();

      var v = this.field[pt.x][pt.y];

      if (v < 0)
        continue;

      if (v == rep || v == c) {
        this.field[pt.x][pt.y] += bound;

        if (this.field[pt.x][pt.y - 1] < bound)
          open.push({x: pt.x, y: pt.y - 1});
        if (this.field[pt.x][pt.y + 1] < bound)
          open.push({x: pt.x, y: pt.y + 1});

        var ny = pt.x < this.w ? pt.y - 1 : pt.y + 1;
        if (pt.x - 1 >= 0 && this.field[pt.x - 1][ny] < bound)
          open.push({x: pt.x - 1, y: ny});          

        ny = pt.x < this.w - 1 ? pt.y + 1 : pt.y - 1;
        if (pt.x + 1 < this.lines && this.field[pt.x + 1][ny] < bound)
          open.push({x: pt.x + 1, y: ny});
      }
    }
  },

  think: function(x, y) {
    var bound = Faery.images.length + 1;

    // Use simple heuristic.
    var colors = [];
    for (var c = 0; c < this.c; ++c) {
      if (this.lastnpc == c || this.lastpc == c)
        continue;

      this.tryPaint(x, y, c);      

      // Revert all markings.
      colors[c] = 0;
      for (var i = 0; i < this.field.length; ++i)
        for (var j = 0; j < this.field[i].length; ++j)
          if (this.field[i][j] >= bound) {
            this.field[i][j] -= bound;
            ++colors[c];
          }
    }    

    var pick = 0;
    for (var i = 1; i < this.c; ++i)
      if (colors[pick] == undefined || colors[i] > colors[pick])
        pick = i;

    return this.paint(x, y, pick, Faery.imageIce, Faery.imageFairies, true);
  },

  thinkGoal: function(x, y) {
    var bound = Faery.images.length + 1;

    // Use simple heuristic.
    var colors = [];
    for (var c = 0; c < this.c; ++c) {
      if (this.lastnpc == c || this.lastpc == c)
        continue;

      this.tryPaint(x, y, c);      

      // Revert all markings.
      colors[c] = 0;
      for (var i = 0; i < this.field.length; ++i)
        for (var j = 0; j < this.field[i].length; ++j)
          if (this.field[i][j] >= bound) {
            this.field[i][j] -= bound;
            var d = (this.field.length - i) / this.field.length;
            colors[c] += 2.0 + 7.0 * d * d;
          }
    }    

    var pick = 0;
    for (var i = 1; i < this.c; ++i)
      if (colors[pick] == undefined || colors[i] > colors[pick])
        pick = i;

    return this.paint(x, y, pick, Faery.imageIce, Faery.imageFairies, true);
  }
}

// Game stats tracker.

var _game_stats_timer;

function game_stats_begin() {
  if (_game_stats_timer) {
  } else {  
    $.ajax({type: "POST", url: "count.php", data: {}, timeout: 15000, cache: false, success: game_stats_update, error: game_stats_retry });
  }
}

function game_stats_update(msg) {
  var parts = msg.split("|");

  $("#game-stats").css("opacity", "0.75").get(0).innerHTML = parts[0] + " waiting, " + parts[1] + " games";

  _game_stats_timer = setTimeout(function() { _game_stats_timer = null; game_stats_begin() }, 5000); 
}

function game_stats_retry() {
  _game_stats_timer = setTimeout(function() { _game_stats_timer = null; game_stats_begin() }, 5000);
}

function game_stats_stop() {
  if (_game_stats_timer) {
    clearTimeout(_game_stats_timer);
    _game_stats_timer = null;
  }
}

// Main game.

var firstLoad = [ "imgs/logo.jpg", "imgs/underclouds.png", "imgs/loading.png" ];
// Preload images for the game.
var preload = [ "imgs/start.png", "imgs/rotate.png", "imgs/buttons.png", "imgs/bg.jpg", "imgs/easy.png", "imgs/exit.png", "imgs/rules.png", "imgs/scoreboard.png", "imgs/hard.png", "imgs/multiplayer.png", "imgs/instructions.png", "imgs/continue.png" ].concat(Faery.preload);

function setImage(id, image)
{
  $(id).attr("src", image);
}

function setBackground(id, image, extra) {
  $(id).css("background", "transparent url(" + image + ") no-repeat " + (extra == undefined ? "" : extra));
};

function setBackgroundWithColor(id, image, color, extra) {
  $(id).css("background", color + " url(" + image + ") no-repeat " + (extra == undefined ? "" : extra));
};

// Global faery game singleton.
var faery;

// Global to store score.
var totalScore = 0;
var totalGames = 0;
var gameHistory = "";

// Game flow.
function gameturn(choice, manual) {
  var a;
  var b;

  if (_mp_multiplayer) {
    mp_turn_cancel_timers();
  }
  
  if (_mp_multiplayer) {
    if (choice >= 0) {
      if (_mp_current_turn % 2 == _mp_turn) {
        a = faery.paint(0, 0, choice, Faery.imageFairies, Faery.imageIce, true);
    
        $("#lbl-player").html(a);
      } else {
        a = faery.paint(faery.field.length - 1, 0, choice, Faery.imageIce, Faery.imageFairies, true);
          
        $("#lbl-pc").html(a);
      }
    }
  } else {
    a = faery.paint(0, 0, choice, Faery.imageFairies, Faery.imageIce, true);  
    b = faery.thinkGoal(faery.field.length - 1, 0);
  }  

  if (_mp_multiplayer && _mp_current_turn % 2 == _mp_turn && manual) {
    $.ajax({type: "POST", url: "turn.php", data: { rq: "fir", a: ++_mp_ajax_seq, id: _mp_game_id, p: choice }, cache: false, success: function() {} });

    _mp_expect_turn += 2;
  }

  if (!_mp_multiplayer) {
    if (a)
      $("#lbl-player").html(a);
    if (b)
      $("#lbl-pc").html(b);
  }

  $("#btn-r").get(0).setAttribute("src", faery.lastpc != 0 && faery.lastnpc != 0 ? "imgs/rbt.png" : "imgs/bt.png");
  $("#btn-y").get(0).setAttribute("src", faery.lastpc != 1 && faery.lastnpc != 1 ? "imgs/ybt.png" : "imgs/bt.png");
  $("#btn-g").get(0).setAttribute("src", faery.lastpc != 2 && faery.lastnpc != 2 ? "imgs/gbt.png" : "imgs/bt.png");
  $("#btn-c").get(0).setAttribute("src", faery.lastpc != 3 && faery.lastnpc != 3 ? "imgs/cbt.png" : "imgs/bt.png");
  $("#btn-b").get(0).setAttribute("src", faery.lastpc != 4 && faery.lastnpc != 4 ? "imgs/bbt.png" : "imgs/bt.png");
  $("#btn-p").get(0).setAttribute("src", faery.lastpc != 5 && faery.lastnpc != 5 ? "imgs/pbt.png" : "imgs/bt.png");

  if (!faery.hasTurns()) {
    faery.playerWon = a >= b;

    if (_mp_multiplayer) {
      mp_turn_cancel_timers();

      a = faery.countCrystals(true);
      b = faery.countCrystals(false);

      faery.playerWon = a >= b;
    }

    if (faery.playerWon)
    {
      totalScore += a * 10;
      totalGames += 1;

      // Update game history.
      gameHistory += faery.difficulty + "_" + faery.rnd.start + "_" + faery.turns + "_" + totalScore + "_";
    }          

    if (a >= b) {
      $("#grats-type").get(0).setAttribute("src", "imgs/win.png");
    } else {
      $("#grats-type").get(0).setAttribute("src", "imgs/lost.png");
    }

    $(".grats-container").css("display", "block");
    $("#grats").css("width", "10px").css("height", "5px").css("opacity", "0").animate(
      {opacity: 1, width: 335, height: 144}, 150, function() {
        $("#grats").animate({width: 258, height: 111}, 400);
      });
    $("#grats-continue").css("opacity", "0").animate({opacity: 1}, 500);

    return;
  }

  if (_mp_multiplayer) {
    _mp_current_turn += 1;

    turn_mp();
  }
}

function gameinit() {
  $("#btn-r").click(function() { if ($(this).attr("src") != "imgs/bt.png") gameturn(0, true); return false; });
  $("#btn-y").click(function() { if ($(this).attr("src") != "imgs/bt.png") gameturn(1, true); return false; });
  $("#btn-g").click(function() { if ($(this).attr("src") != "imgs/bt.png") gameturn(2, true); return false; });
  $("#btn-c").click(function() { if ($(this).attr("src") != "imgs/bt.png") gameturn(3, true); return false; });
  $("#btn-b").click(function() { if ($(this).attr("src") != "imgs/bt.png") gameturn(4, true); return false; });
  $("#btn-p").click(function() { if ($(this).attr("src") != "imgs/bt.png") gameturn(5, true); return false; });
}

var _mp_time_message;
var _mp_time_left;
var _mp_countdown_timer; 
var _mp_turn_wait_timer;
var _mp_end_function;

function mp_wait_for_turn_success(msg) {
  var parts = msg.split("|");
  if (parseInt(parts[0]) == _mp_ajax_seq) {    
    var reply = parts[1];

    if (reply == "0") {
      _mp_turn_wait_timer = setTimeout(mp_wait_for_turn, 1000);
    } else {
      var config_parts = reply.split(" ");

      var pick = parseInt(config_parts[1]);

      mp_turn_cancel_timers();

      // Check if another player surrendered.
      if (config_parts[2] == "3") {
        ++_mp_ajax_seq;

        mp_player_timeout();

        return;
      }

      gameturn(pick);
    }
  }  
}

function mp_turn_cancel_timers() {
  if (_mp_countdown_timer) {
    clearTimeout(_mp_countdown_timer);
    _mp_countdown_timer = null;
  }
  if (_mp_turn_wait_timer) {
    clearTimeout(_mp_turn_wait_timer);
    _mp_turn_wait_timer = null;
  }
}

function mp_wait_for_turn_error() {
  _mp_turn_wait_timer = setTimeout(mp_wait_for_turn, 1000);
}

function mp_wait_for_turn() {
  $.ajax({type: "POST", url: "turn.php", data: { rq: "chk", a: ++_mp_ajax_seq, id: _mp_game_id, t: _mp_expect_turn }, timeout: 15000, cache: false, success: mp_wait_for_turn_success, error: mp_wait_for_turn_error });
}

function mp_skip_turn() {
  gameturn(-1, true);
}

function mp_player_timeout() {
  mp_turn_cancel_timers();
  ++_mp_ajax_seq;

  $("#grats-type").get(0).setAttribute("src", "imgs/win.png");

  $(".grats-container").css("display", "block");
  $("#grats").css("width", "10px").css("height", "5px").css("opacity", "0").animate(
    {opacity: 1, width: 335, height: 144}, 150, function() {
      $("#grats").animate({width: 258, height: 111}, 400);
    });
  $("#grats-continue").css("opacity", "0").animate({opacity: 1}, 500);  
}

function turn_countdown(msg, timer, end_function) {
  _mp_time_left = timer;
  _mp_time_message = msg;
  _mp_end_function = end_function;

  $("#mp-text").get(0).innerHTML = msg + timer;

  _mp_countdown_timer = setTimeout(turn_countdown_timer, 1000);
}

function turn_countdown_timer() {
  --_mp_time_left;

  if (_mp_time_left <= 0) {
    $("#mp-text").get(0).innerHTML = "";

    if (_mp_end_function)
      _mp_end_function();
  } else {
    $("#mp-text").get(0).innerHTML = _mp_time_message + _mp_time_left;

    _mp_countdown_timer = setTimeout(turn_countdown_timer, 1000);
  }
}

function turn_mp() {
  if (_mp_multiplayer) {
    $("#mp-buttons").css("display", _mp_current_turn % 2 == _mp_turn ? "block" : "none");

    if (_mp_current_turn % 2 == _mp_turn) {
      turn_countdown("turn time left ", 20, function() {
        mp_skip_turn();
      });
    } else {            
      turn_countdown("waiting for other player ", 75, function() {
        mp_player_timeout();
      });

      mp_wait_for_turn();
    }
  }
}

function gamemarathon(difficulty, seed, game_type) {
  var w, h;
  if (difficulty == 0) {
    w = 9; h = 8;
  } else {          
    difficulty = 1;
    w = 9; h = 8;
  }

  $("#your-side > img").attr("src", "imgs/side.png");
  $("#your-side").css("display", "block").css("left", "1px").css("top", "68px");

  if (_mp_multiplayer) {
    $("#img-end-game").attr("src", "imgs/surrender.png").css("width", "75px");
  } else {
    $("#img-end-game").attr("src", "imgs/save.png").css("width", "119px");
  }
   
  var reverse = _mp_multiplayer && _mp_turn == 1 ? true : false;

  faery = new Faery(w, h, 6, totalGames, difficulty, seed, game_type, reverse);

  faery.difficulty = difficulty;

  faery.instantiateDots("#field", function() {
    faery.instantiateParticles("#effects", function() {
      faery.complete("#field-wrapper");

      // Paint first dots.
      var a = faery.paint(0, 0, faery.field[0][0], Faery.imageFairies, Faery.imageIce, false);
      var b = faery.paint(faery.field.length - 1, 0, faery.field[faery.field.length - 1][0], Faery.imageIce, Faery.imageFairies, false);

      $("#lbl-player").html(a);
      $("#lbl-pc").html(b);

      // Swap UI.
      $(".buttons-container").css("opacity", "0");
      $(".game-container").css("display", "block");
      $(".menu-container").animate({opacity: 0}, 400, function() { game_stats_stop(); });
      $(".game-container").animate({opacity: 1}, 400, function() {
        $(".menu-container").css("display", "none");

        // Enable game UI.
        if (!_mp_multiplayer || _mp_turn == 0) {
          $("#mp-buttons").css("display", "block");
          $("#mp-text").get(0).innerHTML = _mp_multiplayer ? "your turn" : "&nbsp;";

          $("#btn-r").get(0).setAttribute("src", "imgs/rbt.png");
          $("#btn-y").get(0).setAttribute("src", "imgs/ybt.png");
          $("#btn-g").get(0).setAttribute("src", "imgs/gbt.png");
          $("#btn-c").get(0).setAttribute("src", "imgs/cbt.png");
          $("#btn-b").get(0).setAttribute("src", "imgs/bbt.png");
          $("#btn-p").get(0).setAttribute("src", "imgs/pbt.png");          
        } else {
          $("#mp-buttons").css("display", "none");
          $("#mp-text").css("display", "block").get(0).innerHTML = "&nbsp;";
        }

        $(".buttons-container").animate({opacity: 1}, 400);
        $(".buttons-container").css("display", "block");          

        if (!_mp_multiplayer) {
          if (readCookie("rulesShown") != "1") {
            showrules();
          }
        }

        turn_mp();
      });
    });
  });
}

function showscorecontinue() {
  $(".topscore-container").css("display", "block").css("opacity", "0").animate(
    { opacity: 1}, 400, function() {
    });

  $.post("score.php", 
    { name: $("#txt-name").val(), history: gameHistory }, function(xml) {
      var sb = new StringBuilder();

      sb.append("<div class='topscore-table'>");
      sb.append("<div class='topscore-col'>");
      sb.append("<img src='imgs/easy.png' width='135' height='64' alt='Easy' border='0' style='display: block; margin: auto;'/>");
      $("easy > score", xml).each(function() {
        var n = $("<div/>").text($(this).attr("name")).html();
        sb.appendList(
         	"<div class='topscore-name'>", n, "</div><div class='topscore-score'>", $(this).attr("score"), "</div><div class='topscore-wins'>wins in row ", $(this).attr("games"), "</div>");
      });
      sb.append("</div>");
      
      sb.append("<div class='topscore-col'>");
      sb.append("<img src='imgs/hard.png' width='135' height='64' alt='Hard' border='0' style='display:block; margin: auto;'/>");
      $("hard > score", xml).each(function() {
        var n = $("<div/>").text($(this).attr("name")).html();
        sb.appendList(
         	"<div class='topscore-name'>", n, "</div><div class='topscore-score'>", $(this).attr("score"), "</div><div class='topscore-wins'>wins in row ", $(this).attr("games"), "</div>");
      });            
      sb.append("</div>");
      

      sb.append("</div><div class='clearer'>&nbsp;</div>");
      $("#topscore-text").html(sb.toString());
    });       
}

function showrules() {
  createCookie("rulesShown", "1");

  $(".rules-container").css("opacity", "0").css("display", "block").animate(
    { opacity: 0.99 }, 400);
}

function showscore(showcontainer) {
  $("#topscore-text").html("Loading...");

  if (showcontainer) {
    $(".record-container").css("display", "block");
    $(".record-inner").css("display", "none");
    $(".record-container").animate({opacity: 0.99}, 400, function() {
      setTimeout(showscorecontinue, 100);
    });
  } else {
    setTimeout(showscorecontinue, 100);
  }
}

function recordscore() {
  if (gameHistory != "") {
    $(".scored").html(totalScore + " points!");
    $(".record-container").css("display", "block");
    $("#record-inner").css("opacity", "1").css("display", "block");
    $(".record-container").animate({opacity: 0.99}, 400, function() {
      $("#txt-name").focus();
    });
  } else {
    $(".game-container").animate({opacity: 0}, 400, function() {
      $(".record-container").css("display", "none");
      $(".record-inner").css("display", "none");
      $(".game-container").css("display", "none");
      $(".topscore-container").css("display", "none");
      $(".menu-container").css("display", "block");
      $(".menu").css("display", "block").css("opacity", "1");
      $(".menu-container").animate({opacity: 1}, 400, function() { game_stats_begin(); });
    });
  }
}

// Multiplayer.
var _mp_ajax_seq;
var _mp_game_id;
var _mp_timer;
var _mp_turn;
var _mp_expect_turn;
var _mp_multiplayer;
var _mp_current_turn;

function mp_setup_game(game_id_raw, order, player1, player2) {
  $("#mp-container").animate({opacity: 0}, 400);

  $(".menu-container").animate({opacity: 0}, 400, function() {
    $("#mp-container").css("display", "none").css("opacity", "0");

    _mp_game_id = game_id_raw.substring(1);  
    _mp_turn = parseInt(order);

    if (_mp_turn == 1)
      _mp_expect_turn = 1;
    else
      _mp_expect_turn = 0;

    // Remove player id.
    player1 = player1.split(">")[1];
    
    var parts = player1.split(" ");

    var game_seed = parseFloat(parts[0]);
    var game_type = parseInt(parts[1]);

    _mp_current_turn = 0;

    gamemarathon(1, game_seed, game_type);
  });
}

function mp_wait_for_another_success(msg) {
  var parts = msg.split("|");
  if (parseInt(parts[0]) == _mp_ajax_seq) {
    var reply = parts[1].substring(0, 1);

    if (reply == "r") {
      // The game is ready now.
      $("#waiting-status").get(0).innerHTML = "Connected.";

      mp_setup_game(parts[1], parts[2], parts[3], parts[4]);
    } else {
      // Still waiting.
      _mp_timer = setTimeout(mp_wait_for_another, 2500);
    }    
  }  
}

function mp_wait_for_another_error() {
  _mp_timer = setTimeout(mp_wait_for_another, 2500);
}

function mp_wait_for_another() {
  $.ajax({type: "POST", url: "lobby.php", data: { rq: "ack", a: ++_mp_ajax_seq, id: _mp_game_id }, timeout: 15000, cache: false, success: mp_wait_for_another_success, error: mp_wait_for_another_error });
}

function mp_connect_success(msg) {
  var parts = msg.split("|");
  if (parseInt(parts[0]) == _mp_ajax_seq) {
    var reply = parts[1].substring(0, 1);
    var game_id = parts[1].substring(1);

    if (reply == "r") {
      // Game is ready.
      $("#waiting-status").get(0).innerHTML = "Connected.";

      mp_setup_game(parts[1], parts[2], parts[3], parts[4]);
    } else {
      _mp_game_id = game_id;

      // Waiting for another person.
      $("#waiting-status").get(0).innerHTML = "Waiting for another player...";

      mp_wait_for_another();
    }
  }
}

function game_multiplayer_begin() {
  $("#mp-container").css("display", "none");

  _mp_ajax_seq = 1;

  $("#waiting-status").get(0).innerHTML = "Connecting to server...";
  $("#mp-container").css("display", "block").css("opacity", "0").animate({opacity: 0.8}, 400, function() {
    // Pick rng seed and a field.
    var rng = makeRng();

    var info = rng.seed + " " + Math.floor(Math.random() * 5);

    $.ajax({type: "POST", url: "lobby.php", data: { rq: "beg", a: ++_mp_ajax_seq, ct: info }, cache: false, success: mp_connect_success });
  });
}

// UI flow.
$(function() {
  gameinit();

  $(document.createElement('img')).bind('load', function() {
    if(firstLoad[0]) {
      this.src = firstLoad.shift();
    } else {
      $(".preloading").css("display", "none");

      $(".brand").animate({opacity: 1}, 1500, function() {
        setTimeout(function() {
          $(".brand").animate({opacity: 0}, 1500, function() {
            $(".logo").animate({opacity: 1}, 1000, function() {
            });
          });
        }, 2000);
      });

      $(document.createElement('img')).bind('load', function() {
        if(preload[0]) {
          this.src = preload.shift(); 
        } else {
          setBackgroundWithColor(".game-container", "imgs/bg.jpg", "#000", "top");
          setBackground("#buttons", "imgs/buttons.png", "bottom center");
          setBackgroundWithColor(".warning", "imgs/rotate.png", "#000", "center");
          setImage("#rimg-start", "imgs/start.png");
          setImage("#btn-r", "imgs/rbt.png");
          setImage("#btn-y", "imgs/ybt.png");
          setImage("#btn-g", "imgs/gbt.png");
          setImage("#btn-c", "imgs/cbt.png");
          setImage("#btn-b", "imgs/bbt.png");
          setImage("#btn-p", "imgs/pbt.png");

          $("#loading").css("display", "none");
          $("#btn-start").css("display", "inline");
    
          $(".img-continue").attr("src", "imgs/continue.png");
          $(".img-instructions").attr("src", "imgs/instructions.png");
          $("#img-rules").attr("src", "imgs/rules.png");
          $("#img-multi").attr("src", "imgs/multiplayer.png");
          $("#img-easy").attr("src", "imgs/easy.png");
          $("#img-hard").attr("src", "imgs/hard.png");
          $("#img-exit").attr("src", "imgs/exit.png");
          $("#img-scoreboard").attr("src", "imgs/scoreboard.png");
    
          setTimeout(function() { updateLayout(); }, 100);
        }
      }).trigger('load');        
    }
  }).trigger('load');

  $("#btn-start").click(function() {
    window.scrollTo(0, 1);

    $("#btn-start").click(function() { return false; });          
    $(".logo").animate({opacity: 0}, 400, function() { 
      $(".logo").css("display", "none"); 
      $(".brand").css("display", "none");
    });
    $(".menu-container").css("display", "block");
    $(".menu-container").animate({opacity: 1}, 400, function() { game_stats_begin(); });

    return false;
  });

  $("#btn-easy").click(function() { 
    _mp_multiplayer = false;

    totalScore = 0; totalGames = 0; gameHistory = ""; 
    $(".menu-container").animate({opacity: 0}, 400, function() {
      game_stats_stop();

      gamemarathon(0); 
    });

    return false;
  });

  $("#btn-medium").click(function() { 
    _mp_multiplayer = false;

    totalScore = 0; totalGames = 0; gameHistory = ""; 
    $(".menu-container").animate({opacity: 0}, 400, function() {          
      game_stats_stop(); 

      gamemarathon(1);
    });

    return false;
  });

  $("#btn-multi").click(function() {
    _mp_multiplayer = true;

    window.scrollTo(0, 1);

    $(".menu").animate({opacity: 0}, 400, function() {
      game_stats_stop();
       
      game_multiplayer_begin();
    });    

    return false;
  });

  $("#btn-cancel-connect").click(function() {
    $("#mp-container").animate({opacity: 0}, 400, function() {
      ++_mp_ajax_seq;
      if (_mp_timer) {
        clearTimeout(_mp_timer);
      }

      $.ajax({type: "POST", url: "lobby.php", data: { rq: "can", a: ++_mp_ajax_seq, id: _mp_game_id }, cache: false, success: function() {} });

      $("#mp-container").css("display", "none");

      $(".menu").css("display", "block").css("opacity", "0").animate({opacity: 1}, 400, function() { game_stats_begin() ; });
    });

    return false;
  });

  $("#btn-continue").click(function() {
    if (_mp_multiplayer) {
      $("#grats-continue").animate({opacity: 0}, 400);
      $("#grats").animate({opacity: 0}, 400, function() {
        $(".grats-container").css("display", "none");
      });
      $("#record-inner").animate({opacity: 0}, 400, function() {
        $("#record-inner").css("display", "none");

        $(".game-container").animate({opacity: 0}, 400, function() {
          $(".record-container").css("display", "none");
          $(".record-inner").css("display", "none");
          $(".game-container").css("display", "none");
          $(".topscore-container").css("display", "none");
          $(".menu").css("display", "block").css("opacity", "1");
          $(".menu-container").css("display", "block");
          $(".menu-container").animate({opacity: 1}, 400, function() { game_stats_begin(); });
        });
      });

      return false;
    }

    $("#grats-continue").animate({opacity: 0}, 400);
    $("#grats").animate({opacity: 0}, 400, function() {
      $(".grats-container").css("display", "none");
    });

    if (faery.playerWon) {
      $(".game-container").animate({opacity: 0}, 400, function() {
        gamemarathon(faery.difficulty);
      });
    } else {
      recordscore();
    }

    return false;
  });
  $("#btn-record").click(function() {
    if (_mp_multiplayer) {
      mp_turn_cancel_timers();
      
      ++_mp_ajax_seq;

      $.ajax({type: "POST", url: "turn.php", data: { rq: "sur", p: 3, a: ++_mp_ajax_seq, id: _mp_game_id }, cache: false, success: function() {} });

      $(".game-container").animate({opacity: 0}, 400, function() {
        $(".record-container").css("display", "none");
        $(".record-inner").css("display", "none");
        $(".game-container").css("display", "none");
        $(".topscore-container").css("display", "none");
        $(".menu").css("display", "block").css("opacity", "1");
        $(".menu-container").css("display", "block");
        $(".menu-container").animate({opacity: 1}, 400, function() { game_stats_begin(); });
      });

    } else {
      recordscore();
    }

    return false;
  });

  $("#btn-topscore-continue").click(function() {
    // Fade out everything.
    $(".record-container").animate({opacity: 0}, 400);
    $(".game-container").animate({opacity: 0}, 400, function() {
      $(".record-container").css("display", "none");
      $(".game-container").css("display", "none");
      $(".topscore-container").css("display", "none");
      $(".menu-container").css("display", "block");
      $(".menu").css("display", "block").css("opacity", "1");
      $(".menu-container").animate({opacity: 1}, 400, function() { game_stats_begin(); });
    });

    return false;
  });

  $("#btn-rec-continue").click(function() {
    $("#txt-name").blur();
    $("#record-inner").animate({opacity: 0}, 400, function() {
      $("#record-inner").css("display", "none");

      showscore(false);
    });

    return false;
  });

  $("#btn-scoreboard").click(function() {
    showscore(true);

    return false;
  });

  $("#btn-rules").click(function() {
    showrules();

    return false;
  });

  $("#btn-rules-continue").click(function() {
    $(".rules-container").animate(
      { opacity: 0 }, 400, function() {
        $(this).css("opacity", "0").css("display", "none");
      });

    return false;
  });
});