package com.hero.objects.powers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

import org.jdom.Element;

import com.hero.HeroDesigner;
import com.hero.objects.Adder;
import com.hero.objects.GenericObject;
import com.hero.util.XMLUtility;

/**
 * Copyright (c) 2000 - 2005, CompNet Design, Inc. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, is prohibited unless the following conditions are met: 1.
 * Express written consent of CompNet Design, Inc. is obtained by the developer.
 * 2. Redistributions must retain this copyright notice. THIS SOFTWARE IS
 * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * @author CompNet Design, Inc.
 * @version $Revision$
 */

public class SenseAffectingPower extends Power {
	protected double nontargetingCost;

	protected double nontargetingGroupCost;

	protected double nontargetingHalfCost;

	protected double nontargetingSenseCost;

	protected boolean oldMethod;

	protected double targetingCost;

	protected double targetingGroupCost;

	protected double targetingHalfCost;

	protected double targetingSenseCost;

	protected boolean restoring;

	protected long assignedAddersLastCall;

	protected ArrayList<Adder> availableAddersSaver;

	protected long availableAddersLastCall;

	protected ArrayList<Adder> optionsSaver;

	protected long optionsLastCall;

	protected Adder selectedOptionSaver;

	protected long selectedOptionLastCall;

	public SenseAffectingPower(Element root) {
		super(root);
	}

	public SenseAffectingPower(Element root, String id) {
		super(root, id);
	}

