var PORT = 8080;
var gameMode = true;

const mapWidth = 50;
const mapHeight = 50;

var allSockets = {};
var allPlayers = {};
var userCnt = 0;

var express = require('express');
var app = express();
app.use(express.static(__dirname + '/'));
var server = require('http').Server(app);
var io = require('socket.io')(server);
var map = require("./s_map.js");

var maxX = mapWidth * 32;
var maxY = mapHeight * 32;
var newmap = map.generateMap(mapWidth, mapHeight);
var cssMap = newmap["cssMap"];
var map = newmap["map"];
var utilMap = newmap["utilMap"];
var defaultMvnt = 2;

var itemAttrs = {
	"item_boots_boots1": {
		lvl: 1,
		changeMovementLand: 0.1
	},
	"item_boots_boots2": {
		lvl: 2,
		changeMovementLand: 0.15
	},
	"item_boots_boots3": {
		lvl: 3,
		changeMovementLand: 0.2
	},
	"item_boots_boots4": {
		lvl: 4,
		changeMovementLand: 0.25
	},
	"item_boots_boots5": {
		lvl: 5,
		changeMovementLand: 0.3
	},
	"item_tool_axe1": {
		lvl: 1,
		reduceTreeCutSpeed: 1
	},
	"item_tool_axe2": {
		lvl: 2,
		reduceTreeCutSpeed: 300
	},
	"item_tool_axe3": {
		lvl: 3,
		reduceTreeCutSpeed: 700
	},
	"item_tool_axe4": {
		lvl: 4,
		reduceTreeCutSpeed: 900
	},
	"item_tool_axe5": {
		lvl: 5,
		reduceTreeCutSpeed: 1300
	},
	"item_head_head1": {
		lvl: 1,
		armor: 10
	},
	"item_head_head2": {
		lvl: 2,
		armor: 18
	},
	"item_head_head3": {
		lvl: 3,
		armor: 23
	},
	"item_head_head4": {
		lvl: 4,
		armor: 25
	},
	"item_head_head5": {
		lvl: 5,
		armor: 30
	},
	"item_chest_chest1": {
		lvl: 1,
		armor: 10
	},
	"item_chest_chest2": {
		lvl: 2,
		armor: 18
	},
	"item_chest_chest3": {
		lvl: 3,
		armor: 23
	},
	"item_chest_chest4": {
		lvl: 4,
		armor: 25
	},
	"item_chest_chest5": {
		lvl: 5,
		armor: 30
	},
	"item_legs_legs1": {
		lvl: 1,
		armor: 10
	},
	"item_legs_legs2": {
		lvl: 2,
		armor: 18
	},
	"item_legs_legs3": {
		lvl: 3,
		armor: 23
	},
	"item_legs_legs4": {
		lvl: 4,
		armor: 28
	},
	"item_legs_legs5": {
		lvl: 5,
		armor: 30
	},
	"item_weapon_sword1": {
		lvl: 1,
		dmg: 40
	},
	"item_weapon_sword2": {
		lvl: 2,
		dmg: 60
	},
	"item_weapon_sword3": {
		lvl: 3,
		dmg: 80
	},
	"item_weapon_sword4": {
		lvl: 4,
		dmg: 100
	},
	"item_weapon_sword5": {
		lvl: 5,
		dmg: 130
	},
	"item_weapon_stick1": {
		lvl: 1,
		dmg: 20
	},
	"item_item_bone0": {
		lvl: 0,
	},
	"item_item_spaten3": {
		lvl: 1,
	},
	"item_consumable_fish1": {
		lvl: 3,
		hp: 30,
	},
	"item_consumable_fish2": {
		lvl: 1,
		hp: 10
	},
	"item_item_dimond1": {
		lvl: 10,
		changeMovementSpeed: -0.5
	},
	"item_item_woods1": {
		lvl: 1,
		//changeMovementSpeed: -0.5
	},
}

var items = [];
for (var i in itemAttrs) {
	items.push(i)
}

server.listen(PORT);

