var corrections = [];
corrections.push([]);
corrections.push([]);
corrections.push([]);
corrections.push([]);

var angles = [];
angles.push([]);
angles.push([]);
angles.push([]);
angles.push([]);

for(var i=0; i<4; i++) {
	var baseAngle = i * 90;
	for(var j=0; j<320; j++) {
		var angle = baseAngle - (0.1875 * j - 30);
		angle *= 0.0174532925; // Convert to Radians
		angles[i].push({
			angle : angle,
			x : Math.cos(angle),
			y : Math.sin(angle)
		})
		corrections[i].push(Math.cos((0.1875 * j - 30) * 0.0174532925));
	}
}

var spellEffect = "";

function Map(sourceMap) {
	delete map;
	var map = {
		groundColor : sourceMap.groundColor,
		ceilingColor : sourceMap.ceilingColor,
		backdrop : sourceMap.backdrop,
		width : sourceMap.width,
		height : sourceMap.height,
		tiles : sourceMap.tiles,
		discoveredMap : sourceMap.discoveredMap,
		map : [],
		entities : [],
		scenery : []
	}
	
	for(var i=0; i<sourceMap.map.length; i++) {
		map.map.push([]);
		for(var j=0; j<sourceMap.map[i].length; j++) {
			map.map[i].push(sourceMap.map[i][j]);
		}
	}
	
	for(var i=0; i<sourceMap.entities.length; i++) {
		var entity = {};
		var srcEntity = sourceMap.entities[i];
		for(key in srcEntity) {
			entity[key] = srcEntity[key];
		}
		map.entities.push(entity);
	}
	
	for(var i=0; i<sourceMap.scenery.length; i++) {
		map.scenery.push(sourceMap.scenery[i]);
	}
	
	this.map = map;
	
	var gridWidth = 110 / map.width;
	var gridHeight = 110 / map.height;
	
	var discoveredMap = [];
	if(map.discoveredMap) {
		discoveredMap = map.discoveredMap;
	} else {
		for(var y=0; y<map.height; y++) {
			var row = [];
			discoveredMap.push(row);
			for(var x=0; x<map.width; x++) {
				row.push(0);
			}
		}
		
		map.discoveredMap = discoveredMap;
		sourceMap.discoveredMap = discoveredMap;
	}
	this.discoveredMap = discoveredMap;	
	
	var depthBuffer = [];
	var spriteList = [];
	var resolution = vars.resolution;
	
	var tileList = [];
	for(var i=0; i<map.tiles.length; i++) {
		tileList.push(imageObjects["images/tiles/" + map.tiles[i] + ".jpg"]);
	}
	
	this.thinkers = [];
	
	var player;
	for(var i=0; i<map.entities.length; i++) {
		var entity = map.entities[i];
		if(entity.type == "player") {
			player = entity;
		}
		
		if(entities[entity.type]) {
			for(var key in entities[entity.type]) {
				entity[key] = entities[entity.type][key];
			}
		}
	}
	
	for(var i=0; i<map.entities.length; i++) {
		var entity = map.entities[i];
		if(entity.onLoad) {
			entity.onLoad(this);
		}
		
		if(entity.think) {
			this.thinkers.push(i);
		}
	}
	
	/* This little hack is necessary for some reason under version 1.1.1 of the
	   iPhone firmware. Without it, tiles might not render all the way until
	   they've been rendered in a large size.
	*/
	for(var i=0; i<tileList.length; i++) {
		background.drawImage(tileList[i], 0, 0, 128, 128, 0, 0, 128, 128);
	}
	
	this.getPlayer = function() {
		return player;
	}
	
	this.blocked = function(x, y) {
		if(map.map[y][x] > 0) {
			return true;
		}
		
		var i, entity;
		for(i=0; i<map.entities.length; i++) {
			entity = map.entities[i];
			if(entity.x == x && entity.y == y) {
				if(entity.solid == false) {
					continue;
				}
				return true;
			}
		}
		return false;
	}
	
	this.renderMap = function() {
		minimapTop.clearRect(0, 0, 110, 110);
		minimapTop.fillStyle = "yellow";
		minimapTop.fillRect(player.x * gridWidth + 1, player.y * gridHeight + 1, gridWidth - 2, gridHeight - 2);
		
		minimapTop.fillStyle = "red";
		var entity, distance;
		for(var i=0; i<map.entities.length; i++) {
			entity = map.entities[i];
			if(!entity.attackable || !entity.alive) {
				continue;
			}
			var distanceX = Math.abs(entity.x - player.x);
			var distanceY = Math.abs(entity.y - player.y);
			distance = distanceX + distanceY;
			if(distance <= 5 && distanceX <= 4 && distanceY <= 4) {
				minimapTop.fillRect(entity.x * gridWidth + 1, entity.y * gridHeight + 1, gridWidth - 2, gridHeight - 2);
			}
		}
	}
	
	this.renderMiniMap = function() {
		minimap.clearRect(0, 0, 110, 110);
		var x, y;
		for(y=0; y<discoveredMap.length; y++) {
			for(x=0; x<discoveredMap[y].length; x++) {
				if(discoveredMap[y][x] > 0) {
					if(discoveredMap[y][x] == 1) {
						minimap.fillStyle = "white";
					} else if(discoveredMap[y][x] == 2) {
						minimap.fillStyle = "blue";
					} else if(discoveredMap[y][x] == 3) {
						minimap.fillStyle = "purple";
					}
					minimap.fillRect(x * gridWidth, y * gridHeight, gridWidth, gridHeight);
				}
			}
		}
	}
	
	this.sortObjects = function(list) {
		var x, y, temp;
		for(x=0; x<list.length; x++) {
			for(y=0; y<list.length - 1; y++) {
				if(list[y].distance < list[y + 1].distance) {
					temp = list[y + 1];
					list[y + 1] = list[y];
					list[y] = temp;
				}
			}
		}
	}
	
	this.renderSprites = function() {
		canvas.clearRect(0, 0, 320, 256);
		
		var i, sprite;
		for(i=0; i<spriteList.length; i++) {
			sprite = spriteList[i];
			var offset = 0;
			if(sprite.owner) {
				if(sprite.owner.imageOffset) {
					offset = sprite.owner.imageOffset;
				}
			}
			canvas.drawImage(sprite.image, sprite.sourceX + offset, 0, sprite.width, 128, sprite.destinationX, sprite.destinationY, sprite.destinationWidth, sprite.destinationHeight);
		}
		
		if(spellEffect != "") {
			var img = imageObjects[spellEffect]
			canvas.drawImage(img, (320 - img.width) / 2, (256 - img.width) / 2);
		}
		
		if(vars.weaponImage) {
			var weaponImage =  imageObjects["images/sprites/" + vars.weaponImage + ".png"];
			canvas.drawImage(weaponImage, weaponOffset, 0, 128, 128, 192, 128, 128, 128);
		}
		
		if(map.groundColor == "#664433" && ! vars.mok) {
			canvas.fillStyle = "rgba(255, 64, 0, 0.25)";
			canvas.fillRect(0, 0, 320, 256);
		}
		canvas.drawImage(imageObjects["images/sprites/compass.png"], player.direction * 64, 0, 64, 64, 8, 184, 64, 64);
	}
	
	this.render = function() {
		background.fillStyle = map.groundColor;
		background.fillRect(0, 128, 320, 128);
		
		var baseAngle;
		var backdropOffset = 0;
		
		var direction = player.direction;
		
		if(direction == 1) {
			backdropOffset = 960;
			baseAngle = 90;
		} else if(direction == 2) {
			backdropOffset = 640;
			baseAngle = 180;
		} else if(direction == 3) {
			backdropOffset = 320;
			baseAngle = 270;
		} else {
			baseAngle = 0;
		}
		
		if(map.backdrop) {
			background.drawImage(imageObjects["images/backdrops/" + map.backdrop + ".png"], -backdropOffset, 0);
		} else {
			background.fillStyle = map.ceilingColor;
			background.fillRect(0, 0, 320, 128);
		}
		
		var i, j;
		var msl = map.scenery.length;
		var obj, spriteX, spriteY, spriteZ, playerX, playerY, sx, sy;
		var spriteDistance, distance, spriteXOffset, spriteSize, halfSpriteSize, img;
		var spriteHeight;
		
		var entity, distance, texX, overlap, wallSize;
		var sceneryList = [];
		for(i=0; i<msl; i++) {
			obj = map.scenery[i];
			spriteX = obj.x + 0.5;
			spriteY = obj.y + 0.5;
			spriteZ = 1;

			playerX = player.x + 0.5;
			playerY = player.y + 0.5;

			sx = spriteX - playerX;
			sy = spriteY - playerY;
			spriteDistance = Math.sqrt(sx * sx + sy * sy);
			distance = 1/spriteDistance;
			spriteXOffset = 0;
			if(baseAngle == 90) {
				if(obj.y > player.y) {
					continue;
				}
				spriteXOffset = playerX - spriteX;
			} else if(baseAngle == 270) {
				if(obj.y < player.y) {
					continue;
				}
				spriteXOffset = spriteX - playerX;
			} else if(baseAngle == 0) {
				if(obj.x < player.x) {
					continue;
				}
				spriteXOffset = playerY - spriteY;
			} else {
				if(obj.x > player.x) {
					continue;
				}
				spriteXOffset = spriteY - playerY;
			}
			spriteSize = 256 * distance;
			halfSpriteSize = 128 * distance;
			img = imageObjects["images/sprites/" + obj.image + ".png"];
			spriteHeight = img.height * 2 * distance;
			sceneryList.push({
				distance : spriteDistance,
				spriteDistance : distance,
				image : img,
				halfSpriteSize : halfSpriteSize,
				spriteXOffset : spriteXOffset,
				spriteZ : spriteZ,
				spriteSize : spriteSize,
				spriteHeight : spriteHeight
			});
		}
		
		this.sortObjects(sceneryList);
		var scene;
		for(i=0; i<sceneryList.length; i++) {
			scene = sceneryList[i];
			spriteDistance = scene.spriteDistance;
			img = scene.image;
			halfSpriteSize = scene.halfSpriteSize;
			spriteXOffset = scene.spriteXOffset;
			spriteZ = scene.spriteZ;
			spriteSize = scene.spriteSize;
			spriteHeight = scene.spriteHeight;
			background.drawImage(img, 
				160 - halfSpriteSize - spriteXOffset * 297 * spriteDistance, 
				128 - halfSpriteSize - spriteZ * spriteHeight, 
				spriteSize, 
				spriteHeight);
		}
		
		delete depthBuffer;
		depthBuffer = [];
		
		var angleObject, angle, rayX, rayX, rayDirX, rayDirY, mapX, mapY, side;
		var deltaDistX, deltaDistY, sideDistX, sideDistY, stepX, stepY, hit;
		var playerAngle = angles[direction];
		var correctionArray = corrections[direction];
		for(i=0; i<320; i += resolution) {
			angleObject = playerAngle[i];
			angle = angleObject.angle;
			rayX = player.x + 0.5;
			rayY = player.y + 0.5;
			rayDirX = angleObject.x;
			rayDirY = angleObject.y;
			
			mapX = player.x;
			mapY = player.y;
			
			deltaDistX = Math.abs(1 / rayDirX);
			deltaDistY = Math.abs(1 / rayDirY);
			
			sideDistX = 0.5 * deltaDistX;
			sideDistY = 0.5 * deltaDistY;
			
			hit = 0;
			
			if(rayDirX < 0) {
				stepX = -1;
			} else {
				stepX = 1;
			}
			
			if(rayDirY < 0) {
				stepY = 1;
			} else {
				stepY = -1;
			}
			
			while(true) {
				if(sideDistX < sideDistY) {
					mapX += stepX;
					side = 0;
					if(map.map[mapY][mapX] > 0) {
						break;
					}
					sideDistX += deltaDistX;
				} else {
					mapY += stepY;
					side = 1;
					if(map.map[mapY][mapX] > 0) {
						break;
					}
					sideDistY += deltaDistY;
				}
			}
			
			if(discoveredMap[mapY]) {
				if(discoveredMap[mapY][mapX] == 0) {
					minimap.fillStyle = "white";
					discoveredMap[mapY][mapX] = 1;
					for(j=0; j<map.entities.length; j++) {
						entity = map.entities[j];
						if(entity.type == "door" && entity.x == mapX && entity.y == mapY && entity.secretDoor != true) {
							minimap.fillStyle = "blue";
							discoveredMap[mapY][mapX] = 2;
						} else if(entity.type == "portal" && entity.x == mapX && entity.y == mapY && entity.secretDoor != true) {
							minimap.fillStyle = "purple";
							discoveredMap[mapY][mapX] = 3;
						}
					}

					minimap.fillRect(mapX * gridWidth, mapY * gridHeight, gridWidth, gridHeight);
				}
			}
			
			if(side == 0) {
				distance = sideDistX;
			} else {
				distance = sideDistY;
			}
			
			if(side == 0) {
				texX = rayDirY * distance - 0.5;
				texX -= Math.floor(texX);
				texX *= 128;
				
				if(rayDirX > 0) {
					texX = 128 - texX;
				}
			} else {
				texX = rayDirX * distance - 0.5;
				texX -= Math.floor(texX);
				texX *= 128;
				
				if(rayDirY < 0) {
					texX = 128 - texX;
				}
			}
			
			texX = Math.floor(texX);
			overlap = distance;
			
			distance *= correctionArray[i];
			for(j=0; j<resolution; j++) {
				depthBuffer.push(distance);
			}
			
			distance = 1 / distance;
			
			if(resolution == 1) {
				overlap = 1;
			} else {
				if(direction == 1 || direction == 3) {
					if(side == 0) {
						overlap *= resolution;
					} else {
						overlap *= resolution * 0.5;
					}
				} else {
					if(side == 1) {
						overlap *= resolution;
					} else {
						overlap *= resolution * 0.5;
					}
				}
				if(texX + overlap > 128) {
					overlap = 128 - texX;
					if(overlap == 1) {
						texX = 0;
						overlap = (1 / distance) * (resolution * 0.5);
					}
				}
				
				overlap = Math.round(overlap);
				if(overlap < 1) {
					overlap = 1;
				}
			}
			
			wallSize = 297 * distance;
			background.drawImage(tileList[map.map[mapY][mapX] - 1], texX, 0, overlap, 128, i, (256 - wallSize) * 0.5, resolution, wallSize);
			if(map.backdrop) {
				if(side == 0) {
					if(rayDirX > 0) {
						background.fillStyle = "rgba(0, 0, 24, 0.33)";
						background.fillRect(i, (256 - wallSize) * 0.5, resolution, wallSize);
					}
				} else {
					background.fillStyle = "rgba(0, 0, 0, 0.15)";
					background.fillRect(i, (256 - wallSize) * 0.5, resolution, wallSize);
				}
			} else if(side == 0) {
				background.fillStyle = "rgba(0, 0, 0, 0.33)";
				background.fillRect(i, (256 - wallSize) * 0.5, resolution, wallSize);
			}
		}
		
		delete spriteList;
		spriteList = [];
		var spriteX, spriteY, spriteZ, playerX, playerY, spriteXOffset;
		var sx, sy, spriteDistance, origSpriteDistance, spriteSize;
		var img, imgX, imgY, imgWidth, imgHeight, imgOffset, rightImgOffset;
		var srcX, srcWidth;
		for(i=0; i<map.entities.length; i++) {
			obj = map.entities[i];
			if(!obj.image) {
				continue;
			}
			spriteX = obj.x + 0.5;
			spriteY = obj.y + 0.5;
			spriteZ = 0;

			playerX = player.x + 0.5;
			playerY = player.y + 0.5;

			spriteXOffset = 0;
			if(baseAngle == 90) {
				if(obj.y > player.y) {
					continue;
				}
				spriteXOffset = playerX - spriteX;
			} else if(baseAngle == 270) {
				if(obj.y < player.y) {
					continue;
				}
				spriteXOffset = spriteX - playerX;
			} else if(baseAngle == 0) {
				if(obj.x < player.x) {
					continue;
				}
				spriteXOffset = playerY - spriteY;
			} else {
				if(obj.x > player.x) {
					continue;
				}
				spriteXOffset = spriteY - playerY;
			}
			
			sx = spriteX - playerX;
			sy = spriteY - playerY;
			spriteDistance = Math.sqrt(sx * sx + sy * sy);
			if(spriteDistance == 0) {
				continue;
			}
			origSpriteDistance = spriteDistance;
			spriteDistance = 1/spriteDistance;
			
			spriteSize = Math.round(256 * spriteDistance);
			
			img = imageObjects["images/sprites/" + obj.image + ".png"];
			imgX = Math.round(160 - 128 * spriteDistance - spriteXOffset * 297 * spriteDistance);
			if(imgX > 320) {
				continue;
			}
			imgY = 128 - 108 * spriteDistance - spriteZ * spriteSize;
			imgWidth = spriteSize;
			imgHeight = spriteSize;
			
			spriteDistance = origSpriteDistance;
			
			imgOffset = 0;
			
			if(imgX < 0) {
				imgOffset = 0 - imgX;
			}
			
			for(j=imgX + imgOffset; j<imgX + imgWidth; j++) {
				if(j == 320) {
					imgOffset = imgWidth;
					break;
				}
				
				if(depthBuffer[j] < spriteDistance) {
					imgOffset++;
				} else {
					break;
				}
			}
			
			if(imgOffset >= imgWidth) {
				continue;
			}
			
			rightImgOffset = 0;
			
			if(imgX + imgWidth - 1 >= 320) {
				rightImgOffset = (imgX + imgWidth - 1) - 319;
			}
			
			for(j=imgX + imgWidth - 1 - rightImgOffset; j>=imgX; j--) {
				if(depthBuffer[j] < spriteDistance) {
					rightImgOffset++;
				} else {
					break;
				}
			}
			
			srcX = (imgOffset / imgWidth) * 128;
			srcWidth = 128 - ((imgOffset + rightImgOffset) / imgWidth) * 128;
			
			spriteList.push({
				owner : obj,
				distance : spriteDistance,
				image : img,
				sourceX : srcX,
				width : srcWidth,
				destinationX : imgX + imgOffset,
				destinationY : imgY,
				destinationWidth : imgWidth - imgOffset - rightImgOffset,
				destinationHeight : imgHeight
			});
		}
		
		this.sortObjects(spriteList);
		this.renderSprites();
		this.renderMap();
	}
}