	public ArrayList<Adder> getAssignedAdders() {
		long current = System.currentTimeMillis();
		if ((assignedAddersLastCall > 0)
				&& (assignedAddersLastCall > GenericObject.lastEdit)
				&& (assignedAdders != null)) {
			return assignedAdders;
		}

		ArrayList<Adder> ret = super.getAssignedAdders();
		if (oldMethod) {
			return ret;
		}
		assignedAddersLastCall = current;
		ArrayList<Adder> adjusted = new ArrayList<Adder>();
		ArrayList<SenseGroup> allGroups = SenseGroup.getAllGroups();
		ArrayList<Sense> allSenses = Sense.getAllSenses();
		// take care of the conversions from old to new...
		for (Adder ad : ret) {
			if (ad.getXMLID().equals("ADDITIONAL_GROUP")
					|| ad.getXMLID().equals("GROUP")
					|| ad.getXMLID().equals("SENSEGROUP")) {
				if (ad.getSelectedOption() == null) {
					adjusted.add(ad);
				} else {
					GROUPS: for (SenseGroup group : allGroups) {
						if (group.getXMLID().equals(
								ad.getSelectedOption().getXMLID())) {
							if (group.getDefaultSenseAdders().contains(
									"TARGETINGSENSE")
									&& (targetingGroupCost >= 0)) {
								Adder swap = new Adder(group);
								swap.setLevelCost(-1);
								swap.setLevelValue(-1);
								swap.setAvailableAdders(new ArrayList<Adder>());
								swap.setBaseCost(targetingGroupCost);
								swap.setExclusive(true);
								swap
										.setAlias(ad.getSelectedOption()
												.getAlias());
								swap.setSelected(true);
								swap.setIncludeInBase(true);
								adjusted.add(swap);
							} else if (!group.getDefaultSenseAdders().contains(
									"TARGETINGSENSE")
									&& (nontargetingGroupCost >= 0)) {
								Adder swap = new Adder(group);
								swap.setLevelCost(-1);
								swap.setLevelValue(-1);
								swap.setAvailableAdders(new ArrayList<Adder>());
								swap.setBaseCost(nontargetingGroupCost);
								swap.setExclusive(true);
								swap
										.setAlias(ad.getSelectedOption()
												.getAlias());
								swap.setSelected(true);
								swap.setIncludeInBase(true);
								adjusted.add(swap);
							}
						}
						break GROUPS;
					}
				}
			} else if (ad.getXMLID().equals("ADDITIONAL_SENSE")
					|| ad.getXMLID().equals("SINGLE")) {
				if (ad.getSelectedOption() == null) {
					adjusted.add(ad);
				} else {
					SENSES: for (Sense sense : allSenses) {
						if (sense.getXMLID().equals(
								translateSense(ad.getSelectedOption()
										.getXMLID()))) {
							if (sense.getBuiltInSenseAdders().contains(
									"TARGETINGSENSE")
									&& (targetingSenseCost >= 0)) {
								Adder swap = new Adder(sense);
								swap.setLevelCost(-1);
								swap.setLevelValue(-1);
								swap.setAvailableAdders(new ArrayList<Adder>());
								swap.setBaseCost(targetingSenseCost);
								swap
										.setAlias(ad.getSelectedOption()
												.getAlias());
								swap.setSelected(true);
								swap.setExclusive(true);
								swap.setIncludeInBase(true);
								adjusted.add(swap);
							} else if (!sense.getBuiltInSenseAdders().contains(
									"TARGETINGSENSE")
									&& (nontargetingSenseCost >= 0)) {
								Adder swap = new Adder(sense);
								swap.setLevelCost(-1);
								swap.setLevelValue(-1);
								swap.setAvailableAdders(new ArrayList<Adder>());
								swap.setBaseCost(nontargetingSenseCost);
								swap
										.setAlias(ad.getSelectedOption()
												.getAlias());
								swap.setSelected(true);
								swap.setExclusive(true);
								swap.setIncludeInBase(true);
								adjusted.add(swap);
							}
							break SENSES;
						}
					}
				}
			} else {
				adjusted.add(ad);
			}
		}
		// now we need to go through and check if any of them need to be
		// removed, based on other purchases...
		ArrayList<SenseGroup> purchasedGroups = new ArrayList<SenseGroup>();
		if (selectedOption != null) {
			SenseGroup option = SenseGroup.getGroupByID(selectedOption
					.getXMLID());
			if (option != null) {
				purchasedGroups.add(option);
			}
		}
		for (int i = adjusted.size() - 1; i >= 0; i--) {
			Adder ad = adjusted.get(i);
			if (ad.getXMLID().endsWith("GROUP")) {
				SenseGroup group = SenseGroup.getGroupByID(ad.getXMLID());
				if (group != null) {
					if (purchasedGroups.contains(group)) {
						adjusted.remove(i);
					} else {
						purchasedGroups.add(group);
					}
				}
			}
		}
		// now check the senses
		for (int i = adjusted.size() - 1; i >= 0; i--) {
			Adder ad = adjusted.get(i);
			if (!ad.getXMLID().endsWith("GROUP")) {
				Sense sense = Sense.getSenseByID(ad.getXMLID());
				if (sense != null) {
					SenseGroup group = sense.getGroup();
					if (purchasedGroups.contains(group)) {
						adjusted.remove(i);
					}
				}
			}
		}
		// finally, adjust the half-die adder, if present
		if (GenericObject.findObjectByID(adjusted, "PLUSONEHALFDIE") != null) {
			GenericObject ad = GenericObject.findObjectByID(adjusted,
					"PLUSONEHALFDIE");
			if (selectedOption != null) {
				SenseGroup group = SenseGroup.getGroupByID(selectedOption
						.getXMLID());
				if ((group != null)
						&& group.getDefaultSenseAdders().contains(
								"TARGETINGSENSE")) {
					ad.setBaseCost(targetingHalfCost);
				} else {
					ad.setBaseCost(nontargetingHalfCost);
				}
			} else {
				ad.setBaseCost(nontargetingHalfCost);
			}
		}
		assignedAdders = adjusted;
		return adjusted;
	}