console.log("Pirate running on port:" + PORT);
io.on('connection', function (socket) {
	var playerActionTimer = null;
	userCnt++;
	console.log("New user", socket.id);
	allSockets[socket.id] = socket;
	allPlayers[socket.id] = {
		keys: {},
		x: 10,
		y: 10,
		mvnt: defaultMvnt,
		lookingLeft: 0,
		lookingUp: 0,
		special: {},
		mode: 0, //0 = ship //1 foot
		slots: {
			head: null,
			chest: null,
			legs: null,
			boots: null,
			weapon: null,
			tool: null,
			item: null
		},
		hp: 80,
		socketId: socket.id
	};
	var player = allPlayers[socket.id];
	io.sockets.emit("newPlayer", {
		player: allPlayers[socket.id],
		id: socket.id
	});

	socket.on('disconnect', function () {
		for (var i in player.slots) {
			if (player.slots[i]) {
				let nearFieldCoords = getNearEmptyField(Math.floor(player["x"] / 32), Math.floor(player["y"] / 32));
				if (nearFieldCoords) {
					let newObj = {
						mapY: nearFieldCoords["y"],
						mapX: nearFieldCoords["x"],
						newName: player.slots[i]
					}
					utilMap[nearFieldCoords["y"]][nearFieldCoords["x"]] = newObj["newName"];
					io.sockets.emit("dropItem", newObj);
				}
			}
		}
		userCnt--;
		delete allSockets[socket.id];
		delete allPlayers[socket.id];
		console.log("User Disconnected:", socket.id);
		io.sockets.emit("playerDisc", socket.id);

	});

	socket.on('getmap', function () {

		newmap["allPlayers"] = allPlayers;
		socket.emit("getmap", newmap);
	});

	socket.on('keyUp', function (key) {
		allPlayers[socket.id]["keys"][key] = false;
		if (key == 69 || key == 71) { //space oder G
			player["action"] = false;
			if (playerActionTimer) {
				clearTimeout(playerActionTimer);
				playerActionTimer = null;
			}
			io.sockets.emit("stopAction", socket.id);
		}
	});

	socket.on('keyDown', function (key) {
		allPlayers[socket.id]["keys"][key] = true;
		if (key == 32 && allPlayers[socket.id]["mode"] != 0) { //Space HIT SOMEONE
			//if(allPlayers[socket.id]["mode"] == 1) { // On land
			if (!allPlayers[socket.id]["hitPending"]) {
				io.sockets.emit("showHitAnimation", socket.id);
				allPlayers[socket.id]["hitPending"] = true;
				let allPlayersWithCollision = checkHitWithOtherPlayers(socket.id);
				for (var i = 0; i < allPlayersWithCollision.length; i++) {
					calcDmgAndHitPlayer(allPlayers[socket.id], allPlayers[allPlayersWithCollision[i]])
				}
				console.log(allPlayersWithCollision);

				setTimeout(function () {
					allPlayers[socket.id]["hitPending"] = false;
				}, 500)
			}
			//}
		} else if (key == 71) { // Taste G
			var groundUnder = getGroundUnderPlayer(player["x"], player["y"]);
			if (groundUnder == 10) { //its land
				let cutTime = 6000; //6 Secs
				console.log(player)
				if(player["slots"] && player["slots"]["item"] && player["slots"]["item"] == "item_item_spaten3") {
					cutTime = 2000;
				}
				if (playerActionTimer) {
					clearTimeout(playerActionTimer);
					playerActionTimer = null;
				}
				
				playerActionTimer = setTimeout(function () {

					//Drop a random Item
					let randomItem = getRandomItem(0, 0);
					let nearFieldCoords = getNearEmptyField(Math.floor(player["x"] / 32), Math.floor(player["y"] / 32));

					utilMap[nearFieldCoords["y"]][nearFieldCoords["x"]] = randomItem;
					let newObj = {
						mapY: nearFieldCoords["y"],
						mapX: nearFieldCoords["x"],
						newName: randomItem
					}
					io.sockets.emit("dropItem", newObj);

					socket.emit("stopAnimation", cutTime);
					allPlayers[socket.id]["action"] = false;

				}, cutTime);
				allPlayers[socket.id]["action"] = true;
				socket.emit("animation", cutTime);
			}


			console.log(groundUnder)
		}
	});

	socket.on('actionKey', function (key) {
		var groundUnder = getGroundUnderPlayer(player["x"], player["y"]);
		if (key == 69) {// E

			if (player["mode"] == 0) { //ship
				if (groundUnder >= 1) { //its nearly land
					player["mode"] = 1;
					player["special"]["modechange"] = true;
				}
			} else {
				if (groundUnder <= 1) { //its water land
					player["mode"] = 0;
					player["special"]["modechange"] = true;
				}
			};
			//console.log(player["mode"], groundUnder);
			var obj = getNearestObject(player["x"], player["y"]);
			if (obj) {
				obj["socketId"] = socket.id;
				if (playerActionTimer) {
					clearTimeout(playerActionTimer);
					playerActionTimer = null;
				}
				if (obj["name"] == "forest") {
					let cutTime = 2000; //2 Secs
					if (player["slots"]["tool"] && itemAttrs[player["slots"]["tool"]] && itemAttrs[player["slots"]["tool"]]["reduceTreeCutSpeed"] > 0) {
						cutTime -= itemAttrs[player["slots"]["tool"]]["reduceTreeCutSpeed"];
						playerActionTimer = setTimeout(function () {

							if (Math.random() > 0.75) {
								utilMap[obj["mapY"]][obj["mapX"]] = "item_item_woods1";
								obj["newName"] = "item_item_woods1";
								io.sockets.emit("replaceUtil", obj);
							} else {
								utilMap[obj["mapY"]][obj["mapX"]] = "";
								io.sockets.emit("removeUtil", obj);
							}

							socket.emit("stopAnimation", cutTime);

						}, cutTime);
						socket.emit("animation", cutTime);
					} else {
						socket.emit("displayMsg", "Baum kann nur mit einer Axt gefällt werden!");

						//can not cut tree
					}
				} else if (obj["name"] == "treasure-closed") {
					playerActionTimer = setTimeout(function () {
						utilMap[obj["mapY"]][obj["mapX"]] = "treasure-open";
						obj["newName"] = "treasure-open";
						io.sockets.emit("replaceUtil", obj);
						socket.emit("stopAnimation", 2000);

						//Drop a random Item
						let randomItem = getRandomItem(2, 5);
						let nearFieldCoords = getNearEmptyField(obj["mapX"], obj["mapY"]);

						utilMap[nearFieldCoords["y"]][nearFieldCoords["x"]] = randomItem;
						let newObj = {
							mapY: nearFieldCoords["y"],
							mapX: nearFieldCoords["x"],
							newName: randomItem
						}
						io.sockets.emit("dropItem", newObj);
					}, 2000); //2 Secs
					socket.emit("animation", 2000);
				} else if (obj["name"] == "fass") {
					playerActionTimer = setTimeout(function () {
						utilMap[obj["mapY"]][obj["mapX"]] = "";
						io.sockets.emit("removeUtil", obj);
						socket.emit("stopAnimation", 500);

						//Drop a random Item
						let randomItem = getRandomItem(1, 2);
						let nearFieldCoords = getNearEmptyField(obj["mapX"], obj["mapY"]);

						utilMap[nearFieldCoords["y"]][nearFieldCoords["x"]] = randomItem;
						let newObj = {
							mapY: nearFieldCoords["y"],
							mapX: nearFieldCoords["x"],
							newName: randomItem
						}
						io.sockets.emit("dropItem", newObj);
					}, 500); //2 Secs
					socket.emit("animation", 500);
				} else if (obj["name"].split("_")[0] == "item") {
					let itemName = obj["name"];
					utilMap[obj["mapY"]][obj["mapX"]] = "";
					io.sockets.emit("removeUtil", obj);

					let slotArt = obj["name"].split("_")[1];
					if (allPlayers[socket.id]["slots"][slotArt]) { //Slot is not empty so drop it
						let nearFieldCoords = getNearEmptyField(Math.floor(player["x"] / 32), Math.floor(player["y"] / 32));
						let newObj = {
							mapY: nearFieldCoords["y"],
							mapX: nearFieldCoords["x"],
							newName: allPlayers[socket.id]["slots"][slotArt]
						}
						utilMap[nearFieldCoords["y"]][nearFieldCoords["x"]] = newObj["newName"];
						io.sockets.emit("dropItem", newObj);
						//console.log("drop", newObj)
					}

					console.log(slotArt)
					if (slotArt == "consumable") {
						let hp = itemAttrs[obj["name"]]["hp"];
						allPlayers[socket.id]["hp"] += hp;
						let toMutch = 0;
						if (allPlayers[socket.id]["hp"] > 100) {
							toMutch = allPlayers[socket.id]["hp"] - 100;
						}
						if (allPlayers[socket.id]["hp"] > 100) {
							allPlayers[socket.id]["hp"] = 100;
						}
						socket.emit("displayMsg", "Lebenspunkte +" + (hp - toMutch));
						socket.emit("hpChange", allPlayers[socket.id]["hp"]);
					} else {
						allPlayers[socket.id]["slots"][slotArt] = itemName;
						socket.emit("addItemToInventar", itemName);
					}
				}

				io.sockets.emit("objAction", obj);
			}
		}
		player["action"] = true;
	});
});

