'use strict';

function RoomManager (cfg) {
	this.VERSION              = 64;

	this.MAX_ROOMS            = cfg.MAX_ROOMS;
	this.MAX_CLIENTS          = cfg.MAX_CLIENTS;
	this.MAX_ROOM_NAME_LENGTH = cfg.MAX_ROOM_NAME_LENGTH;
	this.MAX_NICKNAME_LENGTH  = cfg.MAX_NICKNAME_LENGTH;
	this.MAX_PASSWORD_LENGTH  = cfg.MAX_PASSWORD_LENGTH;

	this.clients   = [];
	this.rooms     = [];
	this.roomMap   = {};

	this.replayList = [];
	this.MAX_REPLAY_LENGTH = 20;

	this.maxClientsCount = 0;
	this.maxGamesCount = 0;

	setInterval(this.cleanUp.bind(this),60*1000);
}

RoomManager.prototype.createClient = function (socket,id) {
	if (this.clients.length >= this.MAX_CLIENTS) {
		socket.disconnect();
		return;
	}

	var client;
	if (id) {
		var room;
		for (var i = 0; i < this.rooms.length; i++) {
			room = this.rooms[i];
			// if (!room.reconnecting) continue; // 服务器可能还不知道掉线,所以注释掉.
			// console.log('host:%s\nguest:%s\nyou:%s',room.host.id,room.guest.id,id);
			if (room.host.id === id) {
				client = room.host;
				client.updateSocket(socket);
				break;
			}
			if (room.guest && room.guest.id === id) {
				client = room.guest;
				client.updateSocket(socket);
				break;
			}
		}
		if (client) {
			room.reconnecting = false;
			socket.emit('game reconnect');
			room.emit('opponent reconnect');
		} else {
			socket.emit('game reconnect failed');
		}
	}
	if (!client) {
		client = new Client(this,socket);
	}
	this.clients.push(client);

	// if (this.clients.length > this.maxClientsCount) {
	// 	this.maxClientsCount = this.clients.length;
	// 	console.log(new Date().toISOString().replace('T',' ').substr(0,19)+' Max clients count: %s',this.clients.length);
	// }

	socket.on('error',this.handleError.bind(this,client));
	socket.on('disconnect',this.disconnect.bind(this,client));
	socket.on('createRoom',this.createRoom.bind(this,client));
	socket.on('joinRoom',this.joinRoom.bind(this,client));
	socket.on('leaveRoom',this.leaveRoom.bind(this,client));
	socket.on('lockSpec',this.lockSpec.bind(this,client));
	socket.on('unlockSpec',this.unlockSpec.bind(this,client));
	socket.on('changePosition',this.changePosition.bind(this,client));
	socket.on('getReplayList',this.getReplayList.bind(this,client));
	socket.on('getReplayContent',this.getReplayContent.bind(this,client));
	socket.on('watchLive',this.watchLive.bind(this,client));

	socket.on('ready',client.ready.bind(client));
	socket.on('unready',client.unready.bind(client));
	socket.on('startGame',client.startGame.bind(client));
	socket.on('chat',client.chat.bind(client));
	socket.on('surrender',client.surrender.bind(client));
	socket.on('drop',client.drop.bind(client));
	socket.on('tick',client.tick.bind(client));

	// socket.on('reloadCardInfo',this.reloadCardInfo.bind(this));

	socket.emit('version',this.VERSION);
	this.updateRoomList();
};

RoomManager.prototype.handleError = function (client,err) {
	console.error(err);
	console.error(err.stack);
	// console.trace();
	var errMsg = 'SERVER_ERROR';
	var room = client.room;
	if (room) {
		// if (room.host) {
		// 	room.host.socket.emit('error message',errMsg);
		// }
		// if (room.guest) {
		// 	room.guest.socket.emit('error message',errMsg);
		// }
		room.emit('error message',errMsg);
		this.removeRoom(client.room);
	}
};