	public ArrayList<Adder> getAvailableAdders() {
		long current = System.currentTimeMillis();
		if ((availableAddersLastCall > 0)
				&& (availableAddersLastCall >= GenericObject.lastEdit)
				&& (availableAddersSaver != null)) {
			return availableAddersSaver;
		}
		availableAddersLastCall = current;
		ArrayList<Adder> ret = (ArrayList<Adder>) super.getAvailableAdders()
				.clone();
		if (oldMethod) {
			return ret;
		}
		ArrayList<String> added = new ArrayList<String>();
		for (Adder ad : ret) {
			added.add(ad.getXMLID());
		}
		ArrayList<Adder> assigned = getAssignedAdders();
		ArrayList<String> assignedGroups = new ArrayList<String>();
		for (Adder ad : assigned) {
			if (!added.contains(ad.getXMLID())) {
				ret.add(ad);
				added.add(ad.getXMLID());
				if (ad.getXMLID().endsWith("GROUP")) {
					assignedGroups.add(ad.getXMLID());
				}
			}
		}
		ArrayList<SenseGroup> allGroups = SenseGroup.getAllGroups();
		ArrayList<Sense> allSenses = Sense.getAllSenses();
		Adder selected = getSelectedOption();
		if (selected != null) {
			assignedGroups.add(selected.getXMLID());
		}
		if ((targetingGroupCost >= 0) || (nontargetingGroupCost >= 0)) {
			for (SenseGroup group : allGroups) {
				if (assignedGroups.contains(group.getXMLID())) {
					continue;
				} else if (group.getXMLID().equals("UNUSUALGROUP")
						|| group.getXMLID().equals("NOGROUP")) {
					continue;
				} else {
					if (group.getDefaultSenseAdders()
							.contains("TARGETINGSENSE")
							&& (targetingGroupCost >= 0)) {
						Adder swap = new Adder(group);
						swap.setAlias(group.getDisplay());
						swap.setOptions(new ArrayList<Adder>());
						swap.setSelectedOption(null);
						swap.setAvailableAdders(new ArrayList<Adder>());
						swap.setExclusive(true);
						swap.setLevelCost(-1);
						swap.setLevelValue(-1);
						swap.setBaseCost(targetingGroupCost);
						swap.setSelected(true);
						swap.setIncludeInBase(true);
						ret.add(swap);
						added.add(group.getXMLID());
					} else if (!group.getDefaultSenseAdders().contains(
							"TARGETINGSENSE")
							&& (nontargetingGroupCost >= 0)) {
						Adder swap = new Adder(group);
						swap.setAlias(group.getDisplay());
						swap.setOptions(new ArrayList<Adder>());
						swap.setSelectedOption(null);
						swap.setAvailableAdders(new ArrayList<Adder>());
						swap.setExclusive(true);
						swap.setLevelCost(-1);
						swap.setLevelValue(-1);
						swap.setBaseCost(nontargetingGroupCost);
						swap.setSelected(true);
						swap.setIncludeInBase(true);
						ret.add(swap);
						added.add(group.getXMLID());
					}
				}
			}
		}
		if ((targetingSenseCost >= 0) || (nontargetingSenseCost >= 0)) {
			for (Sense sense : allSenses) {
				if (added.contains(sense.getXMLID())
						|| assignedGroups.contains(sense.getGroup().getXMLID())) {
					continue;
				} else {
					if (sense.getBuiltInSenseAdders()
							.contains("TARGETINGSENSE")
							&& (targetingSenseCost >= 0)) {
						Adder swap = new Adder(sense);
						swap.setAlias(sense.getDisplay());
						swap.setOptions(new ArrayList<Adder>());
						swap.setSelectedOption(null);
						swap.setAvailableAdders(new ArrayList<Adder>());
						swap.setExclusive(true);
						swap.setLevelCost(-1);
						swap.setLevelValue(-1);
						swap.setBaseCost(targetingSenseCost);
						swap.setSelected(true);
						swap.setIncludeInBase(true);
						ret.add(swap);
						added.add(sense.getXMLID());
					} else if (!sense.getBuiltInSenseAdders().contains(
							"TARGETINGSENSE")
							&& (nontargetingSenseCost >= 0)) {
						Adder swap = new Adder(sense);
						swap.setAlias(sense.getDisplay());
						swap.setOptions(new ArrayList<Adder>());
						swap.setSelectedOption(null);
						swap.setAvailableAdders(new ArrayList<Adder>());
						swap.setExclusive(true);
						swap.setLevelCost(-1);
						swap.setLevelValue(-1);
						swap.setBaseCost(nontargetingSenseCost);
						swap.setSelected(true);
						swap.setIncludeInBase(true);
						ret.add(swap);
						added.add(sense.getXMLID());
					}
				}
			}
		}
		if ((selected != null)
				&& ((targetingHalfCost > 0) || (nontargetingHalfCost > 0))
				&& (GenericObject.findObjectByID(ret, "PLUSONEHALFDIE") == null)) {
			SenseGroup sel = SenseGroup.getGroupByID(selected.getXMLID());
			Adder ad = new Adder();
			ad.setDisplay("+1/2 d6");
			ad.setXMLID("PLUSONEHALFDIE");
			ad.setExclusive(true);
			ad.setFixedValue(true);
			ad.setSelectable(true);
			ad.setLevelCost(-1);
			ad.setLevelValue(-1);
			if ((sel != null)
					&& sel.getDefaultSenseAdders().contains("TARGETINGSENSE")) {
				ad.setBaseCost(targetingHalfCost);
			} else if (nontargetingHalfCost > 0) {
				ad.setBaseCost(nontargetingHalfCost);
			} else {
				ad.setBaseCost(targetingHalfCost);
			}
			ad.setIncludeInBase(true);
			ret.add(0, ad);
		}
		availableAddersSaver = ret;
		return ret;
	}