function getNearEmptyField(x, y) {
	x = Math.round(x);
	y = Math.round(y);
	for (var j = 0; j < 20; j++) {
		for (var i = -1 - j; i < 2 + j; i++) {
			for (var k = -1; k < 2; k++) {
				//console.log("check", i, k, y + i, x + k)
				if (utilMap[y + i] && utilMap[y + i][x + k] == "" && cssMap[y + i] && cssMap[y + i][x + k] != "water") {
					return {
						y: y + i,
						x: x + k
					}
				}
			}
		}
	}
}

function calcDmgAndHitPlayer(player1, player2) {
	let dmg = 1;
	dmg += parseFloat(player1["slots"]["weapon"] ? itemAttrs[player1["slots"]["weapon"]]["dmg"] : 0);

	let armor = 1;
	for (var k in player2["slots"]) {
		if (k == "head" || k == "chest" || k == "legs") {
			armor += parseFloat(player1["slots"][k] ? itemAttrs[player1["slots"][k]]["armor"] : 0);
		}
	}
	let hitDmg = dmg * (armor / 100) * 100
	player2["hp"] -= hitDmg;
	if (player2["hp"] < 0) {
		player2["hp"] = 0;
	}

	io.sockets.emit("newPlayerHp", { socketId: player2["socketId"], hp: player2["hp"] })
	console.log(hitDmg, dmg, armor, player1, player2)
}