RoomManager.prototype.disconnect = function (client) {
	removeFromArr(client,this.clients);

	var room = client.room;
	if (!room) return;
	// --- 重新连接开始 ---
	if (room.game && !room.reconnecting) {
		if ((client === room.host) || (client === room.guest)) {
			room.reconnecting = true;
			// !here
			// room.emit('wait for reconnect');
			room.getAllClients().forEach(function (c) {
				if (c === client) return;
				c.emit('wait for reconnect');
			},this);
			return;
		}
	}
	// --- 重新连接结束 ---
	room.reconnecting = false;
	if (client === room.host) {
		// 主机掉线
		room.emit('host disconnected');
		this.removeRoom(room);
	} else if (client === room.guest) {
		// 客机掉线
		if (room.game) {
			room.emit('guest disconnected');
			this.removeRoom(room);
		} else {
			room.guest = null;
			room.update();
			this.updateRoomList();
		}
	} else {
		// 观众掉线
		room.removeSpectator(client);
		room.update();
		this.updateRoomList();
	}
};

RoomManager.prototype.removeRoom = function (room) {
	if (!room) return;
	if (room.game) room.game.destroy();
	room.getAllClients().forEach(function (client) {
		client.reset();
	},this);

	delete this.roomMap[room.name];
	removeFromArr(room,this.rooms);

	this.updateRoomList();
};

RoomManager.prototype.updateRoomList = function () {
	this.clients.forEach(function (client) {
		var list = [];
		this.rooms.forEach(function (room) {
			var flag = (room.live && room.game && !this.checkLiveIP(client,room)) ||
			           (!room.game && !room.isFull());
			if (flag) {
				list.push(room.toInfo());
			}
		},this);
		if (client.room) return;
		client.socket.emit('update room list',list);
		client.socket.emit('update online counter',this.clients.length);
	},this);
};

RoomManager.prototype.checkRoomName = function (roomName) {
	// TODO: 过滤敏感字词
	if (!isStr(roomName) || !roomName || roomName.length > this.MAX_ROOM_NAME_LENGTH) {
		return 'INVALID_ROOM_NAME';
	}
};

RoomManager.prototype.checkNickname = function (nickname) {
	// TODO: 过滤敏感字词
	if (!isStr(nickname) || !nickname || nickname.length > this.MAX_NICKNAME_LENGTH) {
		return 'INVALID_NICKNAME';
	}
};

RoomManager.prototype.checkPassword = function (password) {
	if (!isStr(password) || password.length > this.MAX_PASSWORD_LENGTH) {
		return 'INVALID_PASSWORD';
	}
};

RoomManager.prototype.checkClientInRoom = function (client) {
	if (client.room) return 'ALREADY_IN_A_ROOM';
};

RoomManager.prototype.checkLiveIP = function (client,room) {
	if (!client.socket.handshake) return '';
	if (!room.guest) return '';
	var address = client.socket.handshake.address;
	var guestAddress = room.guest.socket.handshake.address;
	if (address === guestAddress) return 'IP_BANNED';
};

RoomManager.prototype.createRoom = function (client,cfg) {
	var errMsg;
	if (!isObj(cfg) || !isStr(cfg.roomName) || !isStr(cfg.nickname)) {
		errMsg = 'INVALID_CONFIG';
	}
	var roomName = cfg.roomName;
	var nickname = cfg.nickname;
	var password = cfg.password;
	if (!errMsg) {
		errMsg =
			this.checkRoomName(roomName) ||
			this.checkNickname(nickname) ||
			this.checkPassword(password) ||
			this.checkClientInRoom(client);
	}
	if (!errMsg) {
		if (roomName in this.roomMap) {
			errMsg = 'ROOM_ALREADY_EXISTS';
		} else if (this.rooms.length >= this.MAX_ROOMS) {
			errMsg = 'MAX_ROOMS';
		}
	}

	if (errMsg) {
		client.socket.emit('error message',errMsg);
		return;
	}

	// console.log('%s creates room: %s',client.socket.id,roomName);
	if (password) {
		console.log('nickname: %s, roomName: %s, password: %s',nickname,roomName,password);
	}
	// 双向绑定
	var room = new Room(roomName,client,password,!!cfg.mayusRoom);
	client.room = room;
	this.roomMap[roomName] = room;
	this.rooms.push(room);
	client.nickname = nickname;

	// client.socket.emit('host room',{
	// 	roomName: room.name,
	// 	host: nickname,
	// 	guest: ''
	// });
	room.update();
	this.updateRoomList();
};