	public String getColumn2Output() {
		String ret = getAlias();
		ArrayList<String> groups = new ArrayList<String>();
		ArrayList<String> senses = new ArrayList<String>();
		ArrayList<Sense> allSenses = Sense.getAllSenses();
		String group = "[Unknown]";
		if (getSelectedOption() != null) {
			group = getSelectedOption().getAlias();
		}
		if (group.toUpperCase().indexOf("GROUP") > 0) {
			group = group.substring(0, group.toUpperCase().indexOf("GROUP"))
					.trim();
		}
		groups.add(group);
		for (Adder ad : getAssignedAdders()) {
			if (ad.getXMLID().equals("ADDITIONAL_GROUP")
					&& (ad.getSelectedOption() != null)) {
				ad.setDisplayInString(false);
				group = ad.getSelectedOption().getAlias();
				if (group.toUpperCase().indexOf("GROUP") > 0) {
					group = group.substring(0,
							group.toUpperCase().indexOf("GROUP")).trim();
				}
				groups.add(group);
			} else if (ad.getXMLID().endsWith("GROUP")) {
				ad.setDisplayInString(false);
				group = ad.getAlias();
				if (group.toUpperCase().indexOf("GROUP") > 0) {
					group = group.substring(0,
							group.toUpperCase().indexOf("GROUP")).trim();
				}
				groups.add(group);
			} else if (ad.getXMLID().equals("ADDITIONAL_SENSE")
					&& (ad.getSelectedOption() != null)) {
				ad.setDisplayInString(false);
				String sense = ad.getSelectedOption().getAlias();
				senses.add(sense);
			} else if (GenericObject.findObjectByID(allSenses, ad.getXMLID()) != null) {
				ad.setDisplayInString(false);
				String sense = ad.getAlias();
				senses.add(sense);
			}
		}
		if (getName().trim().length() > 0) {
			ret = "<i>" + getName() + ":</i>  " + ret;
		}
		ret += " to ";
		for (int i = 0; i < groups.size(); i++) {
			if ((i > 0) && (i < groups.size() - 1)) {
				ret += ", ";
			} else if ((i == groups.size() - 1) && (i > 0)) {
				ret += " and ";
			}
			ret += groups.get(i);
		}
		if (groups.size() > 1) {
			ret += " Groups";
		} else {
			ret += " Group";
		}
		for (int i = 0; i < senses.size(); i++) {
			if (i < senses.size() - 1) {
				ret += ", ";
			} else {
				ret += " and ";
			}
			ret += senses.get(i);
		}
		ret += " " + getDamageDisplay();
		if ((getInput() != null) && (getInput().trim().length() > 0)) {
			ret += ":  " + getInput();
		}
		String adderString = getAdderString();
		if (adderString.trim().length() > 0) {
			ret += ", " + adderString;
		}
		ret += getModifierString();
		if ((getEndUsage() > 0)
				&& (GenericObject.findObjectByID(HeroDesigner.getActiveHero()
						.getPowers(), "ENDURANCERESERVE") != null)
				&& (GenericObject.findObjectByID(getAllAssignedModifiers(),
						"ENDRESERVEOREND") == null)
				&& !HeroDesigner.getInstance().getPrefs().useWG()) {
			if (getUseENDReserve()) {
				ret += " (uses END Reserve)";
			} else {
				ret += " (uses Personal END)";
			}
		}
		return ret;
	}

