var Field = Class.create();
Field.prototype = {
  tileWidth: 41,
  tileHeight: 41,
  markupTileWidth: 12,
  markupTileHeight: 12,

  initialize: function(stats) {
    this.tiles = $A();
    this.stats = stats;
    this.height = 9;
    this.width = 9;
    this.size = 81;
    for (var r = 0; r < this.height; ++r)
      for (var c = 0; c < this.width; ++c)
	this.tiles[r * this.width + c] = new Tile(r, c);
    Event.observe(document, "keypress", this.onKeyPress.bind(this));
    $("selector").observe("mouseout", this.onMouseOutSelector.bind(this));
  },

  construct: function() {
    this.tiles.each(function(t) {
		      t.construct();
		    });
  },

  reset: function() {
    this.tiles.each(function(t) {
		      t.reset();
		    });
    this.assigned = 0;
    this.id = 0;
    this.activeTile = null;
    this.selecting = false;
  },

  generate: function() {
    if (puzzlesource &&
	puzzlesource.has(this.stats.level)) {
      if (!game.online) game.goOnline();
      var data = puzzlesource.get(this.stats.level);
      var x = data.split("|");
      this.id = parseInt(x[0]);
      var str = x[1];
      for (var i = 0; i < str.length; ++i) {
	var c = str.charAt(i);
	this.tiles[i].reset();
	if (c != ".")
	  this.tiles[i].give(str.charCodeAt(i) - '0'.charCodeAt(0));
      }
    }
    else {
      if (game.online) game.goOffline();
      for (var i = 0; i < 30; ++i) {
	var r = Math.floor(this.height * Math.random());
	var c = Math.floor(this.width * Math.random());
	var t = this.get(r, c);
	if (t.assigned) --i;
	else t.give(Math.floor(10 * Math.random()));
      }
    }
    this.tiles.each(function(t) {
		      t.generate();
		    });
  },

  get: function(r, c) {
    if (c < 0 || r < 0 ||
	c >= this.width || r >= this.height)
      return null;
    return this.tiles[r * this.width + c];
  },

  createElement: function() {
    var div = $("field");
    div.setStyle({width: this.width * this.tileWidth,
    		     height: this.height * this.tileHeight});
    this.tiles.each(function(t) {
		      div.appendChild(t.createElement());
		    });
    if ($F("showmarkup")) {
      this.tiles.each(function(t) {
			var elms = t.createMarkupElements();
			elms.each(function(e) {
				    div.appendChild(e);
				  });
		      });
    }
    this.div = div;
    return div;
  },

  removeElements: function() {
    var div = $("field");
    div.innerHTML = "";
  },

  onAssign: function(t) {
    reportAssign(t.r, t.c, t.n == null ? 0 : t.n, !game.started);
    if (!game.started) game.start();
    ++this.assigned;
    if (game.running && this.assigned == this.width * this.height)
      game.gameOver(true); // win
  },

  findTileInRow: function(r, n) {
    for (var c = 0; c < 9; ++c) {
      var t = this.get(r, c);
      if (t.assigned && t.n == n)
	return t;
    }
    return null;
  },

  findTileInCol: function(c, n) {
    for (var r = 0; r < 9; ++r) {
      var t = this.get(r, c);
      if (t.assigned && t.n == n)
	return t;
    }
    return null;
  },

  findTileInRegion: function(region, n) {
    var rs = 3 * parseInt(region / 3);
    var cs = 3 * (region % 3);
    for (var r = rs; r < rs + 3; ++r)
      for (var c = cs; c < cs + 3; ++c) {
	var t = this.get(r, c);
	if (t.assigned && t.n == n)
	  return t;
      }
    return null;
  },

  onStateChange: function(t, from, to) {
    if (from == t.FLAGGED)
      this.flagged--;
    else if (from == t.MARKED)
      this.marked--;
    else if (from == t.REVEALED)
      this.revealed--; // can't happen?
    if (to == t.REVEALED) {
      this.revealed++;
      if (!t.mine)
	this.onSweep(t);
      else if (game.running)
	game.gameOver(false);
      else if (!game.started)
	game.regenerate(t);
    }
    else if (to == t.FLAGGED)
      this.flagged++;
    else if (to == t.MARKED)
      this.marked++;
    game.updateStatusBar();
  },

  onKeyPress: function(evt) {
    if (this.activeTile &&
	evt.charCode >= '1'.charCodeAt(0) && evt.charCode <= '9'.charCodeAt(0))
      this.activeTile.assign(evt.charCode - '0'.charCodeAt(0));
    else if (this.activeTile &&
	     (evt.keyCode == Event.KEY_BACKSPACE ||
	      evt.keyCode == Event.KEY_RETURN ||
	      evt.keyCode == Event.KEY_DELETE ||
	      evt.charCode == ' '.charCodeAt(0)))
      this.activeTile.unassign();
  },

  select: function(n) {
    if (n == 0) this.activeTile.unassign();
    else if (n >= 1 && n <= 9) this.activeTile.assign(n);
    this.closeSelector();
  },

  onMouseOutSelector: function(evt) {
    var sel = $("selector");
    var pos = Position.cumulativeOffset(sel);
    var x = Event.pointerX(evt);
    var y = Event.pointerY(evt);
    if (x < pos[0] || y < pos[1] ||
	x > pos[0] + sel.getWidth() ||
	y > pos[1] + sel.getHeight())
      this.closeSelector();
  },

  closeSelector: function() {
    $("selector").setStyle({visibility: "hidden"});
    this.selecting = false;
  },

  showSelector: function(t, evt) {
    var pos = Position.cumulativeOffset(t.div);
    var sel = $("selector");
    $("unassign").setStyle({display: (t.assigned ? "inline" : "none")});
    sel.setStyle({visibility: "visible",
		     top: Event.pointerY(evt) - 8,
		     left: pos[0] + parseInt(field.tileWidth / 2 - sel.getWidth() / 2)});
    this.activateTile(t);
    this.selecting = true;
  },

  activateTile: function(t) {
    if (this.selecting) this.closeSelector();
    if (this.activeTile && this.activeTile != t)
      this.deactivateTile(this.activeTile);
    if (!t.given) t.setBorderColor("red");
    this.activeTile = t;
  },

  deactivateTile: function(t) {
    if (this.activeTile == t && !this.selecting) {
      if (!t.given) t.setBorderColor("black");
      this.activeTile = null;
    }
  },

  showMarkup: function() {
    this.tiles.each(function(t) {
		      var elms = t.createMarkupElements();
		      elms.each(function(e) {
				  field.div.appendChild(e);
				});
		    });
  },

  hideMarkup: function() {
    this.tiles.each(function(t) {
		      t.deleteMarkupElements();
		    });
    var elms = document.getElementsByClassName("markup");
    elms.each(function(t) {
		// delete it
	      });
  }
};

