Tile = function(pHash){
	// user set constants
	this.maxZoom = 19;
	// ...Constants...
	this.tileSize = 256;
	this.pixelsPerLonDegree = [];
	this.pixelsPerLonRadian = [];
	this.numTiles = [];
	this.bitmapOrigo = [];
	// Note: These variable names are based on the variables names found in the
	//       Google maps.*.js code.
	this.c = 256;
	this.bc;
	this.Wa;
		
	this.fillInConstants();
}

Tile.prototype = {
	// ...Public...
	'getTileCoords': function(lat, lng, zoom) {
		return this.getTileCoordinate(lat, lng, zoom);
	},

	'getTileLatLong': function(lat, lng, zoom) {
		return this.getLatLong(lat, lng, zoom);
	},

	// ...Private...
	// Fill in the constants array
	'fillInConstants': function() {
		this.bc = 2*Math.PI;
		this.Wa = Math.PI/180;
	
		for(z = 1; z < this.maxZoom+1; z++) {
  			this.pixelsPerLonDegree[z] = this.c / 360;
  			this.pixelsPerLonRadian[z] = this.c / this.bc;
  			e = this.c / 2;
  			this.bitmapOrigo[z] = this.p(e,e);
  			this.numTiles[z] = this.c / 256;
  			this.c *= 2;
		}
	},

	'getBitmapCoordinate': function(a, b, c) {
  		ret = this.p(0,0);
  		ret.x = (this.bitmapOrigo[c].x + b * this.pixelsPerLonDegree[c]);
  		e = Math.sin(a * this.Wa);
  		if(e > 0.9999) {
    		e = 0.9999;
  		}
  		if(e < -0.9999) {
    		e = -0.9999;
  		}
  		ret.y = (this.bitmapOrigo[c].y + 0.5 * Math.log((1 + e) / (1 - e)) * -1*(this.pixelsPerLonRadian[c]));
  		return ret;
	},

	'getTileCoordinate': function(a, b, c, maximize) {
  		ret = this.getBitmapCoordinate(a, b, c);
  		
  		if(maximize  == true) {
	  		ret.x = Math.celling(2 * (ret.x / this.tileSize));
  			ret.y = Math.ceiling(2 * (ret.y / this.tileSize));
		} else {
			ret.x = Math.floor(2 * (ret.x / this.tileSize));
  			ret.y = Math.floor(2 * (ret.y / this.tileSize));
		}
  		return ret;
	},

	'getLatLong': function(a, b, c) {
		ret = this.p(0, 0);
		e = this.getBitmapCoordinate(a, b, c);
  		a = e.x;
		b = e.y;

		ret.x = (a - this.bitmapOrigo[c].x) / this.pixelsPerLonDegree[c];
		e = (b - this.bitmapOrigo[c].y) / (-1*this.pixelsPerLonRadian[c]);
		ret.y = (2 * Math.atan(Math.exp(e)) - Math.PI / 2) / this.Wa;
		return ret;
	},

	'p': function(x,y){
		return {x:x,y:y};
	},
	
	'getKeyholeString': function(a, b, c) {
		s = "";
		myX = a;
		myY = b;
		for(i = 17; i > c; i--) {
			rx = (fmod(myX, 2));
			myX = Math.floor(myX / 2);
			ry = (fmod(myY, 2));
			myY = Math.floor(myY / 2);
			s = this.getKeyholeDirection(rx, ry).s;
		}
		return 't'.s;
	},

	'getKeyholeDirection': function(x, y) {
		if(x == 1) {
			if(y == 1) {
				return 's';
			} else if(y == 0) {
				return 'r';
			}
		} else if(x == 0) {
			if(y == 1) {
				return 't';
			} else if(y == 0) {
				return 'q';
			}
		}
		return '';
	}
}