	public ArrayList<Adder> getOptions() {
		if ((optionsLastCall > 0)
				&& (optionsLastCall >= GenericObject.lastEdit)
				&& (optionsSaver != null) && optionsSaver.size()>0) {
			return optionsSaver;
		}
		optionsLastCall = System.currentTimeMillis();
		ArrayList<Adder> ret = new ArrayList<Adder>();
		if ((targetingCost >= 0) || (nontargetingCost >= 0)) {
			ArrayList<SenseGroup> allGroups = SenseGroup.getAllGroups();
			for (SenseGroup group : allGroups) {
				if (group.getXMLID().equals("UNUSUALGROUP")) {
					continue;
				}
				if (group.getXMLID().equals("NOGROUP")) {
					continue;
				}
				if (group.getDefaultSenseAdders().contains("TARGETINGSENSE")
						&& (targetingCost >= 0)) {
					Adder ad = new Adder(group);
					if (levelValue <= 0) {
						ad.setBaseCost(targetingCost);
						ad.setLevelCost(-1);
						ad.setLevelValue(-1);
					} else {
						ad.setBaseCost(0);
						ad.setLevelCost(targetingCost);
						ad.setLevelValue(levelValue);
					}
					ret.add(ad);
				} else if (!group.getDefaultSenseAdders().contains(
						"TARGETINGSENSE")
						&& (nontargetingCost >= 0)) {
					Adder ad = new Adder(group);
					if (levelValue <= 0) {
						ad.setBaseCost(nontargetingCost);
						ad.setLevelCost(-1);
						ad.setLevelValue(-1);
					} else {
						ad.setBaseCost(0);
						ad.setLevelCost(nontargetingCost);
						ad.setLevelValue(levelValue);
					}
					ret.add(ad);
				}
			}
		}
		if (ret.size() == 0) {
			oldMethod = true;
			optionsSaver = super.getOptions();
			return super.getOptions();
		}
		Collections.sort(ret);
		optionsSaver = ret;
		return ret;
	}

