/
area/
classes/net/sourceforge/pain/logic/
classes/net/sourceforge/pain/logic/event/
classes/net/sourceforge/pain/logic/fn/util/
classes/net/sourceforge/pain/network/console/
classes/net/sourceforge/pain/plugin/
classes/net/sourceforge/pain/plugin/reset/
classes/net/sourceforge/pain/plugin/shutdown/
classes/net/sourceforge/pain/plugin/social/
classest/net/sourceforge/pain/db/data/
doc/
doc/paindb/resources/
src/net/sourceforge/pain/logic/
src/net/sourceforge/pain/logic/event/
src/net/sourceforge/pain/logic/fn/util/
src/net/sourceforge/pain/network/console/
src/net/sourceforge/pain/network/console/telnet/
src/net/sourceforge/pain/plugin/
src/net/sourceforge/pain/plugin/command/
src/net/sourceforge/pain/plugin/reset/
src/net/sourceforge/pain/plugin/shutdown/
src/net/sourceforge/pain/plugin/social/
src/net/sourceforge/pain/util/
tests/
tests/net/sourceforge/pain/db/data/
package net.sourceforge.pain.util.rom;


import net.sourceforge.pain.util.*;

import java.io.*;
import java.util.*;


public class Rom24AreaLoader {

	private static String defaultDamType = "beating";
	private String fileName = null;
	private StringBuffer tmpBuffer = new StringBuffer();

	public String areaName;
	public String areaAuthor;
	public String levelRange;
	public String vnumRange;
	public String resetMessage;
	public String flags;

	public HashMap rooms = new HashMap();
	public HashMap mobiles = new HashMap();
	public HashMap objects = new HashMap();

	public Rom24AreaLoader(String fileName) {
		if (fileName == null) {
			throw new NullPointerException("area file name is null");
		}
		this.fileName = fileName;
	}

	public void load() throws Exception {
//		long starttime = System.currentTimeMillis();

		Log.info("opening file:" + fileName);
		BufferedReader reader = new BufferedReader(new FileReader(fileName), 50 * 1000);

		Log.info("opened");
		try {
			do {
				String state = getState(reader);

				if (state == null) {
					break;
				} else if (state.equals("#AREA")) {
					loadAreaHeaders(reader);
				} else if (state.equals("#RESETMESSAGE")) {
					loadResetMessage(reader);
				} else if (state.equals("#FLAG")) {
					loadFlags(reader);
				} else if (state.equals("#MOBILES")) {
					loadMobiles(reader);
				} else if (state.equals("#OBJECTS")) {
					loadObjects(reader);
				} else if (state.equals("#ROOMS")) {
					loadRooms(reader);
				} else if (state.equals("#RESETS")) {
					loadResets(reader);
				} else if (state.equals("#SHOPS")) {
					loadShops(reader);
				} else if (state.equals("#SPECIALS")) {
					loadSpecials(reader);
				} else if (state.equals("#PRACTICERS")) {
					loadPracticers(reader);
				} else if (state.equals("#OMPROGS")) {
					loadMobProgs(reader);
				} else if (state.equals("#OLIMITS")) {
					loadLimits(reader);
				} else {
					Log.warn("unknown state:" + state + " - ignoring");
				}
			} while (true);

		} finally {
			reader.close();
		}
//		System.out.println("time:" + (System.currentTimeMillis() - starttime));
	}

	private void loadAreaHeaders(BufferedReader reader) throws Exception {
		reader.readLine();// fileName
		String tmp = reader.readLine().trim();

		areaName = tmp.substring(0, tmp.length() - 1);
		levelRange = reader.readLine();
		vnumRange = reader.readLine();
	}

	private void loadResetMessage(BufferedReader reader) throws Exception {
		resetMessage = reader.readLine().split("~")[0];
	}

	private void loadFlags(BufferedReader reader) throws Exception {
		flags = reader.readLine();
	}

