* @param client
	 */
	@SuppressWarnings("unchecked")
	public void op_roomedit(final String input, final Client client) {
		final Player player = getPlayer(client);

		String rcmd = "";
		String rarg = "";

		EditorData data = player.getEditorData();

		if (input.indexOf(" ") != -1) {
			rcmd = input.substring(0, input.indexOf(" ")).toLowerCase();
			rarg = input.substring(input.indexOf(" ") + 1, input.length());
		}
		else {
			rcmd = input.substring(0, input.length()).toLowerCase();
		}

		debug("REDIT CMD");
		debug("rcmd: \"" + rcmd + "\"");
		debug("rarg: \"" + rarg + "\"");

		if (rcmd.equals("abort")) {
			// clear edit flag
			((Room) data.getObject("room")).Edit_Ok = true;

			// exit
			send("< Exiting... >", client);

			// reset editor and player status
			player.setStatus((String) data.getObject("pstatus"));
			player.setEditor(Editors.NONE);
		}
		else if (rcmd.equals("addexit")) {
			// addexit <name> <destination dbref>
			String[] args = rarg.split(" ");

			// if
			if (args.length > 1) {
				final int destination = Integer.parseInt(args[1]);
				final Room r = objectDB.getRoomById(destination);

				if (r != null) {
					Room r1 = (Room) data.getObject("room");
					data.addObject("e|" + args[0], new Exit(args[0], r1.getDBRef(), destination));
					send("Ok.", client);
				}
			}
		}
		else if (rcmd.equals("addthing")) {
			// addthing <thing name/dbref>

			// result:
			// key: add or addthing
			// value: object/dbref of object
			// ex. add, 4
			// ex. addthing, 4

			int dbref = Utils.toInt(rarg, -1);

			final MUDObject m;

			if (dbref != -1) m = objectDB.getById(dbref);
			else             m = objectDB.getByName(rarg);

			if (m != null) {
				if (m instanceof Thing) {
					final Thing thing = (Thing) m;

					Room room = (Room) data.getObject("room");
					Room room1 = objectDB.getRoomById(thing.getLocation());

					room1.removeThing(thing);
					room.addThing(thing);
					thing.setLocation(room.getDBRef());

					send("Ok.", client);
				}
			}
			else { // create new thing and set the name to rarg's value if it's not a number?
				Room room = (Room) data.getObject("room");

				Thing thing = new Thing(rarg);
				room.addThing(thing);
				thing.setLocation(room.getDBRef());

				objectDB.add(thing);
				send("Ok.", client);
			}
		}
		else if (rcmd.equals("additem")) {
			// TODO Fix this, since it shouldn't actually create the item until
			// the room is saved
			Room room = (Room) data.getObject("room");

			if (prototypes.containsKey(rarg)) {
				Item item = createItem(rarg, false);

				item.setLocation(room.getDBRef());

				// TODO fix getObjects somehow
				// final String suffix = "" + data.getObjects("i|" +
				// item.getName()).size();

				// debug("REDIT (suffix): " + suffix);

				/*
				 * if( suffix.equals("0") ) { data.addObject("i|" +
				 * item.getName(), item); } else { data.addObject("i|" +
				 * item.getName() + suffix, item); }
				 */

				// TODO fix this kludge later
				boolean test = false;
				int suffix = 0;

				while (!test) {
					if (suffix == 0) test = data.addObject("i|" + item.getName(), item);
					else             test = data.addObject("i|" + item.getName() + suffix, item);

					suffix++;
				}

				send("Ok.", client);
			}
		}
		else if (rcmd.equals("desc")) {
			String[] rargs = rarg.split(" ");

			if (rargs.length > 1) {
				if (rargs[0].equalsIgnoreCase("-f")) {
					// ?
					data.setObject("desc", rarg);
				}
				else {
					data.setObject("desc", rarg);
				}
			}
			else {
				data.setObject("desc", rarg);
			}

			send("Ok.", client);
		}
		else if (rcmd.equals("dim")) {
			String[] args = rarg.split(" ");

			if (args.length > 1) {
				try {
					System.out.println("Args: \"" + args[0] + "\" \"" + args[1] + "\"");

					Integer dim = Integer.parseInt(args[1]);

					System.out.println("key: " + args[0] + " value: " + dim);

					if (args[0].toLowerCase().equals("x")) {
						data.setObject("x", dim);
						send("Ok.", client);
					}
					else if (args[0].toLowerCase().equals("y")) {
						data.setObject("y", dim);
						send("Ok.", client);
					}
					else if (args[0].toLowerCase().equals("z")) {
						data.setObject("z", dim);
						send("Ok.", client);
					}
					else {
						send("Invalid Dimension.", client);
					}
				}
				catch (final NumberFormatException nfe) {
					debug("op_roomedit(): the specified dimension size is invalid (number format exception)");
					System.out.println("--- Stack Trace ---");
					nfe.printStackTrace();
				}
			}
		}
		else if (rcmd.equals("dirset")) {
			// dirset <direction> <exit dbref>
			// ex. dirset NORTH 28
			// needs to be a valid direction and a valid exit
			String[] args = rarg.split(" ");

			if (args.length == 2) {
				final List<String> cardinal_directions = Utils.mkList("north",
						"northeast", "northwest", "south", "southeast",
						"southwest", "east", "west");

				debug("REDIT: direction = " + args[0]);
				debug("REDIT: exit dbref = " + args[1]);

				// direction validation
				if (cardinal_directions.contains(args[0].toLowerCase())) {
					Integer dbref = Utils.toInt(args[1], -1);

					if (dbref != -1) {
						final Room room = (Room) data.getObject("room");
						final Exit exit = getExit(dbref);

						if (room.getExits().contains(exit)) {
							data.addObject("dirset:" + args[0].toLowerCase(), dbref);
							send("Ok.", client);
						}
						else send("Valid Exit, but it's not attached to this room.", client);
					}
					else send("Invalid Exit!", client);
				}
				else send("Invalid Argument - non-cardinal direction", client);
			}
			else send("Invalid Arguments - incorrect number of argments", client);
		}
		else if (rcmd.equals("done")) {
			// save changes
			op_roomedit("save", client);

			// clear edit flag
			((Room) data.getObject("room")).Edit_Ok = true;

			// exit
			send("< Exiting... >", client);

			// reset editor and player status
			player.setStatus((String) data.getObject("pstatus"));
			player.setEditor(Editors.NONE);
		}
		else if (rcmd.equals("flags")) {
			final ObjectFlag[] flags = new ObjectFlag[] {
					ObjectFlag.BANK,     ObjectFlag.DARK,     ObjectFlag.ENTER_OK,
					ObjectFlag.FORGE,    ObjectFlag.GUEST,    ObjectFlag.HOUSE,
					ObjectFlag.MERCHANT, ObjectFlag.NO_ENTER, ObjectFlag.NO_LEAVE,
					ObjectFlag.NO_TELEPORT
			};

			for (final ObjectFlag flag : flags) {
				if (MudUtils.isAllowed(flag, TypeFlag.ROOM)) {
					send(flag.getName(), client);
				}
			}
		}
		else if (rcmd.equals("help")) {
			if (rarg.equals("")) {
				// output help information
				final List<String> output = (List<String>) Utils.mkList("Room Editor -- Help",
						Utils.padRight("", '-', 74),
						"abort                           abort the editor (no changes will be kept)",
						"addexit <name> <destination>    creates a new exit",
						"additem <prototype key>         creates a new instance of the indicated",
						"                                prototype",
						"addthing <name/dbref>           move an existing object to the room (not",
						"                                sure how it should work)",
						"desc <param> <new description>  change/set the room description",
						"dim <dimension> <size>          change a dimension of the room (x/y/z)",
						"dirset <direction> <exit dbref> associate an exit with a cardinal direction",
						"done                            finish editing (save & exit)",
						"help                            shows this help information",
						"items                           list available item prototypes",
						"npcs",
						"layout                          display a 2D layout visualization",
						"modify                          change layout",
						"name <new name>                 change/set the room name",
						"rooms                           list the other rooms that are in the same",
						"                                zone as this one",
						"save                            save changes to the room",
						"setlocation                     change the location (deprecated?)",
						"setzone                         set the zone that this room belongs to",
						"show                            show basic information about the room",
						"trigger <type> <data>           setup a trigger of the specified type with",
						"                                the specified data",
						"zones                           list the zones that exist",
						Utils.padRight("", '-', 74)
						);

				client.write(output);
			}
			else {
				// output help information specific to the command name given
			}
		}
		else if (rcmd.equals("items")) {
			final List<String> output = new LinkedList<String>();

			for (final String s : prototypes.keySet()) {
				output.add(s);
			}

			client.write(output);
		}
		else if (rcmd.equals("layout")) {
			int width = (Integer) data.getObject("x");
			int length = (Integer) data.getObject("y");

			char[] tiles = ((Room) data.getObject("room")).tiles;
			
			final List<String> sendList = new LinkedList<String>();
			final StringBuilder sb = new StringBuilder();
			
			sendList.add("_ : open space, X : wall, / : door");

			for (int y = 0; y < length; y++) {
				for (int x = 0; x < width; x++) {
					if (x < (width - 1)) sb.append("|" + tiles[y * width + x]);
					else                  sb.append("|" + tiles[y * width + x] + "|");
				}
				
				sendList.add(sb.toString());
				sb.delete(0, sb.length());
			}
			
			send(sendList, client);
			send("Ok.", client);
		}
		else if( rcmd.equals("modify") ) {
			// modify layout 0,0 [5,1] X

			//String expr1 = "(modify)(\\s+)(layout)(\\s+)(\\d+)(,)(\\d+)(\\s+)([_X])";
			//String expr2 = "(modify)(\\s+)(layout)(\\s+)(\\d+)(,)(\\d+)(\\s+)(\\d+)(,)(\\d+)(\\s+)([_X])";

			// "(modify)(\s+)(layout)(\s+)(\d+)(,)(\d+)(\s+)(\d+)(,)(\d+)(\s+)([_\\X])"
			//if( rarg.matches(expr1) || rarg.matches(expr2) ) {
			final String[] args = rarg.split(" ");

			int width = (Integer) data.getObject("x");
			int length = (Integer) data.getObject("y");

			char[] tiles = ((Room) data.getObject("room")).tiles;

			if( args[0].equalsIgnoreCase("layout") ) {
				if( args.length == 3 ) {
					String[] coord = args[1].split(",");
					
					int x = Utils.toInt(coord[0], -1);
					int y = Utils.toInt(coord[1], -1);

					char c = args[2].charAt(0);
					
					if( Utils.range(x, 0, width - 1) && Utils.range(y, 0, length -1) ) {
						tiles[y * width + x] = c;

						send("Ok.", client);
					}
					else {
						send("Incorrect Coordinates", client);
					}

					// width: 5 (x), length: 8 (y)
					// X Y -> 5 x 8 -> 40, 0-39
					// 4, 6
				}
				else if( args.length == 4 ) {
					String[] coord = args[1].split(",");
					String[] dimension = args[2].split(",");
					
					int x = Utils.toInt(coord[0], -1);
					int y = Utils.toInt(coord[1], -1);

					int w = Utils.toInt(dimension[0], 0);
					int l = Utils.toInt(dimension[1], 0);

					char c = args[3].charAt(0);

					// Bounds Checking
					if( Utils.range(x, 0, width - 1) && Utils.range(y, 0, length - 1) ) {
						if( Utils.range(w, x, width) && Utils.range(l, y, length)) {
							debug("Initial index value: " + ((y * width) + x));

							for(int n = 0; n < l; n++) {
								for(int p = 0; p < w; p++) {
									tiles[(y + n) * width + (x + p)] = c;
								}
							}

							send("Ok.", client);
						}

						send("Wrong Bounds", client);
					}

					send("Incorrect Coordinates", client);
				}
				else {
					send("Incorrect Parameters,", client);
				}
			}
			else {
				send("Syntax Error.", client);
			}
		}
		else if (rcmd.equals("name")) {
			data.setObject("name", rarg);
			send("Ok.", client);
		}
		else if (rcmd.equals("rooms")) {
			final Zone z = (Zone) data.getObject("zone");

			if (z != null) {
				final List<String> sendList = Utils.mkList();
				
				for (final Room rm : z.getRooms()) {
					sendList.add(rm.getName() + " (#" + rm.getDBRef() + ")");
				}
				
				send(sendList, client);
			}
			else send("Invalid Zone!", client);
		}
		// TODO improve this
		// TODO consider using a bitset/flags or something to set only changed data
		// TODO consider making editor objects and moving this kind of code into them
		else if (rcmd.equals("save")) {
			//rsave(data);
			Room room = (Room) data.getObject("room");
			
			// NOTE: no bitset is created in editing a room, yet (4-5-2016)
			/*BitSet b = ((BitSet) data.getObject("dirty"));
			
			int n = 0;
			
			final int NAME = 0;
			final int FLAGS = 1;
			final int DESC = 2;
			final int LOC = 3; 

			while(n < 4) {
				boolean dirty = b.get(n);
				
				switch(n) {
				case NAME:  if( dirty ) room.setName((String) data.getObject("name")); break;
				case FLAGS: if( dirty ) room.setFlags((EnumSet<ObjectFlag>) data.getObject("flags")); break;
				case DESC:  if( dirty ) room.setDesc((String) data.getObject("desc")); break;
				case LOC:   if( dirty ) room.setLocation((Integer) data.getObject("location")); break;
				default:    break;
				}
				
				n++;
			}*/
			
			// save room data
			room.setName((String) data.getObject("name"));
			room.setFlags((EnumSet<ObjectFlag>) data.getObject("flags"));
			room.setDesc((String) data.getObject("desc"));
			room.setLocation((Integer) data.getObject("location"));

			room.setDimension("x", (Integer) data.getObject("x"));
			room.setDimension("y", (Integer) data.getObject("y"));
			room.setDimension("z", (Integer) data.getObject("z"));

			if (room.getZone() != null) {
				room.getZone().removeRoom(room);
			}

			room.setZone((Zone) data.getObject("zone"));

			if (room.getZone() != null) {
				room.getZone().addRoom(room);
			}

			// save exits
			for (final String s : data.getKeysByPrefix("e|")) {
				final Exit e = (Exit) data.getObject(s);

				if (!(room.getExits().contains(e))) {
					objectDB.add(e);
					objectDB.addAsNew(e);
					objectDB.addExit(e);

					room.addExit(e);
				}
			}

			// save items
			for (final String s : data.getKeysByPrefix("i|")) {
				final Item item = (Item) data.getObject(s);

				if (!(room.getItems().contains(item))) {
					initCreatedItem(item);
					room.addItem(item);
				}
			}

			// save direction settings
			for (final String s : data.getKeysByPrefix("dirset:")) {
				final Integer dbref = (Integer) data.getObject(s);
				final Exit exit = getExit(dbref);
				
				int temp = s.indexOf(":");
				
				// TODO check to see if temp is -1?
				final Direction dir = Direction.getDirection(s.substring(temp + 1, s.length()));
				
				room.setDirectionExit(dir.getValue(), exit);
			}

			// Save Triggers
			if (data.getObject("onEnter") != null) {
				for (final Trigger t : (LinkedList<Trigger>) data.getObject("onEnter")) {
					room.setTrigger(TriggerType.onEnter, t);
				}
			}

			if (data.getObject("onLeave") != null) {
				for (final Trigger t : (LinkedList<Trigger>) data.getObject("onLeave")) {
					room.setTrigger(TriggerType.onLeave, t);
				}
			}

			send("Room saved.", client);
		}
		else if (rcmd.equals("setflag")) {
			final EnumSet<ObjectFlag> flags = (EnumSet<ObjectFlag>) data.getObject("flags");

			ObjectFlag flag = null;

			if (rarg.startsWith("!")) {
				if (flag == null) flag = ObjectFlag.fromString(rarg.substring(1));
				if (flag == null) flag = ObjectFlag.fromLetter(rarg.charAt(1));
			}
			else {
				if (flag == null) flag = ObjectFlag.fromString(rarg);
				if (flag == null) flag = ObjectFlag.fromLetter(rarg.charAt(0));
			}

			if (flag == null) {
				send("Invalid Flag!", client);
				return;
			}

			if (rarg.startsWith("!")) {
				flags.remove(flag);
				send(flag.name() + " flag removed.", client);
			}
			else {
				flags.add(flag);
				send(flag.name() + " flag set.", client);
			}

			data.setObject("flags", flags);
			send("Ok.", client);
		}
		else if (rcmd.equals("setlocation")) {
			data.setObject("location", Utils.toInt(rarg, -1));
			send("Ok.", client);
		}
		else if (rcmd.equals("setzone")) {
			// data.setObject("zone", getZone(Utils.toInt(rarg, -1)));
			data.setObject("zone", getZone(rarg));
			send("Ok.", client);
		}
		else if (rcmd.equals("show")) {
			Room room = (Room) data.getObject("room");

			// will be a little like examine, just here to show changes
			send("--- Room Editor " + Utils.padRight("", '-', 80 - 16), client);
			// send(Utils.padRight("", '-', 80), client);
			send("DB Reference #: " + room.getDBRef(), client);
			send("Name: " + data.getObject("name"), client);
			send("Flags: " + ((EnumSet<ObjectFlag>) data.getObject("flags")).toString(), client);
			send("Dimensions:", client);
			send("    X: " + (Integer) data.getObject("x"), client);
			send("    Y: " + (Integer) data.getObject("y"), client);
			send("    Z: " + (Integer) data.getObject("z"), client);
			send("Description:", client);
			
			showDesc((String) data.getObject("desc"), 80, client);
			
			send("Location: " + (Integer) data.getObject("location"), client);

			final Zone zone = (Zone) data.getObject("zone");

			if (zone != null) send("Zone: " + colors(zone.getName(), "purple2"), client);
			else              send("Zone: null");

			if (player.getConfigOption("compact-editor")) {
				StringBuilder sb = new StringBuilder();

				List<String> exitStrings = new LinkedList<String>();
				List<String> itemStrings = new LinkedList<String>();

				send(colors(Utils.padRight("Exits:", ' ', 35), getDisplayColor("exit")) + " " + colors(Utils.padRight("Items:", ' ', 35), getDisplayColor("item")), client);

				for (final String s : data.getKeysByPrefix("i|")) {
					System.out.println(s);
					System.out.println("Item Found");

					final Item item = (Item) data.getObject(s);

					if (item.getDBRef() == -1) itemStrings.add(colors(item.getName() + "(#" + item.getDBRef() + ")", "green"));
					else                       itemStrings.add(item.getName() + "(#" + item.getDBRef() + ")");
				}

				for (final String s : data.getKeysByPrefix("e|")) {
					System.out.println(s);
					System.out.println("Exit Found");

					final Exit exit = (Exit) data.getObject(s);

					exitStrings.add(exit.getName() + "(#" + exit.getDBRef() + ") - Source: " + exit.getLocation() + " Dest: " + exit.getDestination());
				}

				// the below will cause null pointer exceptions if one empties
				// before the other
				while (!itemStrings.isEmpty() || !exitStrings.isEmpty()) {
					String exitString = "", itemString = "";

					if (!exitStrings.isEmpty()) exitString = exitStrings.remove(0);
					if (!itemStrings.isEmpty()) itemString = itemStrings.remove(0);

					sb.append(Utils.padRight(exitString, ' ', 35) + " " + Utils.padRight(itemString, ' ', 35));

					send(sb.toString(), client);

					sb.delete(0, sb.length());
				}
			}
			else {
				send(colors("Exits:", getDisplayColor("exit")), client);

				for (final String s : data.getKeysByPrefix("e|")) {
					System.out.println(s);
					System.out.println("Exit Found");

					final Exit exit = (Exit) data.getObject(s);

					send(exit.getName() + "(#" + exit.getDBRef() + ") - Source: " + exit.getLocation() + " Dest: " + exit.getDestination(), client);
				}

				send(colors("Items:", getDisplayColor("item")), client);

				for (final String s : data.getKeysByPrefix("i|")) {
					System.out.println(s);
					System.out.println("Item Found");

					final Item item = (Item) data.getObject(s);

					send(item.getName() + "(#" + item.getDBRef() + ")", client);
				}
			}

			send(Utils.padRight("", '-', 80), client);
		}
		else if (rcmd.equals("trigger")) {
			String[] rargs = rarg.split(" ");

			if (rargs.length >= 2) {
				int type = Utils.toInt(rargs[0], -1);

				if (type == 0) {
					if (data.getObject("onEnter") == null) {
						data.addObject("onEnter", new LinkedList<Trigger>());
					}

					List<String> temp = Arrays.asList(rargs);
					temp = temp.subList(1, temp.size());

					((LinkedList<Trigger>) data.getObject("onEnter")).add(new Trigger(Utils.join(temp, " ")));

					send("Ok.", client);
				}
				else if (type == 1) {
					if (data.getObject("onLeave") == null) {
						data.addObject("onLeave", new LinkedList<Trigger>());
					}

					List<String> temp = Arrays.asList(rargs);
					temp = temp.subList(1, temp.size());

					((LinkedList<Trigger>) data.getObject("onLeave")).add(new Trigger(Utils.join(rargs, " ")));

					send("Ok.", client);
				}
				else {
					send("trigger: Bad trigger type.", client);
				}
			}
			else {
				send("trigger: Bad arguments.", client);
			}
		}
		else if (rcmd.equals("zones")) {
			send("Zones", client);
			send(Utils.padRight("", '-', 40), client);

			if ("".equals(rarg)) {
				for (Zone zone : zones.keySet()) {
					send("" + zone.getName() + " ( " + zone.getRooms().size() + " Rooms )", client);
				}
			}
			else {
				for (Zone zone : zones.keySet()) {
					if (zone.getName().toLowerCase().startsWith(rarg.toLowerCase())) {
						send("" + zone.getName() + " ( " + zone.getRooms().size() + " Rooms )", client);
					}
				}
			}
		}
		else {
			send("No such command.");
		}