RoomManager.prototype.joinRoom = function (client,cfg) {
	var errMsg;
	if (!isObj(cfg) || !isStr(cfg.roomName) || !isStr(cfg.nickname)) {
		errMsg = 'INVALID_CONFIG';
	}
	var roomName = cfg.roomName;
	var nickname = cfg.nickname;
	var password = cfg.password;
	if (!errMsg) {
		errMsg =
			this.checkRoomName(roomName) ||
			this.checkNickname(nickname) ||
			this.checkPassword(password) ||
			this.checkClientInRoom(client);
	}
	var room;
	if (!errMsg) {
		room = this.roomMap[roomName];
		if (!room) {
			errMsg = 'ROOM_DOES_NOT_EXIST';
		} else if (room.game) {
			errMsg = 'GAME_ALREADY_STARTED';
		} else if (room.isFull()) {
			errMsg = 'ROOM_IS_FULL';
		}
	}

	if (errMsg) {
		client.socket.emit('error message',errMsg);
		return;
	}

	if (room.password && (password !== room.password)) {
		client.socket.emit('wrong password');
		return;
	}

	client.nickname = nickname;
	client.room = room;

	if (!room.guest) {
		room.guest = client;
	} else if (!room.isHostSpectatorsFull()) {
		room.pushHostSpectator(client);
	} else {
		room.pushGuestSpectator(client);
	}
	room.update();
	this.updateRoomList();
};

// RoomManager.prototype.toGuest = function (client,room) {
// 	room.removeClient(client);
// 	room.guest = client;
// 	room.update();
// };

// RoomManager.prototype.toHostSpectator = function (client,room,i) {
// 	room.removeClient(client);
// 	room.setHostSpectator(client,i);
// 	room.update();
// };

// RoomManager.prototype.toGuestSpectator = function (client,room,i) {
// 	room.removeClient(client);
// 	room.setGuestSpectator(client,i);
// 	room.update();
// };

RoomManager.prototype.leaveRoom = function (client) {
	var errMsg;
	if (!client.room) {
		errMsg = 'YOU_ARE_NOT_IN_ANY_ROOM';
	} else if (client.room.game && ((client === client.room.host) || (client === client.room.guest))) {
		errMsg = 'GAME_ALREADY_STARTED';
	}

	if (errMsg) {
		client.socket.emit('error message',errMsg);
		return;
	}

	var room = client.room;
	if (client === room.host) {
		room.emit('host left');
		this.removeRoom(room);
	} else {
		room.removeClient(client);
		client.reset();
		room.update();
	}

	// var host = client.room.host;
	// var guest = client.room.guest;
	// if (client === host) {
	// 	if (guest) {
	// 		guest.socket.emit('host left');
	// 	}
	// 	this.removeRoom(client.room);
	// } else {
	// 	host.socket.emit('guest left');
	// 	client.room.guest = null;
	// 	client.reset();
	// }
	this.updateRoomList();
};

function checkSpectatorIndex (i) {
	if (!((i >= 0) && (i < 5))) {
		return 'INVALID_INDEX';
	}
}

RoomManager.prototype.lockSpec = function (client,i) {
	i >>>= 0;
	var errMsg;
	var room = client.room;
	if (!room) {
		errMsg = 'YOU_ARE_NOT_IN_ANY_ROOM';
	} else if (client.room.game) {
		errMsg = 'GAME_ALREADY_STARTED';
	} else if ((client !== room.host) && (client !== room.guest)) {
		errMsg = 'NO_PERMISSION';
	} else {
		errMsg = checkSpectatorIndex(i);
	}

	if (errMsg) {
		client.socket.emit('error message',errMsg);
		return;
	}

	if (client === room.host) {
		room.lockHostSpec(i);
	} else {
		room.lockGuestSpec(i);
	}
	room.update();
	this.updateRoomList();
};

RoomManager.prototype.unlockSpec = function (client,i) {
	i >>>= 0;
	var errMsg;
	var room = client.room;
	if (!room) {
		errMsg = 'YOU_ARE_NOT_IN_ANY_ROOM';
	} else if (room.game) {
		errMsg = 'GAME_ALREADY_STARTED';
	} else if ((client !== room.host) && (client !== room.guest)) {
		errMsg = 'NO_PERMISSION';
	} else {
		errMsg = checkSpectatorIndex(i);
	}

	if (errMsg) {
		client.socket.emit('error message',errMsg);
		return;
	}

	if (client === room.host) {
		room.unlockHostSpec(i);
	} else {
		room.unlockGuestSpec(i);
	}
	room.update();
	this.updateRoomList();
};

