/*
ASCII Pachinko (c) 2004 by Lee W. Fastenau
Feel free to use and/or modify this code as long as you don't make money from it.
This attribution must stay.  Stay, it must.  Yes.
*/

Board = function (elem) {
	this.elem = elem;
	this.source = new Array();
	this.display = new Array();
	this.balls = new Array();
	this.w = 0;
	this.h = 0;
	this.timer = 0;
	
	this.initSource();
	this.initDisplay();
	this.updateDisplay();
}

Board.sideWall = new RegExp('[|\\\\/@<>]');
Board.flr = new RegExp('[_]');
Board.subFlr = new RegExp('[|@-]');
Board.leftRamp = new RegExp('[/<]');
Board.rightRamp = new RegExp('[\\\\>]');
Board.splitter = new RegExp('[A^.]');
Board.lift = new RegExp('[~]');
Board.ballKiller = new RegExp('[XOU]');
Board.wall = '@';

Board.prototype.setChar = function (character,x,y) {
	if (x>=0 && y>=0 && x<this.w && y<this.h)
		this.display[y] = this.display[y].substr(0,x) + character + this.display[y].substr(x+1);
}

Board.prototype.getChar = function (x,y) {
	if (x>=0 && y>=0 && x<this.w && y<this.h)
		return (this.source[y].charAt(x));
	else
		return (Board.wall);
}

Board.prototype.initSource = function () {
	this.source = this.elem.firstChild.nodeValue.split('\r');
	if (this.source.length==1) {
		this.source = this.source[0].split('\n');
	}
	this.h = this.source.length
	for (row in this.source) {
		this.w = this.source[row].length>this.w ? this.source[row].length : this.w;
	}
	for (row in this.source) {
		this.source[row] += new Array (this.w-this.source[row].length+1).join(' ');
	}
}

Board.prototype.addBall = function (character,x,y,mx) {
	this.balls.push(new Ball(character,x,y,mx,this));
}

Board.prototype.initDisplay = function () {
	this.display = this.source.concat();
}

Board.prototype.updateDisplay = function () {
	this.elem.firstChild.nodeValue = this.display.join('\r\n');
}

Board.prototype.animate = function () {
	if (++this.timer>3) {
		this.initDisplay();
		this.updateBalls();
		this.updateDisplay();
		this.timer=0;
	}
}

Board.prototype.updateBalls = function () {
	this.ballCount=0;
	for (ball in this.balls) {
		this.ballCount++;
		if (this.balls[ball].update()) {
			delete this.balls[ball];
		}
	}
}

Board.prototype.start = function (ms) {
	this.callwrapper = new CCallWrapper(this, ms, 'animate');
	CCallWrapper.asyncExecute(this.callwrapper);
}

Ball = function (character,x,y,mx,brd) {
	this.character = character.charAt(0);
	this.x = x<0 ? 0 : (x>brd.w-1 ? brd.w-1 : x);
	this.y = y<0 ? 0 : (y>brd.h-1 ? brd.h-1 : y);
	this.mx = mx;
	this.brd = brd;
}

Ball.prototype.update = function () {
	this.brd.setChar(this.character,this.x,this.y);
	if (this.brd.getChar(this.x,this.y).match(Board.flr)
		|| this.brd.getChar(this.x,this.y+1).match(Board.subFlr)) {
		if (this.brd.getChar(this.x+this.mx,this.y).match(Board.sideWall)) {
			this.mx=-this.mx;
		}
		if (!this.brd.getChar(this.x+this.mx,this.y).match(Board.sideWall)) {
			this.x+=this.mx;
		}
	} else {
		this.y++;
		if (this.brd.getChar(this.x,this.y).match(Board.leftRamp)) {
			this.mx = -1;
			this.x += this.mx;
		} else if (this.brd.getChar(this.x,this.y).match(Board.rightRamp)) {
			this.mx = +1;
			this.x += this.mx;
		} else if (this.brd.getChar(this.x,this.y).match(Board.splitter)) {
			this.mx = Math.floor(Math.random()*2)*2-1;
			this.x += this.mx;
		}
	}
	if (this.brd.getChar(this.x,this.y).match(Board.lift)) {
		this.y--;
	}
	if (this.brd.getChar(this.x,this.y).match(Board.ballKiller)) {
		return (true);
	}
}