function getRandomItem(minLvl, maxLvl) {
	//console.log("rnd", items.length, getRandomNumber(0, items.length), items)
	// return items[32] //Spaten
	// return items[32] //fish
	while (1) {
		let item = items[getRandomNumber(0, items.length)];
		console.log(item)
		if (item && (typeof (maxLvl) == "undefined" || typeof (minLvl) == "undefined" || (itemAttrs[item]["lvl"] >= minLvl && itemAttrs[item]["lvl"] <= maxLvl))) {

			return item;
		}
	}
}

function getNearestObject(x, y) {
	var mapX = Math.floor(x / 32);
	var mapY = Math.floor(y / 32);
	var obj = null;
	if (utilMap[mapY][mapX] != "") {
		obj = {
			mapX: mapX,
			mapY: mapY,
			name: utilMap[mapY][mapX]
		}
	} else {
		var rX = x % 32;
		var rY = y % 32;
		var sX = 0;
		var sY = 0;
		if (rX > 6 && rX < 26) { //He stands in the middle of the field (left-><-right)
			if (rY < 16) { //go Up
				sY = -1;
			} else { //go Down
				sY = 1;
			}
		} else if (rY > 6 && rY < 26) { //He stands in the middle of the field (up-><-down)
			if (rX < 16) { //go left
				sX = -1;
			} else { //go right
				sX = 1;
			}
		}

		if (utilMap[mapY + sY] && utilMap[mapY + sY][mapX + sX] != "") {
			obj = {
				mapY: mapY + sY,
				mapX: mapX + sX,
				name: utilMap[mapY + sY][mapX + sX]
			}
		}
	}

	return obj;

}

setInterval(function () {
	for (var id in allPlayers) {
		var player = allPlayers[id];
		for (var key in player["keys"]) {
			if (player["keys"][key] && player["hp"] > 0) { //if key is pressed

				var tx = 0;
				var ty = 0;

				var mvnt = player["mvnt"];
				if (key == 37) { //left
					tx = -1 * mvnt;
				} else if (key == 38) { //top
					ty = -1 * mvnt;
				} else if (key == 39) { //right
					tx = mvnt;
				} else if (key == 40) { //bottom
					ty = mvnt;
				}

				player.lookingLeft = tx > 0 ? -1 : (tx < 0 ? 1 : player.lookingLeft)
				player.lookingUp = ty > 0 ? -1 : (ty < 0 ? 1 : player.lookingUp)

				//console.log(player)
				if (!player["action"]) {
					var newX = player["x"] + tx;
					var newY = player["y"] + ty;

					var groundUnderPlayer = getGroundUnderPlayer(newX, newY);
					if (checkPlayerPosition(newX, newY, groundUnderPlayer, player)) {
						player["x"] = newX;
						player["y"] = newY;
						let mvnt = getPlayerMvnt(player, groundUnderPlayer);
						if (player["slots"]["boots"] && groundUnderPlayer != 0) { //Not water
							player["mvnt"] = mvnt + itemAttrs[player["slots"]["boots"]]["changeMovementLand"];
							//console.log(groundUnderPlayer, player["mvnt"])
						} else {
							player["mvnt"] = mvnt;
						}

						if (player["slots"]["item"] && player["slots"]["item"]["changeMovementSpeed"]) {
							player["mvnt"] = mvnt + itemAttrs[player["slots"]["item"]]["changeMovementSpeed"];
						}

					}

				}
			}
		}
	}
	io.sockets.emit("setpos", allPlayers);
	for (var id in allPlayers) {
		allPlayers[id]["special"] = {};
	}
}, 20);