	public Adder getSelectedOption() {
		if (!restoring && (selectedOptionLastCall > 0)
				&& (selectedOptionLastCall >= GenericObject.lastEdit)
				&& (selectedOptionSaver != null)) {
			return selectedOptionSaver;
		}
		selectedOptionLastCall = System.currentTimeMillis();
		Adder ret = super.getSelectedOption();
		if ((ret == null) && !restoring) {
			ArrayList<Adder> avail = getOptions();
			if (avail.size() > 0) {
				ret = avail.get(0);
			}
		}
		if (oldMethod || (ret == null)) {
			return ret;
		}

		SenseGroup opGroup = SenseGroup.getGroupByID(ret.getXMLID());
		if (opGroup == null) {
			return ret;
		}
		for (int i = assignedAdders.size() - 1; i >= 0; i--) {
			Adder ad = assignedAdders.get(i);
			if (!ad.getXMLID().endsWith("GROUP")) {
				continue;
			}
			SenseGroup adGroup = SenseGroup.getGroupByID(ad.getXMLID());
			if (adGroup == null) {
				continue;
			}
			if (ad.getXMLID().equals(opGroup.getXMLID())) {
				assignedAdders.remove(i);
			} else if (!opGroup.getDefaultSenseAdders().contains(
					"TARGETINGSENSE")
					&& adGroup.getDefaultSenseAdders().contains(
							"TARGETINGSENSE")) {
				assignedAdders.remove(i);
				ArrayList<Adder> allOps = getOptions();
				for (Adder op : allOps) {
					if (op.getXMLID().equals(ad.getXMLID())) {
						String oldID = selectedOption.getXMLID();
						super.setSelectedOption(op);
						GenericObject.lastEdit = System.currentTimeMillis();
						availableAddersSaver = null;
						ArrayList<Adder> avail = getAvailableAdders();
						for (Adder check : avail) {
							if (check.getXMLID().equals(oldID)) {
								check.setSelected(true);
								assignedAdders.add(check);
								selectedOptionSaver = selectedOption;
								return selectedOption;
							}
						}
						selectedOptionSaver = selectedOption;
						return selectedOption;
					}
				}
				selectedOption.setBaseCost(ad.getBaseCost());
				assignedAdders.set(i, selectedOption);
				ad.setBaseCost(targetingCost);
				selectedOption = ad;
				selectedOptionSaver = selectedOption;
				return selectedOption;
			}
		}
		selectedOptionSaver = ret;
		return ret;
	}

	protected void init(Element element) {
		super.init(element);
		targetingCost = -1;
		nontargetingCost = -1;
		targetingGroupCost = -1;
		targetingSenseCost = -1;
		nontargetingGroupCost = -1;
		nontargetingSenseCost = -1;
		targetingHalfCost = -1;
		nontargetingHalfCost = -1;
		oldMethod = false;
		restoring = false;
		String check = XMLUtility.getValue(element, "TARGETINGGROUPCOST");
		if ((check != null) && (check.trim().length() > 0)) {
			try {
				targetingGroupCost = Double.parseDouble(check);
			} catch (Exception exp) {

			}
		}
		check = XMLUtility.getValue(element, "NONTARGETINGGROUPCOST");
		if ((check != null) && (check.trim().length() > 0)) {
			try {
				nontargetingGroupCost = Double.parseDouble(check);
			} catch (Exception exp) {

			}
		}
		check = XMLUtility.getValue(element, "TARGETINGSENSECOST");
		if ((check != null) && (check.trim().length() > 0)) {
			try {
				targetingSenseCost = Double.parseDouble(check);
			} catch (Exception exp) {

			}
		}
		check = XMLUtility.getValue(element, "NONTARGETINGSENSECOST");
		if ((check != null) && (check.trim().length() > 0)) {
			try {
				nontargetingSenseCost = Double.parseDouble(check);
			} catch (Exception exp) {

			}
		}
		check = XMLUtility.getValue(element, "TARGETINGCOST");
		if ((check != null) && (check.trim().length() > 0)) {
			try {
				targetingCost = Double.parseDouble(check);
			} catch (Exception exp) {

			}
		}
		check = XMLUtility.getValue(element, "NONTARGETINGCOST");
		if ((check != null) && (check.trim().length() > 0)) {
			try {
				nontargetingCost = Double.parseDouble(check);
			} catch (Exception exp) {

			}
		}
		check = XMLUtility.getValue(element, "NONTARGETINGHALFCOST");
		if ((check != null) && (check.trim().length() > 0)) {
			try {
				nontargetingHalfCost = Double.parseDouble(check);
			} catch (Exception exp) {

			}
		}
		check = XMLUtility.getValue(element, "TARGETINGHALFCOST");
		if ((check != null) && (check.trim().length() > 0)) {
			try {
				targetingHalfCost = Double.parseDouble(check);
			} catch (Exception exp) {

			}
		}
	}