RoomManager.prototype.changePosition = function (client,cfg) {
	var errMsg;
	var room = client.room;
	if (!room) {
		errMsg = 'YOU_ARE_NOT_IN_ANY_ROOM';
	} else if (room.game) {
		errMsg = 'GAME_ALREADY_STARTED';
	} else if (client === room.host) {
		errMsg = 'NO_PERMISSION';
	} else if (!isObj(cfg)) {
		errMsg = 'INVALID_CONFIG';
	}

	if (errMsg) {
		client.socket.emit('error message',errMsg);
		return;
	}

	if (cfg.position === 'guest') {
		if (room.guest) return room.update();
		room.removeClient(client);
		room.guest = client;
		return room.update();
	}

	var i = cfg.i >>> 0;
	if (checkSpectatorIndex(i)) return;
	if (cfg.position === 'host-spectator') {
		room.setHostSpectator(client,i);
	} else if (cfg.position === 'guest-spectator') {
		room.setGuestSpectator(client,i);
	}
	room.update();
};

RoomManager.prototype.gameover = function (room,replay) {
	// console.log('%s gameovered',room.name);
	if (room) {
		room.gameover();
	}
	if (replay) {
		this.pushReplay(replay);
	}
	this.updateRoomList();
};

RoomManager.prototype.pushReplay = function (replay) {
	// if (replay.messagePacks.length < 20) return;
	replay.id = Math.random();
	this.replayList.unshift(replay);
	if (this.replayList.length > this.MAX_REPLAY_LENGTH) {
		this.replayList.pop();
	}
};

RoomManager.prototype.getReplayList = function (client) {
	var list = this.replayList.map(function (replay) {
		return {
			id: replay.id,
			win: replay.win,
			surrender: replay.surrender,
			selfLrig: replay.selfLrig,
			opponentLrig: replay.opponentLrig
		};
	},this);
	client.socket.emit('replayList',list);
};

RoomManager.prototype.getReplayContent = function (client,id) {
	if (!isNum(id)) return;
	for (var i = 0; i < this.replayList.length; i++) {
		var replay = this.replayList[i];
		if (replay.id === id) {
			client.socket.emit('replayContent',{
				clientVersion: this.version,
				win: replay.win,
				surrender: replay.surrender,
				messagePacks: replay.messagePacks
			});
			return;
		}
	}
	client.socket.emit('replayContent',null);
};

RoomManager.prototype.watchLive = function (client,cfg) {
	var errMsg;
	if (!isObj(cfg) || !isStr(cfg.roomName)) {
		errMsg = 'INVALID_CONFIG';
	}
	var roomName = cfg.roomName;
	if (!errMsg) {
		errMsg = this.checkClientInRoom(client);
	}
	var room;
	if (!errMsg) {
		room = this.roomMap[roomName];
		if (!room) {
			errMsg = 'ROOM_DOES_NOT_EXIST';
		} else if (!room.live) {
			errMsg = 'NOT_IN_LIVE_MODE';
		} else if (!room.game) {
			errMsg = 'GAME_NOT_IN_PROGRESS';
		} else {
			errMsg = this.checkLiveIP(client,room);
		}
	}

	if (errMsg) {
		client.socket.emit('error message',errMsg);
		return;
	}

	client.room = room;
	room.pushLiveSpectator(client);
	client.emit('liveData',room.game.getLiveMessagePacks());
};

RoomManager.prototype.cleanUp = function () {
	var clients = this.clients.slice();
	clients.forEach(function (client) {
		var socket = client.socket;
		if (socket.disconnected) {
			this.disconnect(client);
			console.error('cleanUp');
		}
	},this);
	var rooms = this.rooms.slice();
	var now = Date.now();
	rooms.forEach(function (room) {
		var flag =
			((now - room.activateTime) >= 3*60*60*1000) ||
			(!room.reconnecting && room.host && room.host.socket.disconnected) ||
			(!room.reconnecting && room.guest && room.guest.socket.disconnected);
		if (!flag) return;
		console.log('clean up: ' + room.name);
		this.removeRoom(room);
	},this);
};

// RoomManager.prototype.reloadCardInfo = function (password) {
// 	if (password !== 'WEBXOSS') return;
// 	var path = require('path');
// 	var filePath = './CardInfo.js';
// 	delete require.cache[path.resolve(filePath)];
// 	require(filePath);
// };

global.RoomManager = RoomManager;