	private void loadMobiles(BufferedReader reader) throws Exception {
		Log.info("loading mobiles...");
		String line;

		while ((line = findLineStarted(reader, "#", false)) != null) {
			String id = line.substring(1).trim();

			if (Integer.parseInt(id) == 0) {
				break;
			}
			// ok its room and we have id;
			Log.info("mobile found, id =" + id);
			ROMMobile mobile = new ROMMobile(id);

			readUntilToken(reader, "~", true);
			line = tmpBuffer.toString().trim();

			mobile.nameList = line.split(" ");
			for (int i = 0; i < mobile.nameList.length; i++) {
				mobile.nameList[i] = mobile.nameList[i].toLowerCase();
			}
//			Log.debug("namelist:" + mobile.nameList[0]);

			readUntilToken(reader, "~", true);
			line = tmpBuffer.toString().trim();
			mobile.shortDesc = line;

			readUntilToken(reader, "~", true);
			line = tmpBuffer.toString().trim();
			mobile.longDesc = line;

			readUntilToken(reader, "~", true);
			line = tmpBuffer.toString().trim();
			mobile.lookDesc = line;

			readUntilToken(reader, "~", true);
			line = tmpBuffer.toString().trim();
			mobile.race = line;

			reader.readLine(); // \n
			line = reader.readLine();
			StringTokenizer st = new StringTokenizer(line, " \t");

			if (st.countTokens() != 4) {
				throw new IllegalArgumentException("wrong: VII.  Act, Affect, Alignment and group:" + line);
			}
			mobile.act = st.nextToken();
			mobile.affect = st.nextToken();
			mobile.alignment = st.nextToken();
			mobile.group = st.nextToken();

			line = reader.readLine();
			st = new StringTokenizer(line, " \t");
			if (st.countTokens() < 5) {
				throw new IllegalArgumentException("wrong: Level, hit bonus, hit dice, mana dice, damage, damtype: " + line);
			}
			mobile.level = Integer.parseInt(st.nextToken());
			mobile.hitBonus = Integer.parseInt(st.nextToken());

			mobile.hitDice = parseDice(st.nextToken());
			mobile.manaDice = parseDice(st.nextToken());
			mobile.damDice = parseDice(st.nextToken());
			mobile.damType = (st.hasMoreTokens() ? st.nextToken() : defaultDamType);

			line = reader.readLine();
			st = new StringTokenizer(line, " \t");
			if (st.countTokens() < 4) {
				throw new IllegalArgumentException("wrong: armor:" + line);
			}
			mobile.armor = new int[4];
			mobile.armor[0] = Integer.parseInt(st.nextToken());
			mobile.armor[1] = Integer.parseInt(st.nextToken());
			mobile.armor[2] = Integer.parseInt(st.nextToken());
			mobile.armor[3] = Integer.parseInt(st.nextToken());

			line = reader.readLine();
			st = new StringTokenizer(line, " \t");
			if (st.countTokens() != 4) {
				throw new IllegalArgumentException("wrong: offence, immune, resist, vuln:" + line);
			}

			mobile.offenses = st.nextToken();
			mobile.immunities = st.nextToken();
			mobile.resistances = st.nextToken();
			mobile.vulnerabilities = st.nextToken();

			//			line = reader.readLine();
			//			st = new StringTokenizer(line, " \t");
			//			mobile.offenses = st.nextToken();
			//			mobile.immunities = st.nextToken();
			//			mobile.resistances = st.nextToken();
			//			mobile.vulnerabilities = st.nextToken();

			line = reader.readLine();
			if (line.length() == 0) {
				line = reader.readLine(); // cells.are contains empty line
			}
			st = new StringTokenizer(line, " \t");
			mobile.position1 = st.nextToken();
			mobile.position2 = st.nextToken();
			mobile.gender = st.nextToken();
			mobile.treasure = Integer.parseInt(st.nextToken());
			// form, parst sizes - ignored;

			mobiles.put(mobile.vnum, mobile);

		}
	}

	private void loadObjects(BufferedReader reader) throws Exception {
		Log.info("loading objects...");
		String line;

		while ((line = findLineStarted(reader, "#", false)) != null) {
			String id = line.substring(1).trim();

			if (Integer.parseInt(id) == 0) {
				break;
			}
			// ok its room and we have id;
			Log.info("object found, id =" + id);
			ROMObject obj = new ROMObject(id);

			readUntilToken(reader, "~", true);
			line = tmpBuffer.toString().trim();

			obj.nameList = line.split(" ");

			readUntilToken(reader, "~", true);
			line = tmpBuffer.toString().trim();
			obj.shortDesc = line;

			readUntilToken(reader, "~", true);
			line = tmpBuffer.toString().trim();
			obj.longDesc = line;

			readUntilToken(reader, "~", true);
			line = tmpBuffer.toString().trim();
			obj.material = line;

			reader.readLine(); // \n

			line = reader.readLine();
			StringTokenizer st = new StringTokenizer(line, " \t");

			if (st.countTokens() != 3) {
				throw new IllegalArgumentException("wrong: Flags, Extras, Wear locations:" + line);
			}
			obj.typeFlags = st.nextToken();
			obj.extraFlags = st.nextToken();
			obj.wearFlags = st.nextToken();

			line = reader.readLine();
			String complexTokens[] = parseComplexTokens(line);

			if (complexTokens.length != 5) {
				throw new IllegalArgumentException("wrong: 	V0 through V4:" + line);
			}

			obj.value[0] = complexTokens[0];
			obj.value[1] = complexTokens[1];
			;
			obj.value[2] = complexTokens[2];
			;
			obj.value[3] = complexTokens[3];
			;
			obj.value[4] = complexTokens[4];
			;

			line = reader.readLine();
			st = new StringTokenizer(line, " \t");

			if (st.countTokens() != 4) {
				throw new IllegalArgumentException("wrong: <level> <weight> <cost> <cond>:" + line);
			}
			obj.level = Integer.parseInt(st.nextToken());
			obj.weight = Integer.parseInt(st.nextToken());
			obj.cost = Integer.parseInt(st.nextToken());
			obj.condition = st.nextToken();
			// + applies
			// + extraDescs
			objects.put(obj.vnum, obj);
		}
	}