	public void restoreFromSave(Element root) {

		super.restoreFromSave(root);
		getOptions(); // sets whether to use the old methods or the new
		if (oldMethod) {
			return;
		}

		restoring = true;
		ArrayList<Adder> holder = availableAdders;
		assignedAdders = new ArrayList<Adder>();
		availableAdders = new ArrayList<Adder>();
		ArrayList<Adder> avail = getAvailableAdders();
		availableAdders = holder;
		ArrayList<Adder> assigned = new ArrayList<Adder>();
		Iterator iter = root.getChildren("ADDER").iterator();
		OUTER: while (iter.hasNext()) {
			Element element = (Element) iter.next();
			String id = XMLUtility.getValue(element, "XMLID");
			if ((id != null)
					&& (id.trim().equals("SINGLE") || id.trim().equals("GROUP")
							|| id.trim().equals("ADDITIONAL_GROUP")
							|| id.trim().equals("ADDITIONAL_SENSE")
							|| id.trim().equals("SENSEGROUP") || id.trim()
							.equals("SENSE"))) {
				String adop = null;
				String val = XMLUtility.getValue(element, "OPTIONID");
				if ((val != null) && (val.trim().length() > 0)) {
					adop = val;
				} else {
					val = XMLUtility.getValue(element, "OPTION");
					if ((val != null) && (val.trim().length() > 0)) {
						adop = val;
					}
				}
				if ((adop != null) && (adop.trim().length() > 0)) {
					if (id.indexOf("GROUP") >= 0) {
						if (adop.toUpperCase().indexOf("GROUP") < 0) {
							if (adop.equals(adop.toUpperCase())) {
								adop = adop + "GROUP";
							} else {
								adop = adop.trim() + " Group";
							}
						}
					}
					if (adop.equals(adop.toUpperCase())) {
						adop = translateSense(adop);
					}
					for (Adder ad : avail) {
						if (ad.getXMLID().startsWith(adop.trim().toUpperCase())
								|| adop.trim().toUpperCase().startsWith(
										ad.getXMLID())
								|| ad.getDisplay().equals(adop)) {
							try {
								ad = (Adder) ad.clone();
								ad.setSelected(true);
								ad.setAlias(XMLUtility.getValue(element,
										"OPTION_ALIAS"));
								assigned.add(ad);
								continue OUTER;
							} catch (Exception exp) {

							}
						}
					}
					Adder ad = getSelectedOption();
					if (ad != null) {
						if (ad.getXMLID().startsWith(adop.trim().toUpperCase())
								|| adop.trim().toUpperCase().startsWith(
										ad.getXMLID())
								|| ad.getDisplay().equals(adop)) {
							try {
								ad = (Adder) ad.clone();
								ad.setSelected(true);
								assigned.add(ad);
								continue OUTER;
							} catch (Exception exp) {

							}
						}
					}

				}
			}

			Adder ad = new Adder(element);
			ad.setExclusive(false);
			ad.setAvailableCheck(true);
			if (GenericObject.findObjectByID(availableAdders, ad.getXMLID()) != null) {
				ad = (Adder) GenericObject.findObjectByID(availableAdders, ad
						.getXMLID());
			}
			ad.setAvailableCheck(false);
			ad.restoreFromSave(element);
			assigned.add(ad);
		}
		assignedAdders = assigned;

		String option = null;
		String check = XMLUtility.getValue(root, "OPTIONID");
		if ((check != null) && (check.trim().length() > 0)) {
			option = check;
		} else {
			check = XMLUtility.getValue(root, "OPTION");
			if ((check != null) && (check.trim().length() > 0)) {
				option = check;
			}
		}
		if (option != null) {
			option = translateSense(option);
			avail = getOptions();
			boolean matched = false;
			LOOP: for (Adder ad : avail) {
				if (ad.getXMLID().startsWith(option)
						|| option.startsWith(ad.getXMLID())
						|| ad.getDisplay().toUpperCase().startsWith(
								option.toUpperCase())
						|| option.toUpperCase().startsWith(
								ad.getDisplay().toUpperCase())) {
					try {
						ad = (Adder) ad.clone();
						ad.setAlias(XMLUtility.getValue(root, "OPTION_ALIAS"));
					} catch (Exception e) {

					}
					setSelectedOption(ad);
					matched = true;
					break LOOP;
				}
			}
			if (!matched) {
				String opalias = XMLUtility.getValue(root, "OPTION_ALIAS");
				if ((opalias != null) && (opalias.trim().length() > 0)) {
					LOOP: for (Adder ad : avail) {
						if (ad.getDisplay().toUpperCase().startsWith(
								opalias.trim().toUpperCase())
								|| opalias.trim().toUpperCase().startsWith(
										ad.getDisplay())) {
							setSelectedOption(ad);
							matched = true;
							break LOOP;
						}
					}
				}
				if (!matched) {
					LOOP: for (Adder ad : avail) {
						if (ad.getBaseCost() == baseCost) {
							setSelectedOption(ad);
							matched = true;
							break LOOP;
						}
					}
				}
			}
		}

		getAssignedAdders(); // takes care of the assigned adders...
		setSelectedOption(getSelectedOption());
		// adjusts the selected option and related costs (if necessary);
		restoring = false;
	}

