	public void handleMsg(Bais msg) {
		byte i = msg.readByte();
		Command c = Command.values()[i];
		Log.d(TAG, "Received: " + c);
		switch (c) {
		case ChannelPlayers:
		case JoinChannel: 
		case LeaveChannel:{
			Channel ch = channels.get(msg.readInt());
			if(ch != null)
				ch.handleChannelMsg(c, msg);
			else
				Log.e(TAG, "Received message for nonexistent channel");
			break;
		} case VersionControl: {
			ProtocolVersion serverVersion = new ProtocolVersion(msg);
			if (serverVersion.compareTo(version) > 0) {
				Log.d(TAG, "Server has newer protocol version than we expect");
			} else if (serverVersion.compareTo(version) < 0) {
				Log.d(TAG, "PO Android uses newer protocol than Server");
			}
			serverSupportsZipCompression = msg.readBool();
			ProtocolVersion lastVersionWithoutFeatures = new ProtocolVersion(msg);
			ProtocolVersion lastVersionWithoutCompatBreak = new ProtocolVersion(msg);
			ProtocolVersion lastVersionWithoutMajorCompatBreak = new ProtocolVersion(msg);
			if (serverVersion.compareTo(version) > 0) {
				if (lastVersionWithoutFeatures.compareTo(version) > 0) {
					Toast.makeText(this, R.string.new_server_features_warning, Toast.LENGTH_SHORT).show();
				} else if (lastVersionWithoutCompatBreak.compareTo(version) > 0) {
					Toast.makeText(this, R.string.minor_compat_break_warning, Toast.LENGTH_SHORT).show();
				} else if (lastVersionWithoutMajorCompatBreak.compareTo(version) > 0) {
					Toast.makeText(this, R.string.major_compat_break_warning, Toast.LENGTH_LONG).show();
				}
			}
			serverName = msg.readString();
			if (chatActivity != null) {
				chatActivity.updateTitle();
			}
			break;
		} case Register: {
			break;
		} case Login: {
			Bais flags = msg.readFlags();
			Boolean hasReconnPass = flags.readBool();
			if (hasReconnPass) {
				reconnectSecret = msg.readQByteArray();
			}
			mePlayer = new PlayerInfo(msg);
			myid = mePlayer.id;
			int numTiers = msg.readInt();
			for (int j = 0; j < numTiers; j++) {
				msg.readString();
			}
			players.put(mePlayer.id, mePlayer);
			break;
		} case TierSelection: {
			msg.readInt(); 
			Tier prevTier = new Tier(msg.readByte(), msg.readString());
			prevTier.parentTier = superTier;
			superTier.subTiers.add(prevTier);
			while(msg.available() != 0) { 
				Tier t = new Tier(msg.readByte(), msg.readString());
				if(t.level == prevTier.level) { 
					prevTier.parentTier.addSubTier(t);
					t.parentTier = prevTier.parentTier;
				}
				else if(t.level < prevTier.level) { 
					while(t.level < prevTier.level)
						prevTier = prevTier.parentTier;
					prevTier.parentTier.addSubTier(t);
					t.parentTier = prevTier.parentTier;
				}
				else if(t.level > prevTier.level) { 
					prevTier.addSubTier(t);
					t.parentTier = prevTier;
				}
				prevTier = t;
			}
			break;
		} case ChannelsList: {
			int numChannels = msg.readInt();
			for(int j = 0; j < numChannels; j++) {
				int chanId = msg.readInt();
				Channel ch = new Channel(chanId, msg.readString(), this);
				channels.put(chanId, ch);
			}
			Log.d(TAG, channels.toString());
			break;
		} case PlayersList: {
			while (msg.available() != 0) { 
				PlayerInfo p = new PlayerInfo(msg);
				PlayerInfo oldPlayer = players.get(p.id);
				players.put(p.id, p);
				if (oldPlayer != null && chatActivity != null) {
					chatActivity.updatePlayer(p, oldPlayer);
				}
			}
			break;
		} case SendMessage: {
			Bais netFlags = msg.readFlags();
			boolean hasChannel = netFlags.readBool();
			boolean hasId = netFlags.readBool();
			Bais dataFlags = msg.readFlags();
			boolean isHtml = dataFlags.readBool();
			Channel chan = hasChannel ? channels.get(msg.readInt()) : null;
			PlayerInfo player = hasId ? players.get(msg.readInt()) : null;
			CharSequence message = msg.readString();
			if (hasId) {
				CharSequence color = (player == null ? "orange" : player.color.toHexString());
				CharSequence name = player.nick();
				if (isHtml) {
					message = Html.fromHtml("<font color='" + color + "'><b>" + name + 
							": </b></font>" + message);
				} else {
					message = Html.fromHtml("<font color='" + color + "'><b>" + name +
							": </b></font>" + StringUtilities.escapeHtml((String)message));
				}
			} else {
				message = isHtml ? Html.fromHtml((String)message) : message;
			}
			if (!hasChannel) {
				if (chatActivity != null && message.toString().contains("Wrong password for this name.")) 
					chatActivity.makeToast(message.toString(), "long");
				else {
					Iterator<Channel> it = joinedChannels.iterator();
					while (it.hasNext()) {
						it.next().writeToHist(message);
					}
				}
			} else {
				if (chan == null) {
					Log.e(TAG, "Received message for nonexistent channel");
				} else {
					chan.writeToHist(message);
				}
			}
			break;
		}
		case BattleList: {
			msg.readInt(); 
			int numBattles = msg.readInt();
			for (; numBattles > 0; numBattles--) {
				int battleId = msg.readInt();
				int player1 = msg.readInt();
				int player2 = msg.readInt();
				addBattle(battleId, new BattleDesc(player1, player2));
			}
			break;
		}
		case ChannelBattle: {
			msg.readInt(); 
			int battleId = msg.readInt();
			int player1 = msg.readInt();
			int player2 = msg.readInt();
			addBattle(battleId, new BattleDesc(player1, player2));
		}
		 case Logout: {
			int playerID = msg.readInt();
			removePlayer(playerID);
			break;
		} case BattleFinished: {
			int battleID = msg.readInt();
			byte battleDesc = msg.readByte();
			msg.readByte(); 
			int id1 = msg.readInt();
			int id2 = msg.readInt();
			Log.i(TAG, "bID " + battleID + " battleDesc " + battleDesc + " id1 " + id1 + " id2 " + id2);
			String[] outcome = new String[]{" won by forfeit against ", " won against ", " tied with "};
			if (battle != null && battle.bID == battleID || spectatedBattles.containsKey(battleID)) {
				if (battle.bID == battleID) {
				}
				if (battleDesc < 2) {
					joinedChannels.peek().writeToHist(Html.fromHtml("<b><i>" + 
							StringUtilities.escapeHtml(playerName(id1)) + outcome[battleDesc] + 
							StringUtilities.escapeHtml(playerName(id2)) + ".</b></i>"));
				}
				if (battleDesc == 0 || battleDesc == 3) {
					closeBattle(battleID);
				}
			}
			removeBattle(battleID);
			break;
		} case SendPM: {
			int playerID = msg.readInt();
			pmedPlayers.add(playerID);
			String pm = new String("This user is running the Pokemon Online Android client and cannot respond to private messages.");
			Baos bb = new Baos();
			bb.putInt(playerID);
			bb.putString(pm);
			socket.sendMessage(bb, Command.SendPM);
			break;
		} case BattleMessage: {
			msg.readInt(); 
			msg.readInt(); 
			if (battle != null)
				battle.receiveCommand(msg);
			break;
		} case EngageBattle: {
			int battleId = msg.readInt();
			Bais flags = msg.readFlags();
			byte mode = msg.readByte();
			int p1 = msg.readInt();
			int p2 = msg.readInt();
			addBattle(battleId, new BattleDesc(p1, p2, mode));
			if(flags.readBool()) { 
				BattleConf conf = new BattleConf(msg);
				battle = new Battle(conf, msg, players.get(conf.id(0)),
						players.get(conf.id(1)), mePlayer.id, battleId, this);
				joinedChannels.peek().writeToHist("Battle between " + playerName(p1) + 
						" and " + playerName(p2) + " started!");
				Intent in;
				in = new Intent(this, BattleActivity.class);
				in.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				startActivity(in);
				findingBattle = false;
			}
			break;
		} 
		case SpectateBattle: {
			Bais flags = msg.readFlags();
			int battleId = msg.readInt();
			if (flags.readBool()) {
				if (spectatedBattles.contains(battleId)) {
					Log.e(TAG, "Already watching battle " + battleId);
					return;
				}
	            BattleConf conf = new BattleConf(msg);
	            SpectatingBattle battle = new SpectatingBattle(conf, players.get(conf.id(0)), players.get(conf.id(1)), battleId, this);
	            spectatedBattles.put(battleId, battle);
	            Intent intent = new Intent(this, BattleActivity.class);
	            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
	            intent.putExtra("battleId", battleId);
	            startActivity(intent);
	        } else {
	            SpectatingBattle battle = spectatedBattles.remove(battleId);
	            if (battle != null && battle.activity != null) {
	            	battle.activity.end();
	            }
	        }
			break;
		}
		case SpectateBattleMessage: {
			int battleId = msg.readInt(); 
			msg.readInt(); 
			if (spectatedBattles.containsKey(battleId)) {
				spectatedBattles.get(battleId).receiveCommand(msg);
			}
			break;
		}
		case AskForPass: {
			salt = msg.readString();
			if (salt.length() < 6) { 
				System.out.println("Protocol Error: The server requires insecure authentication");
				break;
			}
			askedForPass = true;
			if (chatActivity != null && (chatActivity.hasWindowFocus() || chatActivity.progressDialog.isShowing())) {
				chatActivity.notifyAskForPass();
			}
			break;
		} case AddChannel: {
			addChannel(msg.readString(),msg.readInt());
			break;
		} case RemoveChannel: {
			int chanId = msg.readInt();
			if (chatActivity != null)
				chatActivity.removeChannel(channels.get(chanId));
			channels.remove(chanId);
			break;
		} case ChanNameChange: {
			int chanId = msg.readInt();
			if (chatActivity != null)
				chatActivity.removeChannel(channels.get(chanId));
			channels.remove(chanId);
			channels.put(chanId, new Channel(chanId, msg.readString(), this));
			break;
		} default: {
			System.out.println("Unimplented message");
		}
		}

	public void handleMsg(Bais msg) {
		byte i = msg.readByte();
		Command c = Command.values()[i];
		Log.d(TAG, "Received: " + c);
		switch (c) {
		case ChannelPlayers:
		case JoinChannel: 
		case LeaveChannel:{
			Channel ch = channels.get(msg.readInt());
			if(ch != null)
				ch.handleChannelMsg(c, msg);
			else
				Log.e(TAG, "Received message for nonexistent channel");
			break;
		} case VersionControl: {
			ProtocolVersion serverVersion = new ProtocolVersion(msg);
			if (serverVersion.compareTo(version) > 0) {
				Log.d(TAG, "Server has newer protocol version than we expect");
			} else if (serverVersion.compareTo(version) < 0) {
				Log.d(TAG, "PO Android uses newer protocol than Server");
			}
			serverSupportsZipCompression = msg.readBool();
			ProtocolVersion lastVersionWithoutFeatures = new ProtocolVersion(msg);
			ProtocolVersion lastVersionWithoutCompatBreak = new ProtocolVersion(msg);
			ProtocolVersion lastVersionWithoutMajorCompatBreak = new ProtocolVersion(msg);
			if (serverVersion.compareTo(version) > 0) {
				if (lastVersionWithoutFeatures.compareTo(version) > 0) {
					Toast.makeText(this, R.string.new_server_features_warning, Toast.LENGTH_SHORT).show();
				} else if (lastVersionWithoutCompatBreak.compareTo(version) > 0) {
					Toast.makeText(this, R.string.minor_compat_break_warning, Toast.LENGTH_SHORT).show();
				} else if (lastVersionWithoutMajorCompatBreak.compareTo(version) > 0) {
					Toast.makeText(this, R.string.major_compat_break_warning, Toast.LENGTH_LONG).show();
				}
			}
			serverName = msg.readString();
			if (chatActivity != null) {
				chatActivity.updateTitle();
			}
			break;
		} case Register: {
			break;
		} case Login: {
			Bais flags = msg.readFlags();
			Boolean hasReconnPass = flags.readBool();
			if (hasReconnPass) {
				reconnectSecret = msg.readQByteArray();
			}
			mePlayer = new PlayerInfo(msg);
			myid = mePlayer.id;
			int numTiers = msg.readInt();
			for (int j = 0; j < numTiers; j++) {
				msg.readString();
			}
			players.put(mePlayer.id, mePlayer);
			break;
		} case TierSelection: {
			msg.readInt(); 
			Tier prevTier = new Tier(msg.readByte(), msg.readString());
			prevTier.parentTier = superTier;
			superTier.subTiers.add(prevTier);
			while(msg.available() != 0) { 
				Tier t = new Tier(msg.readByte(), msg.readString());
				if(t.level == prevTier.level) { 
					prevTier.parentTier.addSubTier(t);
					t.parentTier = prevTier.parentTier;
				}
				else if(t.level < prevTier.level) { 
					while(t.level < prevTier.level)
						prevTier = prevTier.parentTier;
					prevTier.parentTier.addSubTier(t);
					t.parentTier = prevTier.parentTier;
				}
				else if(t.level > prevTier.level) { 
					prevTier.addSubTier(t);
					t.parentTier = prevTier;
				}
				prevTier = t;
			}
			break;
		} case ChannelsList: {
			int numChannels = msg.readInt();
			for(int j = 0; j < numChannels; j++) {
				int chanId = msg.readInt();
				Channel ch = new Channel(chanId, msg.readString(), this);
				channels.put(chanId, ch);
			}
			Log.d(TAG, channels.toString());
			break;
		} case PlayersList: {
			while (msg.available() != 0) { 
				PlayerInfo p = new PlayerInfo(msg);
				PlayerInfo oldPlayer = players.get(p.id);
				players.put(p.id, p);
				if (oldPlayer != null && chatActivity != null) {
					chatActivity.updatePlayer(p, oldPlayer);
				}
			}
			break;
		} case SendMessage: {
			Bais netFlags = msg.readFlags();
			boolean hasChannel = netFlags.readBool();
			boolean hasId = netFlags.readBool();
			Bais dataFlags = msg.readFlags();
			boolean isHtml = dataFlags.readBool();
			Channel chan = hasChannel ? channels.get(msg.readInt()) : null;
			PlayerInfo player = hasId ? players.get(msg.readInt()) : null;
			CharSequence message = msg.readString();
			if (hasId) {
				CharSequence color = (player == null ? "orange" : player.color.toHexString());
				CharSequence name = player.nick();
				if (isHtml) {
					message = Html.fromHtml("<font color='" + color + "'><b>" + name + 
							": </b></font>" + message);
				} else {
					message = Html.fromHtml("<font color='" + color + "'><b>" + name +
							": </b></font>" + StringUtilities.escapeHtml((String)message));
				}
			} else {
				message = isHtml ? Html.fromHtml((String)message) : message;
			}
			if (!hasChannel) {
				if (chatActivity != null && message.toString().contains("Wrong password for this name.")) 
					chatActivity.makeToast(message.toString(), "long");
				else {
					Iterator<Channel> it = joinedChannels.iterator();
					while (it.hasNext()) {
						it.next().writeToHist(message);
					}
				}
			} else {
				if (chan == null) {
					Log.e(TAG, "Received message for nonexistent channel");
				} else {
					chan.writeToHist(message);
				}
			}
			break;
		}
		case BattleList: {
			msg.readInt(); 
			int numBattles = msg.readInt();
			for (; numBattles > 0; numBattles--) {
				int battleId = msg.readInt();
				int player1 = msg.readInt();
				int player2 = msg.readInt();
				addBattle(battleId, new BattleDesc(player1, player2));
			}
			break;
		}
		case ChannelBattle: {
			msg.readInt(); 
			int battleId = msg.readInt();
			int player1 = msg.readInt();
			int player2 = msg.readInt();
			addBattle(battleId, new BattleDesc(player1, player2));
		}
		 case Logout: {
			int playerID = msg.readInt();
			removePlayer(playerID);
			break;
		} case BattleFinished: {
			int battleID = msg.readInt();
			byte battleDesc = msg.readByte();
			msg.readByte(); 
			int id1 = msg.readInt();
			int id2 = msg.readInt();
			Log.i(TAG, "bID " + battleID + " battleDesc " + battleDesc + " id1 " + id1 + " id2 " + id2);
			String[] outcome = new String[]{" won by forfeit against ", " won against ", " tied with "};
			if (battle != null && battle.bID == battleID || spectatedBattles.containsKey(battleID)) {
				if (battle != null && battle.bID == battleID) {
				}
				if (battleDesc < 2) {
					joinedChannels.peek().writeToHist(Html.fromHtml("<b><i>" + 
							StringUtilities.escapeHtml(playerName(id1)) + outcome[battleDesc] + 
							StringUtilities.escapeHtml(playerName(id2)) + ".</b></i>"));
				}
				if (battleDesc == 0 || battleDesc == 3) {
					closeBattle(battleID);
				}
			}
			removeBattle(battleID);
			break;
		} case SendPM: {
			int playerID = msg.readInt();
			pmedPlayers.add(playerID);
			String pm = new String("This user is running the Pokemon Online Android client and cannot respond to private messages.");
			Baos bb = new Baos();
			bb.putInt(playerID);
			bb.putString(pm);
			socket.sendMessage(bb, Command.SendPM);
			break;
		} case BattleMessage: {
			msg.readInt(); 
			msg.readInt(); 
			if (battle != null)
				battle.receiveCommand(msg);
			break;
		} case EngageBattle: {
			int battleId = msg.readInt();
			Bais flags = msg.readFlags();
			byte mode = msg.readByte();
			int p1 = msg.readInt();
			int p2 = msg.readInt();
			addBattle(battleId, new BattleDesc(p1, p2, mode));
			if(flags.readBool()) { 
				BattleConf conf = new BattleConf(msg);
				battle = new Battle(conf, msg, players.get(conf.id(0)),
						players.get(conf.id(1)), mePlayer.id, battleId, this);
				joinedChannels.peek().writeToHist("Battle between " + playerName(p1) + 
						" and " + playerName(p2) + " started!");
				Intent in;
				in = new Intent(this, BattleActivity.class);
				in.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				startActivity(in);
				findingBattle = false;
			}
			break;
		} 
		case SpectateBattle: {
			Bais flags = msg.readFlags();
			int battleId = msg.readInt();
			if (flags.readBool()) {
				if (spectatedBattles.contains(battleId)) {
					Log.e(TAG, "Already watching battle " + battleId);
					return;
				}
	            BattleConf conf = new BattleConf(msg);
	            SpectatingBattle battle = new SpectatingBattle(conf, players.get(conf.id(0)), players.get(conf.id(1)), battleId, this);
	            spectatedBattles.put(battleId, battle);
	            Intent intent = new Intent(this, BattleActivity.class);
	            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
	            intent.putExtra("battleId", battleId);
	            startActivity(intent);
	        } else {
	            SpectatingBattle battle = spectatedBattles.remove(battleId);
	            if (battle != null && battle.activity != null) {
	            	battle.activity.end();
	            }
	        }
			break;
		}
		case SpectateBattleMessage: {
			int battleId = msg.readInt(); 
			msg.readInt(); 
			if (spectatedBattles.containsKey(battleId)) {
				spectatedBattles.get(battleId).receiveCommand(msg);
			}
			break;
		}
		case AskForPass: {
			salt = msg.readString();
			if (salt.length() < 6) { 
				System.out.println("Protocol Error: The server requires insecure authentication");
				break;
			}
			askedForPass = true;
			if (chatActivity != null && (chatActivity.hasWindowFocus() || chatActivity.progressDialog.isShowing())) {
				chatActivity.notifyAskForPass();
			}
			break;
		} case AddChannel: {
			addChannel(msg.readString(),msg.readInt());
			break;
		} case RemoveChannel: {
			int chanId = msg.readInt();
			if (chatActivity != null)
				chatActivity.removeChannel(channels.get(chanId));
			channels.remove(chanId);
			break;
		} case ChanNameChange: {
			int chanId = msg.readInt();
			if (chatActivity != null)
				chatActivity.removeChannel(channels.get(chanId));
			channels.remove(chanId);
			channels.put(chanId, new Channel(chanId, msg.readString(), this));
			break;
		} default: {
			System.out.println("Unimplented message");
		}
		}
