"use strict";
(function (window, undefined) {
	var hasClass = System.hasClass,
		addClass = System.addClass,
		removeClass = System.removeClass,
		jsApiRequest = System.jsApiRequest,

		world = document.getElementById('world'),
		robot,
		floor = [],
		floorShadow = [],
		code = [[], [], []],
		INSTRUCTION_CLASSES = ['none', 'go', 'jump', 'left', 'right', 'switch', 'func1', 'func2'];

	window['Draw'] = {
		animTimeout:null,

		rotation:0,

		ANIM:{
			INVALID:-1,
			NONE:0,
			YP:1,
			XP:2,
			YN:3,
			XN:4,
			YP_JUMP:5,
			XP_JUMP:6,
			YN_JUMP:7,
			XN_JUMP:8,
			JUMP:9,
			ROTATE_03:10,
			ROTATE_10:11,
			ROTATE_21:12,
			ROTATE_32:13,
			ROTATE_01:14,
			ROTATE_12:15,
			ROTATE_23:16,
			ROTATE_30:17,
			SWITCH_OFF:18,
			SWITCH_ON:19
		},

		initLevel:function () {
			var elem, pos, x, y;

			world.innerHTML = '';

			switch (Draw.rotation) {
				case 0:
					x = -transformCoords(Level.width * 64, 0).x - transformCoords(0, Level.height * 64).x;
					y = -transformCoords(Level.width * 64, Level.height * 64).y;
					break;
				case 90:
					x = transformCoords(Level.height * 64, 0).x - transformCoords(0, Level.width * 64).x;
					y = -transformCoords(Level.height * 64, 0).y + transformCoords(0, Level.width * 64).y;
					break;
				case 180:
					x = -transformCoords(Level.width * 64, 0).x - transformCoords(0, Level.height * 64).x;
					y = -transformCoords(0, 0).y + transformCoords(Level.width * 64, Level.height * 64).y;
					break;
				case 270:
					x = -transformCoords(Level.height * 64, 0).x + transformCoords(0, Level.width * 64).x;
					y = transformCoords(Level.height * 64, 0).y - transformCoords(0, Level.width * 64).y;
					break;
			}

			world.style.marginLeft = Math.round(x / 2 - 47 - 124) + 'px';
			world.style.marginTop = Math.round(y / 2 - 47) + 'px';

			floor = new Array(Level.height);
			floorShadow = new Array(Level.height);

			for (y = 0; y < Level.height; y++) {
				floor[y] = new Array(Level.width);
				floorShadow[y] = new Array(Level.width);

				for (x = 0; x < Level.width; x++) {
					elem = document.createElement('div');
					pos = transformCoords(x << 6, y << 6);
					elem.style.left = pos.x + 'px';
					elem.style.top = pos.y + 'px';
					elem.style.zIndex = 30000 - zIndex(x, y);
					floor[y][x] = world.appendChild(elem);

					elem = document.createElement('div');
					pos = transformCoords(x << 6, y << 6);
					elem.style.left = pos.x + 'px';
					elem.style.top = pos.y + 'px';
					elem.style.zIndex = 40000 - zIndex(x, y);
					floorShadow[y][x] = world.appendChild(elem);
				}
			}

			robot = document.createElement('div');
			robot.className = 'robot';
			world.appendChild(robot);
		},
		resetLevel:function () {
			var x, y;

			Game.robot.x = Level.x;
			Game.robot.y = Level.y;
			Game.robot.dir = Level.dir;

			for (y = 0; y < Level.height; y++) {
				for (x = 0; x < Level.width; x++) {
					switch (Level.getFloor(x, y)) {
						case '.':
							floor[y][x].className = 'floorNone';
							floor[y][x].style.zIndex = 30000 - zIndex(x, y);
							floorShadow[y][x].className = 'floorNoneshadow';
							break;

						case '0':
							floor[y][x].className = 'floor0';
							floor[y][x].style.zIndex = 30000 - zIndex(x, y);
							floorShadow[y][x].className = 'floor0shadow';
							break;
						case '1':
							floor[y][x].className = 'floor1';
							floor[y][x].style.zIndex = 50000 - zIndex(x, y);
							floorShadow[y][x].className = 'floor1shadow';
							break;
						case '2':
							floor[y][x].className = 'floor2';
							floor[y][x].style.zIndex = 50000 - zIndex(x, y);
							floorShadow[y][x].className = 'floor2shadow';
							break;
						case '3':
							floor[y][x].className = 'floor3';
							floor[y][x].style.zIndex = 50000 - zIndex(x, y);
							floorShadow[y][x].className = 'floor3shadow';
							break;

						case 'a':
							floor[y][x].className = 'floor0 off';
							floor[y][x].style.zIndex = 30006 - zIndex(x, y);
							floorShadow[y][x].className = 'floor0shadow';
							break;
						case 'b':
							floor[y][x].className = 'floor1 off';
							floor[y][x].style.zIndex = 50000 - zIndex(x, y);
							floorShadow[y][x].className = 'floor1shadow';
							break;
						case 'c':
							floor[y][x].className = 'floor2 off';
							floor[y][x].style.zIndex = 50000 - zIndex(x, y);
							floorShadow[y][x].className = 'floor2shadow';
							break;
						case 'd':
							floor[y][x].className = 'floor3 off';
							floor[y][x].style.zIndex = 50000 - zIndex(x, y);
							floorShadow[y][x].className = 'floor3shadow';
							break;

						case 'A':
							floor[y][x].className = 'floor0 on';
							floor[y][x].style.zIndex = 49995 - zIndex(x, y);
							floorShadow[y][x].className = 'floor0shadow';
							break;
						case 'B':
							floor[y][x].className = 'floor1 on';
							floor[y][x].style.zIndex = 50000 - zIndex(x, y);
							floorShadow[y][x].className = 'floor1shadow';
							break;
						case 'C':
							floor[y][x].className = 'floor2 on';
							floor[y][x].style.zIndex = 50000 - zIndex(x, y);
							floorShadow[y][x].className = 'floor2shadow';
							break;
						case 'D':
							floor[y][x].className = 'floor3 on';
							floor[y][x].style.zIndex = 50000 - zIndex(x, y);
							floorShadow[y][x].className = 'floor3shadow';
							break;

						default:
							floor[y][x].className = 'floor0';
							floorShadow[y][x].className = 'floor0shadow';
							break;
					}
				}
			}

			Draw.setRobotPosition();
		},

		animate:function (anim, zOffset, frame) {
			frame = frame || 0;

			Draw.animTimeout = setTimeout(function () {
				Draw.animTimeout = null;
				switch (anim) {
					case Draw.ANIM.INVALID:
						Game.nextInstruction();
						return;
					case Draw.ANIM.YP:
						switch (frame) {
							case 0: Draw.setRobotPosition(0, 0.1); break;
							case 1: Draw.setRobotPosition(0, 0.2); break;
							case 2: Draw.setRobotPosition(0, 0.3); break;
							case 3: Draw.setRobotPosition(0, 0.4); break;
							case 4: Draw.setRobotPosition(0, 0.5); break;
							case 5: Draw.setRobotPosition(0, 0.6, 0, -1); break;
							case 6: Draw.setRobotPosition(0, 0.7, 0, -1); break;
							case 7: Draw.setRobotPosition(0, 0.8, 0, -1); break;
							case 8: Draw.setRobotPosition(0, 0.9); break;
							case 9:
								Game.robot.y++;
								Draw.setRobotPosition();
								Game.nextInstruction();
								return;
						}
						break;
					case Draw.ANIM.XP:
						switch (frame) {
							case 0: Draw.setRobotPosition(0.1, 0); break;
							case 1: Draw.setRobotPosition(0.2, 0); break;
							case 2: Draw.setRobotPosition(0.3, 0); break;
							case 3: Draw.setRobotPosition(0.4, 0, 0); break;
							case 4: Draw.setRobotPosition(0.5, 0, 0); break;
							case 5: Draw.setRobotPosition(0.6, 0, 0, -1); break;
							case 6: Draw.setRobotPosition(0.7, 0, 0, -1); break;
							case 7: Draw.setRobotPosition(0.8, 0, 0, -1); break;
							case 8: Draw.setRobotPosition(0.9, 0, 0); break;
							case 9:
								Game.robot.x++;
								Draw.setRobotPosition();
								Game.nextInstruction();
								return;
						}
						break;
					case Draw.ANIM.YN:
						switch (frame) {
							case 0: Draw.setRobotPosition(0, -0.1); break;
							case 1: Draw.setRobotPosition(0, -0.2, 0, -1); break;
							case 2: Draw.setRobotPosition(0, -0.3, 0, -1); break;
							case 3: Draw.setRobotPosition(0, -0.4, 0, -1); break;
							case 4: Draw.setRobotPosition(0, -0.5); break;
							case 5: Draw.setRobotPosition(0, -0.6); break;
							case 6: Draw.setRobotPosition(0, -0.7); break;
							case 7: Draw.setRobotPosition(0, -0.8); break;
							case 8: Draw.setRobotPosition(0, -0.9); break;
							case 9:
								Game.robot.y--;
								Draw.setRobotPosition();
								Game.nextInstruction();
								return;
						}
						break;
					case Draw.ANIM.XN:
						switch (frame) {
							case 0: Draw.setRobotPosition(-0.1, 0); break;
							case 1: Draw.setRobotPosition(-0.2, 0, 0, -1); break;
							case 2: Draw.setRobotPosition(-0.3, 0, 0, -1); break;
							case 3: Draw.setRobotPosition(-0.4, 0, 0, -1); break;
							case 4: Draw.setRobotPosition(-0.5, 0); break;
							case 5: Draw.setRobotPosition(-0.6, 0); break;
							case 6: Draw.setRobotPosition(-0.7, 0); break;
							case 7: Draw.setRobotPosition(-0.8, 0); break;
							case 8: Draw.setRobotPosition(-0.9, 0); break;
							case 9:
								Game.robot.x--;
								Draw.setRobotPosition();
								Game.nextInstruction();
								return;
						}
						break;
					case Draw.ANIM.YP_JUMP:
						switch (frame) {
							case 0: Draw.setRobotPosition(0, 0.1, (zOffset === 1 ? 0.6 : 0.4)); break;
							case 1: Draw.setRobotPosition(0, 0.2, (zOffset === 1 ? 1.1 : 0.7)); break;
							case 2: Draw.setRobotPosition(0, 0.3, (zOffset === 1 ? 1.5 : 0.9)); break;
							case 3: Draw.setRobotPosition(0, 0.4, (zOffset === 1 ? 1.8 : 1.0)); break;
							case 4: Draw.setRobotPosition(0, 0.5, (zOffset === 1 ? 2.0 : 0.9)); break;
							case 5: Draw.setRobotPosition(0, 0.6, (zOffset === 1 ? 2.1 : 0.7), -1); break;
							case 6: Draw.setRobotPosition(0, 0.7, (zOffset === 1 ? 2.0 : 0.4), -1); break;
							case 7: Draw.setRobotPosition(0, 0.8, (zOffset === 1 ? 1.8 : 0.0), -1); break;
							case 8: Draw.setRobotPosition(0, 0.9, (zOffset === 1 ? 1.4 : -0.5)); break;
							case 9: if (zOffset < -1) { Draw.setRobotPosition(0, 0.9, -1); break; }
							case 10: if (zOffset < -2) { Draw.setRobotPosition(0, 0.9, -2); break; }
							case 11:
								Game.robot.y++;
								Draw.setRobotPosition();
								Game.nextInstruction();
								return;
						}
						break;
					case Draw.ANIM.XP_JUMP:
						switch (frame) {
							case 0: Draw.setRobotPosition(0.1, 0, (zOffset === 1 ? 0.6 : 0.4)); break;
							case 1: Draw.setRobotPosition(0.2, 0, (zOffset === 1 ? 1.1 : 0.7)); break;
							case 2: Draw.setRobotPosition(0.3, 0, (zOffset === 1 ? 1.5 : 0.9)); break;
							case 3: Draw.setRobotPosition(0.4, 0, (zOffset === 1 ? 1.8 : 1.0)); break;
							case 4: Draw.setRobotPosition(0.5, 0, (zOffset === 1 ? 2.0 : 0.9)); break;
							case 5: Draw.setRobotPosition(0.6, 0, (zOffset === 1 ? 2.1 : 0.7), -1); break;
							case 6: Draw.setRobotPosition(0.7, 0, (zOffset === 1 ? 2.0 : 0.4), -1); break;
							case 7: Draw.setRobotPosition(0.8, 0, (zOffset === 1 ? 1.8 : 0.0), -1); break;
							case 8: Draw.setRobotPosition(0.9, 0, (zOffset === 1 ? 1.4 : -0.5)); break;
							case 9: if (zOffset < -1) { Draw.setRobotPosition(0.9, 0, -1); break; }
							case 10: if (zOffset < -2) { Draw.setRobotPosition(0.9, 0, -2); break; }
							case 11:
								Game.robot.x++;
								Draw.setRobotPosition();
								Game.nextInstruction();
								return;
						}
						break;
					case Draw.ANIM.YN_JUMP:
						switch (frame) {
							case 0: Draw.setRobotPosition(0, -0.1, (zOffset === 1 ? 0.6 : 0.4)); break;
							case 1: Draw.setRobotPosition(0, -0.2, (zOffset === 1 ? 1.1 : 0.7), -1); break;
							case 2: Draw.setRobotPosition(0, -0.3, (zOffset === 1 ? 1.5 : 0.9), -1); break;
							case 3: Draw.setRobotPosition(0, -0.4, (zOffset === 1 ? 1.8 : 1.0), -1); break;
							case 4: Draw.setRobotPosition(0, -0.5, (zOffset === 1 ? 2.0 : 0.9)); break;
							case 5: Draw.setRobotPosition(0, -0.6, (zOffset === 1 ? 2.1 : 0.7)); break;
							case 6: Draw.setRobotPosition(0, -0.7, (zOffset === 1 ? 2.0 : 0.4)); break;
							case 7: Draw.setRobotPosition(0, -0.8, (zOffset === 1 ? 1.8 : 0.0)); break;
							case 8: Draw.setRobotPosition(0, -0.9, (zOffset === 1 ? 1.4 : -0.5)); break;
							case 9: if (zOffset < -1) { Draw.setRobotPosition(0, -0.9, -1); break; }
							case 10: if (zOffset < -2) { Draw.setRobotPosition(0, -0.9, -2); break; }
							case 11:
								Game.robot.y--;
								Draw.setRobotPosition();
								Game.nextInstruction();
								return;
						}
						break;
					case Draw.ANIM.XN_JUMP:
						switch (frame) {
							case 0: Draw.setRobotPosition(-0.1, 0, (zOffset === 1 ? 0.6 : 0.4)); break;
							case 1: Draw.setRobotPosition(-0.2, 0, (zOffset === 1 ? 1.1 : 0.7), -1); break;
							case 2: Draw.setRobotPosition(-0.3, 0, (zOffset === 1 ? 1.5 : 0.9), -1); break;
							case 3: Draw.setRobotPosition(-0.4, 0, (zOffset === 1 ? 1.8 : 1.0), -1); break;
							case 4: Draw.setRobotPosition(-0.5, 0, (zOffset === 1 ? 2.0 : 0.9)); break;
							case 5: Draw.setRobotPosition(-0.6, 0, (zOffset === 1 ? 2.1 : 0.7)); break;
							case 6: Draw.setRobotPosition(-0.7, 0, (zOffset === 1 ? 2.0 : 0.4)); break;
							case 7: Draw.setRobotPosition(-0.8, 0, (zOffset === 1 ? 1.8 : 0.0)); break;
							case 8: Draw.setRobotPosition(-0.9, 0, (zOffset === 1 ? 1.4 : -0.5)); break;
							case 9: if (zOffset < -1) { Draw.setRobotPosition(-0.9, 0, -1); break; }
							case 10: if (zOffset < -2) { Draw.setRobotPosition(-0.9, 0, -2); break; }
							case 11:
								Game.robot.x--;
								Draw.setRobotPosition();
								Game.nextInstruction();
								return;
						}
						break;
					case Draw.ANIM.JUMP:
						switch (frame) {
							case 0: Draw.setRobotPosition(0, 0, 0.5); break;
							case 1: Draw.setRobotPosition(0, 0, 0.9); break;
							case 2: Draw.setRobotPosition(0, 0, 1.2); break;
							case 3: Draw.setRobotPosition(0, 0, 1.4); break;
							case 4: Draw.setRobotPosition(0, 0, 1.5); break;
							case 5: Draw.setRobotPosition(0, 0, 1.4); break;
							case 6: Draw.setRobotPosition(0, 0, 1.2); break;
							case 7: Draw.setRobotPosition(0, 0, 0.9); break;
							case 8: Draw.setRobotPosition(0, 0, 0.5); break;
							case 9:
								Draw.setRobotPosition();
								Game.nextInstruction();
								return;
						}
						break;
					case Draw.ANIM.ROTATE_03:
						Game.robot.dir = 3;
						Draw.setRobotPosition();
						Game.nextInstruction();
						return;
					case Draw.ANIM.ROTATE_10:
						Game.robot.dir = 0;
						Draw.setRobotPosition();
						Game.nextInstruction();
						return;
					case Draw.ANIM.ROTATE_21:
						Game.robot.dir = 1;
						Draw.setRobotPosition();
						Game.nextInstruction();
						return;
					case Draw.ANIM.ROTATE_32:
						Game.robot.dir = 2;
						Draw.setRobotPosition();
						Game.nextInstruction();
						return;
					case Draw.ANIM.ROTATE_01:
						Game.robot.dir = 1;
						Draw.setRobotPosition();
						Game.nextInstruction();
						return;
					case Draw.ANIM.ROTATE_12:
						Game.robot.dir = 2;
						Draw.setRobotPosition();
						Game.nextInstruction();
						return;
					case Draw.ANIM.ROTATE_23:
						Game.robot.dir = 3;
						Draw.setRobotPosition();
						Game.nextInstruction();
						return;
					case Draw.ANIM.ROTATE_30:
						Game.robot.dir = 0;
						Draw.setRobotPosition();
						Game.nextInstruction();
						return;
					case Draw.ANIM.SWITCH_OFF:
						removeClass(floor[Game.robot.y][Game.robot.x], 'on');
						addClass(floor[Game.robot.y][Game.robot.x], 'off');
						Game.nextInstruction();
						return;
					case Draw.ANIM.SWITCH_ON:
						removeClass(floor[Game.robot.y][Game.robot.x], 'off');
						addClass(floor[Game.robot.y][Game.robot.x], 'on');
						if (Game.checkSolved()) {
							Game.stopExecution();
							document.getElementById('overlay').style.display = 'block';

							if (Game.score.invalid * 2 > Game.score.executed) {
								document.getElementById('levelSolvedTitle').innerHTML =
									i18n.levelSolvedTitle0[Math.floor(Math.random() * i18n.levelSolvedTitle0.length)];
							} else {
								document.getElementById('levelSolvedTitle').innerHTML =
									'<b>' + i18n.levelSolvedTitle1[Math.floor(Math.random() * i18n.levelSolvedTitle1.length)] + '!</b> ' +
									i18n.levelSolvedTitle2[Math.floor(Math.random() * i18n.levelSolvedTitle2.length)] + '.<br />' +
									i18n.levelSolvedTitle3[Math.floor(Math.random() * i18n.levelSolvedTitle3.length)] + '!';
							}
							document.getElementById('levelSolvedSummary').innerHTML =
								(Game.score.invalid === 0 ? i18n.levelSolvedSummary1 : i18n.levelSolvedSummary2).replace(/(\[[a-z]+\])/g, function (match) {
									switch (match) {
										case '[score]': return Game.score.getScore();
										case '[used]': return Game.score.used;
										case '[executed]': return Game.score.executed;
										case '[invalid]': return Game.score.invalid;
									}
									return match;
								});
							document.getElementById('levelSolvedRank').innerHTML =	'';
							document.getElementById('levelSolvedList').innerHTML =	'';
							removeClass(document.getElementById('levelSolvedSubmit'), 'disabled');
							document.getElementById('levelSolved').style.display = 'block';

							jsApiRequest('http://robotrace.de/db/get_leaderboard.json?level=' + Level.id + '&score=' + Game.score.getScore(), function (json) {
								if (typeof(json.error) !== 'undefined') {
									alert('Error: ' + json.error + '\n' + json.errmsg);
									return;
								}

								document.getElementById('levelSolvedRank').innerHTML =	i18n.levelSolvedRank.replace('[ranking]', json.ranking);
								
								var score = 0, rank = 0, out = '';
								if (typeof(json.top100) === 'object') {
									for (var i = 0; i < json.top100.length; i++) {
										if (json.top100[i].score > score) {
											rank++;
											score = json.top100[i].score;
										}
										out += rank + '. ' + json.top100[i].name + ' (Score: ' + json.top100[i].score + ')<br />';
									}
								}
								document.getElementById('levelSolvedList').innerHTML = out;
							});
						} else {
							Game.nextInstruction();
						}
						return;
				}

				Draw.animate(anim, zOffset, frame + 1);
			}, (Game.executionSpeed === Game.SPEED.FAST_FORWARD ? (frame === 0 ? 0 : 10) : (frame === 0 ? 500 : 30)));
		},

		setRobotPosition:function (offsetX, offsetY, offsetZ, zIndexFix) {
			var pos, x, y, dir;

			x = Game.robot.x + (offsetX || 0);
			y = Game.robot.y + (offsetY || 0);

			if (x < 0 || y < 0 || x >= Level.width || y >= Level.height) {
				addClass(robot, 'invisible');
			} else {
				removeClass(robot, 'invisible');
			}

			pos = transformCoords(x * 64, y * 64);
			switch (Draw.rotation) {
				case 0: dir = Game.robot.dir; break;
				case 90: dir = (Game.robot.dir + 3) % 4; break;
				case 180: dir = (Game.robot.dir + 2) % 4; break;
				case 270: dir = (Game.robot.dir + 1) % 4; break;
			}
			if (!hasClass(robot, 'dir' + dir)) {
				removeClass(robot, '(dir0|dir1|dir2|dir3)');
				addClass(robot, 'dir' + dir);
			}
			robot.style.left = pos.x + 'px';
			robot.style.top = (pos.y - ((Level.getFloorHeight(Game.robot.x, Game.robot.y) + (offsetZ || 0)) * 16)) + 'px';
			robot.style.zIndex = (50002 + (zIndexFix || 0)) - zIndex(x, y);
		},

		getFloorElem:function (x, y) {
			return floor[y][x];
		},

		transformCoords:function (x, y) {
			return transformCoords(x, y);
		},
		
		reTransformCoords:function (x, y) {
			var yn = -((x / (0.3437 * 2)) + (y / (0.6880 * 0.5))) / 2,
				xn = ((-y - (yn * 0.3437)) * 2) / 0.6880,
				t;

			switch (Draw.rotation) {
				case 270:
					t = Level.width * 64 - yn;
					yn = xn;
					xn = t;
					break;
				case 180:
					xn = (Level.width - 0) * 64 - xn;
					yn = (Level.height - 0) * 64 - yn;
					break;
				case 90:
					t = yn;
					yn = Level.height * 64 - xn;
					xn = t;
					break;
			}

			return { x:xn, y:yn };
		}
	};

	Game.initialized('Draw');

	function transformCoords(x, y) {
		var t;
		switch (Draw.rotation) {
			case 90:
				t = x;
				x = (Level.height - 1) * 64 - y;
				y = t;
				break;
			case 180:
				x = (Level.width - 1) * 64 - x;
				y = (Level.height - 1) * 64 - y;
				break;
			case 270:
				t = y;
				y = (Level.width - 1) * 64 - x;
				x = t;
				break;
		}

		x *= 0.6880;
		y *= 0.3437;
		return { x:x - Math.round(y * 2), y:-(y + Math.round(x * 0.5)) };
	}

	function zIndex(x, y) {
		var t;
		switch (Draw.rotation) {
			case 0:
				return Math.round((x + y) * 3);
			case 90:
				return Math.round((x + ((Level.height - 1) - y)) * 3);
			case 180:
				return Math.round((((Level.width - 1) - x) + ((Level.height - 1) - y)) * 3);
			case 270:
				return Math.round((((Level.width - 1) - x) + y) * 3);
			default:
				return 0;
		}
	}
})(window);