	public void setSelectedOption(Adder option) {
		GenericObject.lastEdit = System.currentTimeMillis();
		if (oldMethod || (option == null)) {
			super.setSelectedOption(option);
			return;
		}
		if (option.getXMLID().equals("ADDITIONAL_GROUP")
				|| option.getXMLID().equals("GROUP")
				|| option.getXMLID().equals("SENSEGROUP")) {
			ArrayList<Adder> allOps = getOptions();
			if (option.getSelectedOption() == null) {
				super.setSelectedOption(allOps.get(0));
				getSelectedOption();
				return;
			}
			for (Adder ad : allOps) {
				if (ad.getXMLID().equals(option.getSelectedOption().getXMLID())) {
					super.setSelectedOption(ad);
					getSelectedOption();
					return;
				}
			}
			// if we get here, then just use the first option...
			super.setSelectedOption(allOps.get(0));
			getSelectedOption();
			return;
		}
		super.setSelectedOption(option);
		return;
	}

	private String translateSense(String val) {
		String ret = val;
		if (oldMethod) {
			return ret;
		}
		if (val.equals("HEARING")) {
			ret = "NORMALHEARING";
		}
		if (val.equals("SONAR")) {
			ret = "ACTIVESONAR";
		}
		if (val.equals("MA")) {
			ret = "MENTALAWARENESS";
		}
		if (val.equals("MS")) {
			ret = "MINDSCAN";
		}
		if (val.equals("RP")) {
			ret = "RADIOPERCEPTION";
		}
		if (val.equals("RT")) {
			ret = "RADIOPERCEIVETRANSMIT";
		}
		if (val.equals("RADIOTRANSMISSION")) {
			ret = "RADIOPERCEIVETRANSMIT";
		}
		if (val.equals("SIGHT")) {
			ret = "NORMALSIGHT";
		}
		if (val.equals("SMELL")) {
			ret = "NORMALSMELL";
		}
		if (val.equals("TASTE")) {
			ret = "NORMALTASTE";
		}
		if (val.equals("TOUCH")) {
			ret = "NORMALTOUCH";
		}
		if (val.equals("IR")) {
			ret = "INFRAREDPERCEPTION";
		}
		if (val.equals("IRPERCEPTION")) {
			ret = "INFRAREDPERCEPTION";
		}
		if (val.equals("NRAY")) {
			ret = "NRAYPERCEPTION";
		}
		if (val.equals("SENSORYTALENTS")) {
			ret = "DANGER_SENSE";
		}
		if (val.equals("UV")) {
			ret = "ULTRAVIOLETPERCEPTION";
		}
		if (val.equals("UVPERCEPTION")) {
			ret = "ULTRAVIOLETPERCEPTION";
		}
		if (val.equals("ULTRASONIC")) {
			ret = "ULTRASONICPERCEPTION";
		}
		return ret;
	}

}