	private void loadResets(BufferedReader reader) throws Exception {
		if (rooms.size() == 0 /*|| (mobiles.size() == 0 && objects.size() == 0)*/) { // offcol.are: rooms and resets!
			if (reader.readLine().trim().equals("S")) {
				return;
			}
			throw new RuntimeException("cant load resets before rooms, objects and mobiles!");
		}
		Log.info("loading resets...");
		// only MOB and OBJ reset loading implemented
		do {
			String line = reader.readLine();
			String comment = "";
			int commentStart = line.indexOf('*');
			if (commentStart > 0) {
				comment = line.substring(commentStart + 1);
				line = line.substring(0, commentStart - 1);
			}
			if (line.startsWith("O")) {
				Log.info("reading reset:" + line);
				StringTokenizer st = new StringTokenizer(line, " \t");

				if (st.countTokens() < 5) {
					Log.warn("IGNORING: wrong reset:" + line);
					continue;
				}
				ROMObjectReset reset = new ROMObjectReset();
				reset.comment = comment;

				st.nextToken();
				st.nextToken();
				reset.obj = (ROMObject) objects.get(st.nextToken());
				if (reset.obj == null) {
					Log.warn("IGNORING: can't find object for reset:" + line);
					continue;
				}
				reset.count = Integer.parseInt(st.nextToken());
				if (reset.count < 1 && reset.count != -1) {
					Log.warn("IGNORING: wrong reset count:" + line);
					continue;
				}

				//				String tmp = st.nextToken();
				ROMRoom room = (ROMRoom) rooms.get(st.nextToken());

				if (room == null) {
					Log.warn("IGNORING: can't find room for reset:" + line);
					continue;
				}
				room.resets.add(reset);
			} else if (line.startsWith("M")) {
				Log.info("reading reset:" + line);
				StringTokenizer st = new StringTokenizer(line, " \t");

				if (st.countTokens() < 6) {
					Log.warn("IGNORING: wrong reset:" + line);
					continue;
				}
				ROMMobileReset reset = new ROMMobileReset();
				reset.comment = comment;

				st.nextToken();
				st.nextToken();
				reset.mob = (ROMMobile) mobiles.get(st.nextToken());
				if (reset.mob == null) {
					Log.warn("IGNORING: can't find mobile for reset:" + line);
					continue;
				}
				int limit = Integer.parseInt(st.nextToken());

				if (reset.mob.limit != 0 && reset.mob.limit != limit) {
					if (reset.mob.limit < limit) {
						Log.warn("Mobile limit changed from:" + reset.mob.limit + " to:" + limit);
					} else {
						Log.warn("Mobile already has higher limit:" + reset.mob.limit + ". Ignoring" + limit);
					}
				}
				reset.mob.limit = limit;

				ROMRoom room = (ROMRoom) rooms.get(st.nextToken());

				if (room == null) {
					Log.warn("IGNORING: can't find room for reset:" + line);
					continue;
				}
				String countStr = st.nextToken();
				int commentIndex = countStr.indexOf("*");

				if (commentIndex > -1) { // there is values like '1*' / mount the doom
					countStr = countStr.substring(0, commentIndex);
				}
				reset.count = Integer.parseInt(countStr);
				if (reset.count < 1) {
					Log.warn("IGNORING: wrong reset count:" + line);
					continue;
				}
				room.resets.add(reset);
			} else if (line.startsWith("S")) {
				break;
			} else {
				Log.info("ignoring unsupported reset:" + line);
				continue;
			}
		} while (true);
	}

	private void loadShops(BufferedReader reader) {
	}

	private void loadSpecials(BufferedReader reader) {
	}

	private void loadPracticers(BufferedReader reader) {
	}

	private void loadMobProgs(BufferedReader reader) {
	}

	private void loadLimits(BufferedReader reader) {
	}