function checkPlayerPosition(newX, newY, groundUnderPlayer, player) {
	if (newX < 0 || newY < 0 || newX > maxX || newY > maxY) {
		return false;
	}
	if (player["mode"] == 0) { //ship
		if (groundUnderPlayer < 0 || groundUnderPlayer > 2) { //dont move with ship on land
			return false;
		}
	} else {
		if (groundUnderPlayer == 7) { //forrest so dont move
			return false;
		}
	}

	return true;
}

function getGroundUnderPlayer(newX, newY) { //0 = water; 10 = hard ground
	if (newX < 0 || newY < 0 || newX > maxX || newY > maxY) {
		return 0;
	}
	var mapX = Math.floor(newX / 32);
	var mapY = Math.floor(newY / 32);

	if (!cssMap[mapY] || !cssMap[mapY][mapX]) {
		return false;
	}
	var ground = cssMap[mapY][mapX];
	var util = utilMap[mapY][mapX];
	if (util == "forest") {
		return 7;
	}
	var groundSplit = ground.split("_");
	if (groundSplit.length != 2) {
		if (ground == "water") {
			return 0;
		}
		return 10; //Ground
	}
	return parseFloat(groundSplit[1]);
}

function getPlayerMvnt(player, groundUnderPlayer) {
	if (player["mode"] == 0) { //ship
		if (groundUnderPlayer === 1) {
			return 0.5;
		} else if (groundUnderPlayer === 2) {
			return 0.1;
		}
	} else {
		if (groundUnderPlayer === 0) {
			return 0.1;
		} else if (groundUnderPlayer === 1) {
			return 0.5;
		} else if (groundUnderPlayer === 2) {
			return 0.8;
		}
		return 1.5;
	}
	return defaultMvnt;
}

function getRandomNumber(min, max) { //min (inclusive) and max (exclusive)
	return Math.floor(Math.random() * (max - min + 1)) + min;
}

function checkHitWithOtherPlayers(socketId) {
	let playersWithCollision = [];
	let mainPlayer = allPlayers[socketId];
	for (var sid in allPlayers) {
		if (sid != socketId) { //Dont check collision with self
			let targetPlayer = allPlayers[sid];
			let bottom = mainPlayer.keys["40"];
			let left = mainPlayer.keys["37"];
			//console.log(mainPlayer.x, mainPlayer.y, 28, targetPlayer.x, targetPlayer.y, 8)
			if (isCircleCircleCollision(mainPlayer.x, mainPlayer.y, 28, targetPlayer.x, targetPlayer.y, 8)) {
				let hit = false;
				console.log(mainPlayer.lookingLeft, mainPlayer.lookingUp, mainPlayer.x, mainPlayer.y, targetPlayer.x, targetPlayer.y)
				if (mainPlayer.lookingLeft == 1 && mainPlayer.x >= targetPlayer.x) {
					hit = true;
				} else if (mainPlayer.lookingLeft == -1 && mainPlayer.x <= targetPlayer.x) {
					hit = true;
				} else if (mainPlayer.lookingUp == 1 && mainPlayer.y >= targetPlayer.y) {
					hit = true;
				} else if (mainPlayer.lookingUp == -1 && mainPlayer.y <= targetPlayer.y) {
					hit = true;
				}

				if (hit) {
					playersWithCollision.push(sid);
				}
			}
		}
	}
	return playersWithCollision;
}

function isCircleRecCollision(cx, cy, cr, rx, ry, rw, rh) {
	var cDistx = Math.abs(cx - rx);
	var cDisty = Math.abs(cy - ry);
	if (cDistx > rw / 2 + cr)
		return false;
	if (cDisty > rh / 2 + cr)
		return false;
	if (cDistx <= rw / 2)
		return true;
	if (cDisty <= rh / 2)
		return false;
	return (cDistx - rw / 2) * (cDistx - rw / 2) + (cDisty - rh / 2) * (cDisty - rh / 2) <= cr * cr;
}

function isCircleCircleCollision(x1, y1, r1, x2, y2, r2) {
	var dx = x1 - x2;
	var dy = y1 - y2;
	var sr = r1 + r2;
	if (Math.abs(dx) <= sr && Math.abs(dy) <= sr) { //Check main condition
		return Math.sqrt(dx * dx + dy * dy) < sr // true = collision detected!
	}
	return false;
}