	private void loadRooms(BufferedReader reader) throws Exception {
		Log.info("loading rooms...");
		String line;

		while ((line = findLineStarted(reader, "#", false)) != null) {
			String id = line.substring(1).trim();

			if (Integer.parseInt(id) == 0) {
				break;
			}
			// ok its room and we have id;
			Log.info("room found, id =" + id);
			ROMRoom room = new ROMRoom(id);

			// reading name
			readUntilToken(reader, "~", true);
			line = tmpBuffer.toString().trim();

			room.name = line;
			// reading name
			if (readUntilToken(reader, "~", false)) {
				room.desc = tmpBuffer.toString().trim();
			}
			//			Log.info("loading exits for room");
			// ok now load exits
//			String[] roomExits = new String[6];

			do {
				line = reader.readLine().trim().toUpperCase();
				if (line.length() == 2 && line.charAt(0) == 'D') { // ok its exit
					int direction = line.charAt(1) - '0';
					readUntilToken(reader, "~", true);
					String exitDescr = tmpBuffer.toString();
					readUntilToken(reader, "~", true);
					reader.readLine(); // \r\n
					line = reader.readLine().trim();
					line = line.substring(line.lastIndexOf(' ') + 1, line.length());
					room.exits[direction] = line;
					room.exitsDescs[direction] = exitDescr;
				}
			} while (!line.equals("S"));

			rooms.put(room.vnum, room);
			//			Log.info("------------------------");
			//			Log.info("room added: rom id=" + id);
			//			Log.info(" name=" + room.desc);
			//			Log.info(" desc=" + room.name);
		}
		Log.info("rooms loaded OK");
	}

	private static String findLineStarted(BufferedReader reader, String token, boolean required) throws Exception {
//		int c1 = token.charAt(token.length() - 1);

		String line;

		do {
			line = reader.readLine();
			if (line == null) {
				break;
			}
			if (line.startsWith(token)) {
				return line;
			}
		} while (true);
		if (required) {
			throw new Exception("required linestart not found:" + token);
		}
		return null;
	}

	private boolean readUntilToken(BufferedReader reader, String token, boolean required) throws Exception {
		if (tmpBuffer.length() > 0) {
			tmpBuffer = new StringBuffer();
		}
		final int tokenLen = token.length();
		final int c1 = token.charAt(tokenLen - 1);
		int bufLen;
		while (true) {
			int c2 = reader.read();
			if (c2 == -1) {
				if (required) {
					Log.info("Buffer before error:" + tmpBuffer.toString());
					throw new Exception("required token not found:" + token);
				} else {
					return false;
				}
			}
			tmpBuffer.append((char) c2);

			if (c2 == c1 && (bufLen = tmpBuffer.length()) >= tokenLen) {
				boolean match = true;
				for (int i = 2; i <= tokenLen; i++) {
					if (tmpBuffer.charAt(bufLen - i) != token.charAt(tokenLen - i)) {
						match = false;

					}
				}
				if (match) {
					tmpBuffer.setLength(bufLen - tokenLen);
					break;
				}
			}
		}
		return true;
	}

	private static String getState(BufferedReader reader) throws Exception {
		String state;

		do {
			state = reader.readLine();
			if (state == null) {
				return null;
			}

			if (state.startsWith("#")) {
				state = state.trim().toUpperCase();
				if (state.equals("#$")) {
					return null;
				}
				return state;
			}
		} while (true);
	}

	private static int[] parseDice(String dice) {
		dice = dice.toUpperCase();
		int indexD = dice.indexOf("D");

		if (indexD < 0) {
			throw new IllegalArgumentException("wrong dice:" + dice);
		}
		int indexA = dice.indexOf("+", indexD);

		if (indexA < 0) {
			throw new IllegalArgumentException("wrong dice:" + dice);
		}
		int dices[] = new int[3];

		dices[0] = Integer.parseInt(dice.substring(0, indexD));
		dices[1] = Integer.parseInt(dice.substring(indexD + 1, indexA));
		dices[2] = Integer.parseInt(dice.substring(indexA + 1));
		return dices;
	}

	private String[] parseComplexTokens(String line) {
		if (tmpBuffer.length() > 0) {
			tmpBuffer = new StringBuffer();
		}
		ArrayList list = new ArrayList();
		boolean outQuote = true;

		for (int i = 0; i < line.length(); i++) {
			char c = line.charAt(i);

			if ((c == ' ' || c == '\t') && outQuote) {
				if (tmpBuffer.length() > 0) {
					list.add(tmpBuffer.toString());
					tmpBuffer = new StringBuffer();
				}
			} else if (c == '\'') {
				if (tmpBuffer.length() > 0) {
					list.add(tmpBuffer.toString());
					tmpBuffer = new StringBuffer();
				} else {
					if (!outQuote) { // '' exp
						list.add(null);
					}
				}
				outQuote = !outQuote;
			} else {
				tmpBuffer.append(c);
			}
		}
		if (tmpBuffer.length() > 0) {
			list.add(tmpBuffer.toString());
			tmpBuffer = new StringBuffer();
		}

		String result[] = new String[list.size()];

		for (int i = 0; i < list.size(); i++) {
			result[i] = (String) list.get(i);
		}
		return result;
	}
}