package com.hero.ui.dialog;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import org.jdom.Element;

import com.hero.HeroDesigner;
import com.hero.model.DoubleDocument;
import com.hero.objects.Adder;
import com.hero.objects.ElementalControl;
import com.hero.objects.GenericObject;
import com.hero.objects.List;
import com.hero.objects.Multipower;
import com.hero.objects.disads.Disadvantage;
import com.hero.objects.modifiers.Modifier;
import com.hero.objects.modifiers.PartialCoverage;
import com.hero.objects.powers.Entangle;
import com.hero.objects.powers.ExtraLimbs;
import com.hero.objects.powers.Power;
import com.hero.objects.powers.Sense;
import com.hero.objects.powers.SenseAdder;
import com.hero.ui.widgets.AdderPanel;
import com.hero.ui.widgets.CustomAdderPanel;
import com.hero.ui.widgets.LevelTF;
import com.hero.ui.widgets.ModifierPanel;
import com.hero.ui.widgets.PopupMessage;
import com.hero.util.Rounder;

/**
 * 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 GenericDialog extends JDialog {
	protected class MultiplierDialog extends JDialog {
		private static final long serialVersionUID = 3337484664394711637L;

		private JLabel multiplierLbl;

		private JTextField multiplierTF;

		private JLabel notesLbl;

		private JButton closeBtn;

		private MultiplierDialog dialogRef;

		public MultiplierDialog() {
			super(GenericDialog.this, "Cost Multiplier", true);
			dialogRef = this;
			initWidgets();
			initListeners();
			layoutComponent();
			pack();
		}

		private void initListeners() {
			multiplierTF.getDocument().addDocumentListener(
					new DocumentListener() {
						public void changedUpdate(DocumentEvent e) {
							if (multiplierTF.getText().trim().length() > 0) {
								object.setMultiplier(Double
										.parseDouble(multiplierTF.getText()));
							} else {
								object.setMultiplier(1);
							}
							updateValues();
						}

						public void insertUpdate(DocumentEvent e) {
							if (multiplierTF.getText().trim().length() > 0) {
								object.setMultiplier(Double
										.parseDouble(multiplierTF.getText()));
							} else {
								object.setMultiplier(1);
							}
							updateValues();
						}

						public void removeUpdate(DocumentEvent e) {
							if (multiplierTF.getText().trim().length() > 0) {
								object.setMultiplier(Double
										.parseDouble(multiplierTF.getText()));
							} else {
								object.setMultiplier(1);
							}
							updateValues();
						}
					});
			closeBtn.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					dialogRef.setVisible(false);
				}
			});
		}

		private void initWidgets() {
			multiplierLbl = new JLabel("Cost Multiplier: ");
			multiplierTF = new JTextField(new DoubleDocument(500, 0), ""
					+ object.getMultiplier(), 6);
			notesLbl = new JLabel(
					"<html><b>Note:</b>  A cost multiplier is intended as a convenience item only.<br>It is not an official part of the Hero System 5th Edition or the Hero System 6th Edition rules<br>and should only be used with GM permission.");
			closeBtn = new JButton("Close");
		}

		private void layoutComponent() {
			JPanel panel = new JPanel(new GridBagLayout());
			GridBagConstraints gbc = new GridBagConstraints();
			gbc.gridx = 0;
			gbc.gridy = 0;
			gbc.gridwidth = 1;
			gbc.weightx = 0;
			gbc.weighty = 0;
			gbc.anchor = GridBagConstraints.EAST;
			gbc.insets = new Insets(5, 5, 5, 5);
			panel.add(multiplierLbl, gbc);
			gbc.gridx = 1;
			gbc.weightx = 1;
			gbc.fill = GridBagConstraints.HORIZONTAL;
			panel.add(multiplierTF, gbc);
			gbc.gridx = 0;
			gbc.gridy = 1;
			gbc.gridwidth = 2;
			gbc.weightx = 0;
			gbc.insets = new Insets(5, 15, 5, 15);
			gbc.weighty = 1;
			panel.add(notesLbl, gbc);
			gbc.gridy = 2;
			gbc.insets = new Insets(5, 5, 5, 5);
			gbc.anchor = GridBagConstraints.SOUTH;
			gbc.fill = GridBagConstraints.NONE;
			panel.add(closeBtn, gbc);
			setContentPane(panel);
		}
	}

	private class RequiredAdderLevelListener implements PropertyChangeListener {
		Adder adder;

		LevelTF tf;

		public RequiredAdderLevelListener(Adder ad, LevelTF level) {
			adder = ad;
			tf = level;
		}

		public void propertyChange(PropertyChangeEvent e) {
			adder.setLevels(tf.getCurrent());
			updateValues();
		}
	}

	private class RequiredAdderComboListener implements ItemListener {
		Adder adder;

		JComboBox combo;

		DocumentListener listener;

		public RequiredAdderComboListener(Adder ad, JComboBox com) {
			adder = ad;
			combo = com;
			listener = new DocumentListener() {
				public void changedUpdate(DocumentEvent e) {
					if (adder.getSelectedOption() != null) {
						adder.getSelectedOption().setAlias(
								((JTextField) combo.getEditor()
										.getEditorComponent()).getText());
					}
				}

				public void insertUpdate(DocumentEvent e) {
					if (adder.getSelectedOption() != null) {
						adder.getSelectedOption().setAlias(
								((JTextField) combo.getEditor()
										.getEditorComponent()).getText());
					}
				}

				public void removeUpdate(DocumentEvent e) {
					if (adder.getSelectedOption() != null) {
						adder.getSelectedOption().setAlias(
								((JTextField) combo.getEditor()
										.getEditorComponent()).getText());
					}
				}
			};
			((JTextField) combo.getEditor().getEditorComponent()).getDocument()
					.addDocumentListener(listener);
		}

		public void itemStateChanged(ItemEvent e) {
			if (combo.getSelectedItem() == null) {
				return;
			}
			if (combo.getSelectedItem() instanceof Adder) {
				((JTextField) combo.getEditor().getEditorComponent())
						.getDocument().removeDocumentListener(listener);
				Adder sel = (Adder) combo.getSelectedItem();
				String alias = sel.getAlias();
				adder.setSelectedOption(sel);
				updateValues();
				layoutExtrasPanel();
				((JTextField) combo.getEditor().getEditorComponent())
						.setText(alias);
				adder.getSelectedOption().setAlias(alias);
				((JTextField) combo.getEditor().getEditorComponent())
						.getDocument().addDocumentListener(listener);
			}
		}
	}

	private static final long serialVersionUID = -6523068785490967657L;

	protected JLabel activePointsLbl;

	protected JPanel adderPanel;

	protected ArrayList<AdderPanel> adderPanels;

	protected JButton addModifierBtn;

	protected JButton cancelBtn;

	/**
	 * Whether the user clicked the ok button to exit the dialog or used the
	 * cancel button/"X"
	 */
	public boolean cancelButtonClicked;

	protected JButton customAdderBtn;

	protected JButton privateAdderBtn;

	protected JPanel customAdderPanel;

	protected JButton defineBtn;

	protected JButton deleteBtn;

	protected JButton multiplierBtn;

	/**
	 * Whether the user clicked the delete button.
	 */
	public boolean deleteButtonClicked;

	protected JCheckBox displayActivePointsCB;

	protected JLabel displayLbl;

	protected DocumentListener displayListener;

	protected JTextField displayTF;

	protected JComboBox exampleCombo;

	protected ItemListener exampleItemListener;

	protected DocumentListener exampleDocumentListener;

	protected JPanel extrasPanel;

	protected JScrollPane extrasScroll;

	protected JLabel inputLbl;

	protected boolean isNew;

	protected boolean isPower;

	public JLabel levelsLbl;

	protected LevelTF levelTF;

	protected JPanel modifierPanel;

	protected JPanel privateModifierPanel;

	protected JPanel privateAdderPanel;

	protected JPanel multiplierPanel;

	protected ArrayList<ModifierPanel> modifierPanels;

	public JLabel nameLbl;

	public JTextField nameTF;

	protected JButton notesBtn;

	protected GenericObject object;

	protected JButton okBtn;

	/**
	 * Whether the user clicked the ok button to exit the dialog.
	 */
	public boolean okButtonClicked;

	public JComboBox optionsCB;

	public ItemListener optionListener;

	public DocumentListener optionAliasListener;

	public JLabel optionsLbl;

	public JLabel pointsLbl;

	protected LevelTF pointsTF;

	public JLabel realCostLbl;

	protected ArrayList<Adder> requiredAdderAdders;
	protected ArrayList<Adder> requiredLevelAdderAdders;

	protected ArrayList<JComboBox> requiredAdderCombos;
	protected ArrayList<LevelTF> requiredLevelAdderTFs;
	protected Hashtable<JLabel, Adder> requiredLevelAdderLabels = new Hashtable<JLabel, Adder>();

	protected JCheckBox ultraCB;

	protected JPanel equipmentPanel;

	protected JCheckBox carriedCB;

	protected JTextField priceTF;

	protected JLabel priceLbl;

	protected JTextField weightTF;

	protected JLabel weightLbl;

	protected JLabel endSourceLbl;

	protected JComboBox endSourceCombo;

	protected JLabel sfxLbl;

	protected JComboBox sfxCombo;

	public GenericDialog(GenericObject object, boolean isNew, boolean isPower) {
		super(HeroDesigner.getAppFrame(), object.getDisplay(), true);
		this.object = object;
		object.setPower(isPower);
		this.isNew = isNew;
		this.isPower = isPower;
		if (object.isPower()) {
			this.isPower = true;
		}
		setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
	}

	/**
	 * Call this method to display the dialog.
	 * 
	 * @return
	 */
	public int display() {
		setVisible(true);
		if (cancelButtonClicked) {
			return 0;
		} else if (deleteButtonClicked) {
			return -1;
		} else if (okButtonClicked) {
			return 1;
		} else {
			return 0;
		}
	}

	/**
	 * Retrieves an adder by XMLID from the supplied Vector of Adders. Will also
	 * look through the Assigned Adders for any Adder that it finds.
	 * 
	 * @param id
	 * @param list
	 * @return
	 */
	public Adder getAdderFromList(String id, ArrayList<Adder> list) {
		for (Adder comp : list) {
			if ((comp.getXMLID() != null)
					&& comp.getXMLID().trim().toUpperCase().equals(
							id.trim().toUpperCase())) {
				return comp;
			} else if (comp.getAssignedAdders().size() > 0) {
				Adder ret = getAdderFromList(id, comp.getAssignedAdders());
				if (ret != null) {
					return ret;
				}
			}
		}
		return null;
	}

	/**
	 * Returns a Vector of AdderPanel objects that are currently being displayed
	 * in this dialog.
	 * 
	 * @return
	 */
	public ArrayList<AdderPanel> getAdderPanels() {
		return adderPanels;
	}

	/**
	 * Retrieves a Modifier by XMLID from the supplied Vector of Modifiers. Will
	 * also look through the Assigned Modifiers for any Modifier that it finds.
	 * 
	 * @param id
	 * @param list
	 * @return
	 */
	public Modifier getModifierFromList(String id, ArrayList<Modifier> list) {
		for (Modifier comp : list) {
			if ((comp.getXMLID() != null)
					&& comp.getXMLID().trim().toUpperCase().equals(
							id.trim().toUpperCase())) {
				return comp;
			} else if (comp.getAssignedModifiers().size() > 0) {
				Modifier ret = getModifierFromList(id, comp
						.getAssignedModifiers());
				if (ret != null) {
					return ret;
				}
			}
		}
		return null;
	}

	/**
	 * Returns a Vector of ModifierPanel objects that are currently being
	 * displayed in this dialog.
	 * 
	 * @return
	 */
	public ArrayList<ModifierPanel> getModifierPanels() {
		return modifierPanels;
	}

	protected JPanel getModsPanel() {
		JPanel mods = new JPanel(new FlowLayout(FlowLayout.CENTER));
		mods.add(customAdderBtn);
		mods.add(privateAdderBtn);
		mods.add(addModifierBtn);
		return mods;
	}

	/**
	 * Returns the object that this dialog is editing.
	 * 
	 * @return
	 */
	public GenericObject getObject() {
		return object;
	}

	protected JPanel getTopPanel() {
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.gridx = 0;
		gbc.gridy = 10;
		gbc.weightx = 0;
		gbc.weighty = 0;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		gbc.fill = GridBagConstraints.NONE;
		gbc.anchor = GridBagConstraints.EAST;
		gbc.insets = new Insets(2, 5, 5, 2);
		gbc.anchor = GridBagConstraints.WEST;
		gbc.gridwidth = 3;
		gbc.weightx = 0;
		equipmentPanel.add(carriedCB, gbc);
		gbc.gridy++;
		gbc.anchor = GridBagConstraints.EAST;
		gbc.gridwidth = 1;
		equipmentPanel.add(weightLbl, gbc);
		gbc.gridx = 1;
		gbc.anchor = GridBagConstraints.WEST;
		gbc.weightx = 0;
		equipmentPanel.add(weightTF, gbc);
		gbc.gridx = 2;
		gbc.weightx = 1;
		equipmentPanel.add(new JLabel(HeroDesigner.getInstance().getPrefs()
				.isMetric() ? "kg" : "lbs"), gbc);
		gbc.gridy++;
		gbc.gridx = 0;
		gbc.anchor = GridBagConstraints.EAST;
		gbc.weightx = 0;
		equipmentPanel.add(priceLbl, gbc);
		gbc.gridx = 1;
		gbc.anchor = GridBagConstraints.WEST;
		if (HeroDesigner.getActiveHero().getRules().isEquipmentUnitsPrefix()) {
			gbc.gridwidth = 2;
			gbc.weightx = 1;
			gbc.anchor = GridBagConstraints.WEST;
			gbc.insets = new Insets(2, 0, 5, 2);
			JPanel pricePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
			pricePanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
			pricePanel.add(new JLabel(HeroDesigner.getActiveHero().getRules()
					.getEquipmentCostUnits()));
			pricePanel.add(priceTF);
			equipmentPanel.add(pricePanel, gbc);
			gbc.insets = new Insets(2, 5, 5, 2);
		} else {
			gbc.gridwidth = 2;
			gbc.weightx = 1;
			gbc.anchor = GridBagConstraints.WEST;
			gbc.insets = new Insets(2, 0, 5, 2);
			JPanel pricePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
			pricePanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
			pricePanel.add(priceTF);
			pricePanel.add(new JLabel(HeroDesigner.getActiveHero().getRules()
					.getEquipmentCostUnits()));
			equipmentPanel.add(pricePanel, gbc);
			gbc.insets = new Insets(2, 5, 5, 2);
		}
		equipmentPanel.setBorder(BorderFactory
				.createTitledBorder("Equipment Info."));
		gbc.gridx = 0;
		gbc.gridy = 10;
		gbc.weightx = 0;
		gbc.weighty = 0;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		gbc.fill = GridBagConstraints.NONE;
		gbc.anchor = GridBagConstraints.EAST;
		gbc.insets = new Insets(2, 5, 5, 2);
		JPanel panel = new JPanel(new GridBagLayout());
		panel.add(nameLbl, gbc);
		gbc.gridx = 1;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.gridwidth = 2;
		panel.add(nameTF, gbc);
		gbc.gridy = 20;
		gbc.gridwidth = 1;
		gbc.fill = GridBagConstraints.NONE;
		gbc.gridx = 0;
		panel.add(displayLbl, gbc);
		gbc.gridy = 24;
		if (object.getOptions().size() > 0) {
			panel.add(optionsLbl, gbc);
		}
		for (int i = 0; (i < requiredAdderAdders.size()) && (i < 5); i++) {
			gbc.gridy++;
			Adder ad = requiredAdderAdders.get(i);
			panel.add(new JLabel(ad.getAlias()), gbc);
		}
		for (int i = 0; (i < requiredLevelAdderAdders.size()) && (i < 5); i++) {
			gbc.gridy++;
			Adder ad = requiredLevelAdderAdders.get(i);
			JLabel l = new JLabel(ad.getAlias());
			requiredLevelAdderLabels.put(l, ad);
			panel.add(l, gbc);
		}
		gbc.gridy = 30;
		if ((object.getMinimumCost() != object.getMaxCost())
				&& object.isMinSet() && object.isMaxSet()) {
			panel.add(pointsLbl, gbc);
		}
		if ((object instanceof ExtraLimbs)
				|| (object instanceof PartialCoverage)
				|| ((object.getLevelCost() > 0) && (object.getLevelValue() > 0))) {
			gbc.gridy = 70;
			panel.add(levelsLbl, gbc);
		}
		gbc.gridy = 20;
		gbc.gridx = 1;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.anchor = GridBagConstraints.WEST;
		gbc.weightx = 1;
		gbc.gridwidth = 2;
		panel.add(displayTF, gbc);
		gbc.gridy = 24;
		if (object.getOptions().size() > 0) {
			panel.add(optionsCB, gbc);
		}
		for (int i = 0; (i < requiredAdderCombos.size()) && (i < 5); i++) {
			gbc.gridy++;
			JComboBox combo = requiredAdderCombos.get(i);
			panel.add(combo, gbc);
		}
		for (int i = 0; (i < requiredLevelAdderTFs.size()) && (i < 5); i++) {
			gbc.gridy++;
			gbc.fill = gbc.NONE;
			LevelTF tf = requiredLevelAdderTFs.get(i);
			panel.add(tf, gbc);
		}
		gbc.gridy = 30;
		if ((object.getMinimumCost() != object.getMaxCost())
				&& object.isMinSet() && object.isMaxSet()) {
			gbc.fill = GridBagConstraints.NONE;
			panel.add(pointsTF, gbc);
			gbc.fill = GridBagConstraints.HORIZONTAL;
		}
		if (object.isUserInput()) {
			gbc.gridy = 40;
			gbc.gridx = 0;
			gbc.gridwidth = 1;
			gbc.anchor = GridBagConstraints.EAST;
			gbc.fill = GridBagConstraints.NONE;
			gbc.weightx = 0;
			panel.add(inputLbl, gbc);
			gbc.gridx = 1;
			gbc.fill = GridBagConstraints.HORIZONTAL;
			gbc.weightx = 1;
			gbc.gridwidth = 2;
			panel.add(exampleCombo, gbc);
		}
		gbc.fill = GridBagConstraints.NONE;
		gbc.anchor = GridBagConstraints.WEST;
		gbc.gridwidth = 1;
		gbc.weightx = 0;
		if ((object instanceof ExtraLimbs)
				|| ((object.getLevelCost() != 0) && (object.getLevelValue() != 0))) {
			gbc.gridy = 70;
			panel.add(levelTF, gbc);
		}
		if (!(object instanceof Disadvantage)
				&& !(object instanceof Modifier)
				&& ((object.getParentList() == null) || !(object
						.getParentList() instanceof ElementalControl))) {
			gbc.gridx = 0;
			gbc.gridwidth = 1;
			gbc.gridheight = 1;
			gbc.fill = GridBagConstraints.NONE;
			gbc.anchor = GridBagConstraints.EAST;
			gbc.gridy = 69;
			gbc.gridx = 0;
			gbc.anchor = GridBagConstraints.EAST;
			gbc.fill = GridBagConstraints.NONE;
			panel.add(sfxLbl, gbc);
			gbc.gridx = 1;
			gbc.gridwidth = GridBagConstraints.REMAINDER;
			gbc.anchor = GridBagConstraints.WEST;
			panel.add(sfxCombo, gbc);
		}
		gbc.gridx = 0;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		gbc.fill = GridBagConstraints.NONE;
		gbc.anchor = GridBagConstraints.EAST;
		gbc.gridy = 91;
		gbc.gridx = 0;
		gbc.anchor = GridBagConstraints.EAST;
		gbc.fill = GridBagConstraints.NONE;
		panel.add(endSourceLbl, gbc);
		gbc.gridx = 1;
		gbc.gridwidth = GridBagConstraints.REMAINDER;
		gbc.anchor = GridBagConstraints.WEST;
		panel.add(endSourceCombo, gbc);
		gbc.gridy = 99;
		gbc.gridx = 0;
		gbc.gridwidth = GridBagConstraints.REMAINDER;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		if (object.isEquipment()) {
			panel.add(equipmentPanel, gbc);
		}
		return panel;
	}

	/**
	 * Returns true if any Adders with the specified XMLID are currently in a
	 * de-selected state.
	 * 
	 * @param xmlid
	 * @return
	 */
	public boolean hasAvailable(String xmlid) {
		for (int i = 0; i < adderPanels.size(); i++) {
			AdderPanel p = adderPanels.get(i);
			if (p.getAdder().getXMLID().equals(xmlid)) {
				if (p.hasAvailable()) {
					return true;
				}
			}
		}
		return false;
	}

	protected void init() {
		if (isNew) {
			// initialize the required adders...
			ArrayList<Adder> assigned = object.getAssignedAdders();
			ArrayList<Adder> available = object.getAvailableAdders();
			for (int i = 0; i < available.size(); i++) {
				Adder ad = available.get(i);
				if (ad.isRequired()) {
					ad = ad.clone();
					ad.setSelected(true);
					ad.setAlias(ad.getDisplay());
					boolean added = false;
					for (int j = 0; j < assigned.size(); j++) {
						Adder comp = assigned.get(j);
						if (comp.getXMLID().equals(ad.getXMLID())) {
							assigned.set(j, ad);
							added = true;
						}
					}
					if (!added) {
						assigned.add(ad);
					}
				}
			}
		}
		if ((object.getOptions().size() > 0)
				&& (isNew || (object.getSelectedOption() == null))) {
			Adder o = object.getOptions().get(0);
			object.setSelectedOption(o);
		}
		initWidgets();
		if (object.getSelectedOption() != null) {
			double base = object.getBaseCost();
			String alias = object.getSelectedOption().getAlias();
			optionsCB.setSelectedItem(object.getSelectedOption());
			((JTextField) optionsCB.getEditor().getEditorComponent())
					.setText(alias);
			if (pointsTF != null) {
				pointsTF.setCurrent((int) Rounder.roundHalfUp(base));
			}
		}
		pack();
		if (HeroDesigner.getInstance().getPrefs().isRememberDialogPosition()) {
			if ((HeroDesigner.getInstance().getPrefs().getDialogX() > 50)
					&& (HeroDesigner.getInstance().getPrefs().getDialogY() > 50)) {
				setSize(new Dimension(HeroDesigner.getInstance().getPrefs()
						.getDialogX(), HeroDesigner.getInstance().getPrefs()
						.getDialogY()));
			} else {
				setSize(getPreferredSize());
			}
			if ((HeroDesigner.getInstance().getPrefs().getDialogScreenX() > 0)
					&& (HeroDesigner.getInstance().getPrefs()
							.getDialogScreenY() > 0)) {
				setLocation(HeroDesigner.getInstance().getPrefs()
						.getDialogScreenX(), HeroDesigner.getInstance()
						.getPrefs().getDialogScreenY());
			} else {
				setLocationRelativeTo(HeroDesigner.getAppFrame());
			}
		} else {
			setSize(new Dimension(700, 550));
			setLocationRelativeTo(HeroDesigner.getAppFrame());
		}
		initListeners();
		layoutComponent();
		updateValues();

		if (!HeroDesigner.getActiveHero().getRules().multiplierAllowed()) {
			multiplierBtn.setVisible(false);
		} else {
			multiplierBtn.setVisible(true);
		}
		// once more to set unavailable adders
		layoutExtrasPanel();
		updateValues();
	}

	protected void initListeners() {
		endSourceCombo.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				object.setUseENDReserve(endSourceCombo.getSelectedIndex() == 1);
			}
		});
		multiplierBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				MultiplierDialog dialog = new MultiplierDialog();
				dialog.setLocationRelativeTo(GenericDialog.this);
				dialog.setVisible(true);
				dialog.dispose();
				GenericObject.lastEdit = System.currentTimeMillis();
				updateValues();
			}
		});
		carriedCB.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				object.setCarried(carriedCB.isSelected());
				GenericObject.lastEdit = System.currentTimeMillis();
			}
		});
		priceTF.getDocument().addDocumentListener(new DocumentListener() {
			public void changedUpdate(DocumentEvent e) {
				if (priceTF.getText().trim().length() == 0) {
					object.setPrice(0);
				} else {
					object.setPrice(Double.parseDouble(priceTF.getText()));
				}
				GenericObject.lastEdit = System.currentTimeMillis();
			}

			public void insertUpdate(DocumentEvent e) {
				if (priceTF.getText().trim().length() == 0) {
					object.setPrice(0);
				} else {
					object.setPrice(Double.parseDouble(priceTF.getText()));
				}
				GenericObject.lastEdit = System.currentTimeMillis();
			}

			public void removeUpdate(DocumentEvent e) {
				if (priceTF.getText().trim().length() == 0) {
					object.setPrice(0);
				} else {
					object.setPrice(Double.parseDouble(priceTF.getText()));
				}
				GenericObject.lastEdit = System.currentTimeMillis();
			}
		});
		weightTF.getDocument().addDocumentListener(new DocumentListener() {
			public void changedUpdate(DocumentEvent e) {
				if (weightTF.getText().trim().length() == 0) {
					object.setWeight(0);
				} else {
					object.setWeight(Double.parseDouble(weightTF.getText()));
				}
				GenericObject.lastEdit = System.currentTimeMillis();
			}

			public void insertUpdate(DocumentEvent e) {
				if (weightTF.getText().trim().length() == 0) {
					object.setWeight(0);
				} else {
					object.setWeight(Double.parseDouble(weightTF.getText()));
				}
				GenericObject.lastEdit = System.currentTimeMillis();
			}

			public void removeUpdate(DocumentEvent e) {
				if (weightTF.getText().trim().length() == 0) {
					object.setWeight(0);
				} else {
					object.setWeight(Double.parseDouble(weightTF.getText()));
				}
				GenericObject.lastEdit = System.currentTimeMillis();
			}
		});
		for (int i = 0; i < requiredAdderCombos.size(); i++) {
			JComboBox combo = requiredAdderCombos.get(i);
			Adder ad = requiredAdderAdders.get(i);
			combo.addItemListener(new RequiredAdderComboListener(ad, combo));
		}
		for (int i = 0; i < requiredLevelAdderTFs.size(); i++) {
			LevelTF tf = requiredLevelAdderTFs.get(i);
			Adder ad = requiredLevelAdderAdders.get(i);
			tf
					.addPropertyChangeListener(new RequiredAdderLevelListener(
							ad, tf));
		}
		displayActivePointsCB.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				object.setDisplayActiveCost(displayActivePointsCB.isSelected());
				GenericObject.lastEdit = System.currentTimeMillis();
			}
		});
		ultraCB.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				object.setUltra(ultraCB.isSelected());
				GenericObject.lastEdit = System.currentTimeMillis();
				updateValues();
			}
		});
		nameTF.getDocument().addDocumentListener(new DocumentListener() {
			public void changedUpdate(DocumentEvent e) {
				object.setName(nameTF.getText());
				GenericObject.lastEdit = System.currentTimeMillis();
			}

			public void insertUpdate(DocumentEvent e) {
				object.setName(nameTF.getText());
				GenericObject.lastEdit = System.currentTimeMillis();
			}

			public void removeUpdate(DocumentEvent e) {
				object.setName(nameTF.getText());
				GenericObject.lastEdit = System.currentTimeMillis();
			}
		});
		optionListener = new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				if (optionsCB.getSelectedItem() instanceof Adder) {
					Adder ad = (Adder) optionsCB.getSelectedItem();
					String aliasHolder = ad.getAlias();
					String displayHolder = ad.getDisplay();
					object.setSelectedOption(ad);
					GenericObject.lastEdit = System.currentTimeMillis();
					updateValues();
					layoutExtrasPanel();
					// the following is necessary to prevent race conditions
					// with the document listener on the combobox
					if (object.getSelectedOption() != null) {
						object.getSelectedOption().setAlias(aliasHolder);
						if (object.getSelectedOption().getDisplay().equals(
								displayHolder)) {
							((JTextField) optionsCB.getEditor()
									.getEditorComponent()).setText(aliasHolder);
						} else {
							((JTextField) optionsCB.getEditor()
									.getEditorComponent()).setText(object
									.getSelectedOption().getDisplay());
						}
					}
				}
			}
		};
		optionsCB.addItemListener(optionListener);
		optionAliasListener = new DocumentListener() {
			public void changedUpdate(DocumentEvent e) {
			}

			public void insertUpdate(DocumentEvent e) {
				if (object.getSelectedOption() != null) {
					object.getSelectedOption().setAlias(
							((JTextField) optionsCB.getEditor()
									.getEditorComponent()).getText());
				}
				GenericObject.lastEdit = System.currentTimeMillis();
			}

			public void removeUpdate(DocumentEvent e) {
				if (object.getSelectedOption() != null) {
					object.getSelectedOption().setAlias(
							((JTextField) optionsCB.getEditor()
									.getEditorComponent()).getText());
				}
				GenericObject.lastEdit = System.currentTimeMillis();
			}
		};
		((JTextField) optionsCB.getEditor().getEditorComponent()).getDocument()
				.addDocumentListener(optionAliasListener);
		privateAdderBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (!(object instanceof List)) {
					return;
				}
				Adder adder = new Adder(new Element("ADDER"));
				adder.setDisplay("Private Adder");
				adder.setAlias("Private Adder");
				adder.setPrivate(true);
				adder.setBaseCost(0);
				adder.setSelected(true);
				adder.setExclusive(false);
				((List) object).getPrivateAdders().add(adder);
				GenericObject.lastEdit = System.currentTimeMillis();
				layoutExtrasPanel();
				validate();
				privateAdderPanel.scrollRectToVisible(privateAdderPanel
						.getBounds());
				updateValues();
			}
		});
		customAdderBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Adder adder = new Adder(new Element("ADDER"));
				adder.setDisplay(object instanceof List ? "Common Adder"
						: "Custom Adder");
				adder.setAlias(object instanceof List ? "Common Adder"
						: "Custom Adder");
				adder.setBaseCost(0);
				adder.setSelected(true);
				adder.setExclusive(false);
				object.getAssignedAdders().add(adder);
				GenericObject.lastEdit = System.currentTimeMillis();
				layoutExtrasPanel();
				validate();
				customAdderPanel.scrollRectToVisible(customAdderPanel
						.getBounds());
				updateValues();
			}
		});
		displayListener = new DocumentListener() {
			public void changedUpdate(DocumentEvent e) {
				object.setAlias(displayTF.getText());
				GenericObject.lastEdit = System.currentTimeMillis();
				updateValues();
			}

			public void insertUpdate(DocumentEvent e) {
				object.setAlias(displayTF.getText());
				GenericObject.lastEdit = System.currentTimeMillis();
				updateValues();
			}

			public void removeUpdate(DocumentEvent e) {
				object.setAlias(displayTF.getText());
				GenericObject.lastEdit = System.currentTimeMillis();
				updateValues();
			}
		};
		displayTF.getDocument().addDocumentListener(displayListener);
		exampleItemListener = new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				if (object.isOtherInputAllowed()) {
					object.setInput(((JTextField) exampleCombo.getEditor()
							.getEditorComponent()).getText());
				} else {
					object.setInput(exampleCombo.getSelectedItem().toString());
				}
				GenericObject.lastEdit = System.currentTimeMillis();
				// updateValues();
			}
		};
		exampleCombo.addItemListener(exampleItemListener);
		if (object.isOtherInputAllowed()) {
			exampleDocumentListener = new DocumentListener() {
				public void changedUpdate(DocumentEvent e) {
					String input = ((JTextField) exampleCombo.getEditor()
							.getEditorComponent()).getText();
					object.setInput(input);
					GenericObject.lastEdit = System.currentTimeMillis();
					// updateValues();
				}

				public void insertUpdate(DocumentEvent e) {
					String input = ((JTextField) exampleCombo.getEditor()
							.getEditorComponent()).getText();
					object.setInput(input);
					GenericObject.lastEdit = System.currentTimeMillis();
					// updateValues();
				}

				public void removeUpdate(DocumentEvent e) {
					String input = ((JTextField) exampleCombo.getEditor()
							.getEditorComponent()).getText();
					object.setInput(input);
					GenericObject.lastEdit = System.currentTimeMillis();
					// updateValues();
				}
			};
			((JTextField) exampleCombo.getEditor().getEditorComponent())
					.getDocument().addDocumentListener(exampleDocumentListener);
		}
		levelTF.addPropertyChangeListener("Level",
				new PropertyChangeListener() {
					public void propertyChange(PropertyChangeEvent e) {
						if (object.getLevels() == levelTF.getCurrent()) {
							return;
						}
						object.setLevels(levelTF.getCurrent());
						GenericObject.lastEdit = System.currentTimeMillis();
						updateValues();
						if ((object instanceof SenseAdder)
								|| (object instanceof Entangle)) {
							layoutExtrasPanel();
						}
					}
				});
		defineBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (object == null) {
					return;
				}
				if (object.getDefinition() == null) {
					return;
				}
				if (object.getDefinition().trim().length() == 0) {
					return;
				}
				PopupMessage popup = PopupMessage.getInstance(HeroDesigner
						.getAppFrame(), defineBtn, object.getDefinition(),
						false);
				popup.setVisible(true);
			}
		});
		cancelBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				cancelButtonClicked = true;
				deleteButtonClicked = false;
				okButtonClicked = false;
				setVisible(false);
			}
		});
		okBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				GenericObject.lastEdit = System.currentTimeMillis();
				cancelButtonClicked = false;
				deleteButtonClicked = false;
				okButtonClicked = true;
				setVisible(false);
			}
		});
		deleteBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				cancelButtonClicked = false;
				deleteButtonClicked = true;
				okButtonClicked = false;
				setVisible(false);
				updateValues();
			}
		});
		notesBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				NotesDialog dialog = new NotesDialog(object, GenericDialog.this);
				dialog.setSize(getSize());
				dialog.setLocationRelativeTo(GenericDialog.this);
				dialog.setVisible(true);
				updateValues();
			}
		});
		addModifierBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				AvailableModifiersDialog dialog = new AvailableModifiersDialog(
						object, GenericDialog.this);
				dialog.setVisible(true);
				GenericObject.lastEdit = System.currentTimeMillis();
			}
		});
		if ((object.getMinimumCost() != object.getMaxCost())
				&& object.isMinSet() && object.isMaxSet()) {
			pointsTF.addPropertyChangeListener("Level",
					new PropertyChangeListener() {
						public void propertyChange(PropertyChangeEvent e) {
							object.setBaseCost(pointsTF.getCurrent());
							GenericObject.lastEdit = System.currentTimeMillis();
							updateValues();
						}
					});
		}
		sfxCombo.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				object.setSFX(((JTextField) sfxCombo.getEditor()
						.getEditorComponent()).getText());
			}
		});
		((JTextField) sfxCombo.getEditor().getEditorComponent()).getDocument()
				.addDocumentListener(new DocumentListener() {
					public void changedUpdate(DocumentEvent e) {
						object.setSFX(((JTextField) sfxCombo.getEditor()
								.getEditorComponent()).getText());
					}

					public void insertUpdate(DocumentEvent e) {
						object.setSFX(((JTextField) sfxCombo.getEditor()
								.getEditorComponent()).getText());
					}

					public void removeUpdate(DocumentEvent e) {
						object.setSFX(((JTextField) sfxCombo.getEditor()
								.getEditorComponent()).getText());
					}
				});
	}

	protected void initWidgets() {
		multiplierBtn = new JButton("Cost Multiplier");
		equipmentPanel = new JPanel(new GridBagLayout());
		carriedCB = new JCheckBox("Carried");
		carriedCB.setSelected(object.isCarried());
		priceLbl = new JLabel("Price: ");
		weightLbl = new JLabel("Weight: ");
		BigDecimal bd = new BigDecimal(object.getPrice());
		bd = bd.setScale(HeroDesigner.getActiveHero().getRules()
				.getEquipmentCostDecimalPlaces(), BigDecimal.ROUND_HALF_UP);
		priceTF = new JTextField(
				new DoubleDocument(99999999999d, -99999999999d), bd.toString(),
				6);
		bd = new BigDecimal(object.getWeight());
		bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
		weightTF = new JTextField(new DoubleDocument(99999999999d,
				-99999999999d), bd.toString(), 6);
		requiredAdderCombos = new ArrayList<JComboBox>();
		requiredAdderAdders = new ArrayList<Adder>();
		requiredLevelAdderTFs = new ArrayList<LevelTF>();
		requiredLevelAdderAdders = new ArrayList<Adder>();
		for (int i = 0; i < object.getAssignedAdders().size(); i++) {
			Adder ad = object.getAssignedAdders().get(i);
			if (ad.isRequired()) {
				if (isNew) {
					ad.setAlias(ad.getDisplay());
				}
				if (ad.getOptions() != null && ad.getOptions().size() > 0) {
					JComboBox combo = new JComboBox(ad.getOptions().toArray());
					combo.setEditable(true);
					combo.setSelectedItem(ad.getSelectedOption());
					((JTextField) combo.getEditor().getEditorComponent())
							.setText(ad.getSelectedOption().getAlias());
					requiredAdderCombos.add(combo);
					requiredAdderAdders.add(ad);
				} else if (ad.getLevelCost() > 0) {
					LevelTF tf = new LevelTF(ad.getLevels(), ad.getMaxLevel(),
							ad.getMinimumLevel());
					requiredLevelAdderAdders.add(ad);
					requiredLevelAdderTFs.add(tf);
				}
			}
		}
		adderPanels = new ArrayList<AdderPanel>();
		modifierPanels = new ArrayList<ModifierPanel>();
		optionsLbl = new JLabel(object.getOptionLabel() + ":");
		optionsCB = new JComboBox(object.getOptions().toArray());
		optionsCB.setEditable(true);
		displayLbl = new JLabel("Display:");
		levelsLbl = new JLabel(object.getLevelsLabel());
		inputLbl = new JLabel(object.getInputLabel());
		activePointsLbl = new JLabel("Active Point:  "
				+ Rounder.roundUp(object.getActiveCost()));
		realCostLbl = new JLabel("Real Cost:  "
				+ Rounder.roundUp(object.getRealCost()));
		displayActivePointsCB = new JCheckBox("Display Active Points");
		displayActivePointsCB.setSelected(object.getDisplayActiveCost());
		displayTF = new JTextField(object.getAlias(), 20);
		levelTF = new LevelTF(object.getLevels(), object.getMaxLevel(), object
				.getMinimumLevel());
		exampleCombo = new JComboBox(object.getExamples().toArray());
		exampleCombo.setEditable(object.isOtherInputAllowed());
		exampleCombo.setFont(displayTF.getFont());
		if ((object.getInput() != null) && (object.getExamples().size() > 0)) {
			exampleCombo.setSelectedItem(object.getInput());
		} else if (object.getInput() != null) {
			((JTextField) exampleCombo.getEditor().getEditorComponent())
					.setText(object.getInput());
		}
		extrasPanel = new JPanel(new GridBagLayout());
		extrasPanel.setBackground(Color.white);
		extrasPanel.setOpaque(true);
		extrasScroll = new JScrollPane(extrasPanel);
		extrasScroll.getVerticalScrollBar().setUnitIncrement(10);
		extrasScroll.setPreferredSize(new Dimension(650, 150));
		extrasScroll.getVerticalScrollBar().setUnitIncrement(5);
		extrasScroll.getViewport().setBackground(Color.white);
		extrasScroll.getViewport().setOpaque(true);
		customAdderPanel = new JPanel(new GridBagLayout());
		customAdderPanel.setBorder(BorderFactory
				.createTitledBorder(object instanceof List ? "Common Adders"
						: "Custom Adders"));
		customAdderPanel.setOpaque(false);
		adderPanel = new JPanel(new GridBagLayout());
		adderPanel.setBorder(BorderFactory
				.createTitledBorder((object instanceof List ? "Common " : "")
						+ "Adders"));
		adderPanel.setOpaque(false);
		modifierPanel = new JPanel(new GridBagLayout());
		modifierPanel.setBorder(BorderFactory
				.createTitledBorder((object instanceof List ? "Common " : "")
						+ "Modifiers"));
		modifierPanel.setOpaque(false);
		privateModifierPanel = new JPanel(new GridBagLayout());
		privateModifierPanel.setBorder(BorderFactory
				.createTitledBorder("Private Modifiers"));
		privateModifierPanel.setOpaque(false);
		privateAdderPanel = new JPanel(new GridBagLayout());
		privateAdderPanel.setBorder(BorderFactory
				.createTitledBorder("Private Adders"));
		privateAdderPanel.setOpaque(false);
		multiplierPanel = new JPanel(new GridBagLayout());
		multiplierPanel.setBorder(BorderFactory
				.createTitledBorder("Cost Multipliers"));
		multiplierPanel.setOpaque(false);
		customAdderBtn = new JButton(object instanceof List ? "Common Adder"
				: "Custom Adder");
		customAdderBtn.setVisible(object.allowsOtherAdders());
		privateAdderBtn = new JButton("Private Adder");
		privateAdderBtn.setVisible(object instanceof List);
		addModifierBtn = new JButton("Add Modifier...");
		addModifierBtn.setVisible(object.allowsOtherModifiers());
		notesBtn = new JButton("Notes...");
		okBtn = new JButton("OK");
		deleteBtn = new JButton("Delete");
		deleteBtn.setVisible(!isNew);
		cancelBtn = new JButton("Cancel");
		defineBtn = new JButton("Define");
		if ((object.getDefinition() != null)
				&& (object.getDefinition().trim().length() > 0)) {
			defineBtn.setEnabled(true);
		} else {
			defineBtn.setEnabled(false);
		}
		if (object.getMinimumCost() != object.getMaxCost()) {
			pointsLbl = new JLabel("Points:");
			pointsTF = new LevelTF((int) object.getBaseCost(), (int) object
					.getMaxCost(), (int) object.getMinimumCost());
		}
		nameLbl = new JLabel("Name:");
		nameTF = new JTextField(object.getName());
		Font font = nameTF.getFont();
		font = new Font(font.getFontName(), Font.ITALIC, font.getSize());
		nameTF.setFont(font);
		if (HeroDesigner.getActiveTemplate().is6E()) {
			ultraCB = new JCheckBox("Fixed Slot");
		} else {
			ultraCB = new JCheckBox("Ultra Slot");
		}
		ultraCB.setSelected(object.isUltra());
		sfxLbl = new JLabel("Special Effect:");
		ArrayList<String> fx = new ArrayList<String>();
		for (int i = 0; i < GenericObject.standardSFX.length; i++) {
			fx.add(GenericObject.standardSFX[i]);
		}
		sfxCombo = new JComboBox(fx.toArray());
		sfxCombo.setEditable(true);
		sfxCombo.setSelectedItem(object.getSFX());
		endSourceLbl = new JLabel("END Source:");
		ArrayList<String> source = new ArrayList<String>();
		source.add("Personal END");
		source.add("END Reserve");
		endSourceCombo = new JComboBox(source.toArray());
		if (object.getUseENDReserve()) {
			endSourceCombo.setSelectedIndex(1);
		} else {
			endSourceCombo.setSelectedIndex(0);
		}
		if ((object.getEndUsage() == 0)
				|| ((GenericObject.findObjectByID(HeroDesigner.getActiveHero()
						.getPowers(), "ENDURANCERESERVE") == null) && (GenericObject
						.findObjectByID(HeroDesigner.getActiveHero()
								.getEquipment(), "ENDURANCERESERVE") == null))
				|| (GenericObject.findObjectByID(object.getAssignedModifiers(),
						"ENDRESERVEOREND") != null)) {
			endSourceLbl.setVisible(false);
			endSourceCombo.setVisible(false);
		}
	}

	private void insertAdder(Adder adder, GenericObject parent,
			AdderPanel parentPanel, GridBagConstraints gbc, int level) {
		if (adder.isRequired() && !(parent instanceof Adder)
				&& !(parent instanceof Modifier)) {
			// just return...we'll take care of it in the main dialog...
			return;
		}
		gbc.insets = new Insets(0, level * 20, 0, 0);
		AdderPanel panel = new AdderPanel(parent, adder, parentPanel, this);
		adderPanels.add(panel);
		adderPanel.add(panel, gbc);
		gbc.gridy++;
		if (adder.getAvailableAdders() != null) {
			for (int i = 0; i < adder.getAvailableAdders().size(); i++) {
				try {
					Adder ad = adder.getAvailableAdders().get(i);
					ad = ad.clone();
					ad.setAlias(ad.getDisplay());
					ad.setSelected(false);
					ad.setAvailableCheck(true);
					boolean inserted = false;
					for (int j = 0; j < adder.getAssignedAdders().size(); j++) {
						Adder check = adder.getAssignedAdders().get(j);
						if (check.equals(ad)) {
							insertAdder(check, adder, panel, gbc, level + 1);
							inserted = true;
						}
					}
					ad.setAvailableCheck(false);
					if (!inserted) {
						insertAdder(ad, adder, panel, gbc, level + 1);
					}
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		}
	}

	private void insertModifier(Modifier mod, GenericObject parent,
			GridBagConstraints gbc, int level) {
		gbc.insets = new Insets(0, level * 20, 0, 0);
		ModifierPanel panel = new ModifierPanel(object, mod, this);
		modifierPanels.add(panel);
		gbc.gridy++;
		if (mod.isPrivate()) {
			privateModifierPanel.add(panel, gbc);
		} else {
			modifierPanel.add(panel, gbc);
		}
	}

	private void insertMultiplier(Modifier mod, GenericObject parent,
			GridBagConstraints gbc, int level) {
		gbc.insets = new Insets(0, level * 20, 0, 0);
		ModifierPanel panel = new ModifierPanel(object, mod, this);
		modifierPanels.add(panel);
		gbc.gridy++;
		multiplierPanel.add(panel, gbc);
	}

	protected void layoutComponent() {
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.weightx = 1;
		gbc.weighty = 0;
		gbc.gridwidth = GridBagConstraints.REMAINDER;
		gbc.gridheight = 1;
		gbc.fill = GridBagConstraints.BOTH;
		gbc.anchor = GridBagConstraints.WEST;
		gbc.insets = new Insets(0, 0, 0, 0);
		gbc.weighty = 0;
		extrasPanel.add(adderPanel, gbc);
		gbc.gridy++;
		extrasPanel.add(privateAdderPanel, gbc);
		gbc.gridy++;
		extrasPanel.add(customAdderPanel, gbc);
		gbc.gridy++;
		gbc.weighty = 0;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		extrasPanel.add(multiplierPanel, gbc);
		gbc.gridy++;
		extrasPanel.add(modifierPanel, gbc);
		gbc.gridy++;
		extrasPanel.add(privateModifierPanel, gbc);
		gbc.weighty = 1;
		gbc.fill = GridBagConstraints.BOTH;
		JPanel filler = new JPanel();
		filler.setOpaque(true);
		filler.setBackground(Color.white);
		extrasPanel.add(filler, gbc);
		extrasPanel.setOpaque(true);
		extrasPanel.setBackground(Color.white);
		gbc.gridy = 0;
		gbc.weighty = 0;
		gbc.fill = GridBagConstraints.BOTH;
		gbc.insets = new Insets(5, 5, 5, 5);
		JPanel panel = new JPanel(new GridBagLayout());
		panel.add(getTopPanel(), gbc);
		gbc.anchor = GridBagConstraints.WEST;
		gbc.fill = GridBagConstraints.NONE;
		gbc.gridwidth = 1;
		gbc.gridy++;
		panel.add(activePointsLbl, gbc);
		gbc.gridx = 1;
		gbc.gridwidth = 2;
		gbc.anchor = GridBagConstraints.CENTER;
		JPanel holder = new JPanel(new FlowLayout(FlowLayout.CENTER));
		holder.add(displayActivePointsCB);
		if (isPower && (object.getParentList() != null)
				&& (object.getParentList() instanceof Multipower)
				&& (object.getMainPower() == null)) {
			holder.add(ultraCB);
		}
		panel.add(holder, gbc);
		gbc.gridx = 3;
		gbc.gridwidth = 1;
		gbc.anchor = GridBagConstraints.EAST;
		panel.add(realCostLbl, gbc);
		gbc.gridwidth = GridBagConstraints.REMAINDER;
		gbc.anchor = GridBagConstraints.WEST;
		gbc.gridy++;
		gbc.gridx = 0;
		gbc.weighty = 1;
		gbc.weightx = 1;
		gbc.fill = GridBagConstraints.BOTH;
		panel.add(extrasScroll, gbc);
		gbc.gridy++;
		gbc.weighty = 0;
		gbc.weightx = 0;
		gbc.fill = GridBagConstraints.NONE;
		gbc.anchor = GridBagConstraints.NORTH;
		panel.add(getModsPanel(), gbc);
		gbc.gridy++;
		gbc.anchor = GridBagConstraints.SOUTH;
		gbc.gridy++;
		JPanel buttons = new JPanel(new FlowLayout(FlowLayout.CENTER));
		buttons.add(notesBtn);
		buttons.add(multiplierBtn);
		buttons.add(okBtn);
		buttons.add(deleteBtn);
		buttons.add(cancelBtn);
		buttons.add(defineBtn);
		panel.add(buttons, gbc);
		setContentPane(new JScrollPane(panel));
	}

	public void layoutExtrasPanel() {
		object.verifyModifiers();
		adderPanel.removeAll();
		customAdderPanel.removeAll();
		modifierPanel.removeAll();
		privateModifierPanel.removeAll();
		privateAdderPanel.removeAll();
		multiplierPanel.removeAll();
		adderPanels.clear();
		modifierPanels.clear();
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.weightx = 1;
		gbc.weighty = 1;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.anchor = GridBagConstraints.NORTHWEST;
		gbc.insets = new Insets(0, 0, 0, 0);
		ArrayList<Adder> assigned = new ArrayList<Adder>();
		ArrayList<Adder> available = object.getAvailableAdders();
		ArrayList<Adder> assignedAdders = object.getAssignedAdders();
		ArrayList<Adder> privateAdders = new ArrayList<Adder>();
		if (object instanceof List) {
			privateAdders = ((List) object).getPrivateAdders();
		}
		for (int i = 0; i < available.size(); i++) {
			try {
				Adder adder = available.get(i);
				if (adder.isRequired()) {
					continue;
				}
				adder = adder.clone();
				adder.setSelected(false); // fix for lack of cloning
				boolean inserted = false;
				for (int j = 0; j < assignedAdders.size(); j++) {
					Adder comp = assignedAdders.get(j);
					if (comp.getXMLID().equals(adder.getXMLID())
							&& comp.display.equals(adder.display)
							&& !(comp.getXMLID().equals("ADDER") || comp
									.getXMLID().equals("GENERIC_OBJECT"))) {
						adder = comp;
						assigned.add(adder);
						insertAdder(adder, object, null, gbc, 0);
						inserted = true;
					}
				}
				if (!inserted) {
					insertAdder(adder, object, null, gbc, 0);
				}
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
		if (gbc.gridy > 0) {
			adderPanel.setVisible(true);
		} else {
			adderPanel.setVisible(false);
		}
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.weightx = 1;
		gbc.weighty = 1;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.anchor = GridBagConstraints.NORTHWEST;
		gbc.insets = new Insets(0, 0, 0, 0);
		for (int i = 0; i < assignedAdders.size(); i++) {
			Adder adder = assignedAdders.get(i);
			if (!assigned.contains(adder) && !adder.isRequired()) {
				// it's a custom adder....
				CustomAdderPanel panel = new CustomAdderPanel(object, adder,
						GenericDialog.this);
				customAdderPanel.add(panel, gbc);
				gbc.gridy++;
			}
		}
		if (gbc.gridy > 0) {
			customAdderPanel.setVisible(true);
		} else {
			customAdderPanel.setVisible(false);
		}
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.weightx = 1;
		gbc.weighty = 1;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.anchor = GridBagConstraints.NORTHWEST;
		gbc.insets = new Insets(0, 0, 0, 0);
		for (int i = 0; i < privateAdders.size(); i++) {
			Adder adder = privateAdders.get(i);
			if (!adder.isSelected()) {
				continue;
			}
			// it's a custom adder....
			CustomAdderPanel panel = new CustomAdderPanel(object, adder,
					GenericDialog.this);
			privateAdderPanel.add(panel, gbc);
			gbc.gridy++;
		}
		privateAdderPanel.setVisible(gbc.gridy > 0);
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.weightx = 1;
		gbc.weighty = 1;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.anchor = GridBagConstraints.NORTHWEST;
		gbc.insets = new Insets(0, 0, 0, 0);
		multiplierPanel.setVisible(false);
		modifierPanel.setVisible(false);
		privateModifierPanel.setVisible(false);
		int numPublic = 0;
		int numPrivate = 0;
		ArrayList<Modifier> vec = object.getAssignedModifiers();
		vec = (ArrayList<Modifier>) vec.clone();
		if (object instanceof List) {
			List list = (List) object;
			vec.addAll(list.getPrivateMods());
		}
		for (int i = 0; i < object.getAvailableModifiers().size(); i++) {
			Modifier mod = object.getAvailableModifiers().get(i);
			if (!mod.useMultiplier()) {
				continue;
			}
			if (mod.included(object).trim().length() > 0) {
				continue;
			}
			mod = mod.clone();
			mod.setAlias(mod.getDisplay());
			mod.setAvailableCheck(true);
			INNER: for (int j = 0; j < vec.size(); j++) {
				Modifier test = vec.get(j);
				test.setAvailableCheck(true);
				if (test.equals(mod)) {
					mod = test;
					test.setAvailableCheck(false);
					break INNER;
				}
				test.setAvailableCheck(false);
			}
			mod.setAvailableCheck(false);
			insertMultiplier(mod, object, gbc, 0);
			multiplierPanel.setVisible(true);
		}
		for (int i = 0; i < vec.size(); i++) {
			Modifier mod = vec.get(i);
			mod.setAvailableCheck(true);
			if (mod.useMultiplier() && !mod.getXMLID().equals("GENERIC_OBJECT")
					&& !mod.getXMLID().equals("CUSTOM_MODIFIER")) {
				INNER: for (int j = 0; j < object.getAvailableModifiers()
						.size(); j++) {
					Modifier test = object.getAvailableModifiers().get(j);
					test.setAvailableCheck(true);
					if (test.equals(mod)) {
						test.setAvailableCheck(false);
						mod = null;
						break INNER;
					}
				}
			}
			if (mod != null) {
				mod.setAvailableCheck(false);
				if (mod.useMultiplier()) {
					insertMultiplier(mod, object, gbc, 0);
					multiplierPanel.setVisible(true);
				} else {
					insertModifier(mod, object, gbc, 0);
					if (mod.isPrivate()) {
						numPrivate++;
					} else {
						numPublic++;
					}
				}
			}
		}
		if (numPublic > 0) {
			modifierPanel.setVisible(true);
		}
		if (numPrivate > 0) {
			privateModifierPanel.setVisible(true);
		}
		modifierPanel.revalidate();
		privateModifierPanel.revalidate();
		extrasPanel.revalidate();
		extrasScroll.revalidate();
	}

	/**
	 * Recalculates all values and performs the layout on the extras panel
	 * (which displays the Adders and Modifiers).
	 */
	public void recalc() {
		layoutExtrasPanel();
		validate();
		Rectangle rect = modifierPanel.getBounds();
		rect = new Rectangle(rect.x, rect.y + rect.height, rect.width,
				rect.height);
		modifierPanel.scrollRectToVisible(rect);
		updateValues();
	}

	/**
	 * Resets the ModifierPanels
	 */
	public void resetMods() {
		ArrayList<Modifier> assigned = object.getAssignedModifiers();
		for (int i = 0; i < modifierPanels.size(); i++) {
			ModifierPanel panel = modifierPanels.get(i);
			if (GenericObject.findObjectByID(assigned, panel.getModifier()
					.getXMLID()) == null) {
				panel.setSelected(false);
			}
		}
		updateValues();
	}

	@Override
	public void setLocationRelativeTo(Component c) {
		if (!HeroDesigner.getInstance().getPrefs().isRememberDialogPosition()) {
			super.setLocationRelativeTo(c);
		}
	}

	/**
	 * Whether the name field (and label) are visible.
	 * 
	 * @param val
	 */
	public void setNameVisible(boolean val) {
		nameTF.setVisible(val);
		nameLbl.setVisible(val);
	}

	/**
	 * Sets the visibility of this dialog.
	 */
	@Override
	public void setVisible(boolean val) {
		object.setAppAdjusted(false);
		if ((object instanceof Sense) || (object instanceof SenseAdder)) {
			Power.lastSenseEdit = System.currentTimeMillis();
		}
		if (!val && isShowing()) {
			HeroDesigner.getInstance().getPrefs().setDialogScreenX(
					(int) getLocationOnScreen().getX());
			HeroDesigner.getInstance().getPrefs().setDialogScreenY(
					(int) getLocationOnScreen().getY());
			HeroDesigner.getInstance().getPrefs().setDialogX(
					this.getSize().width);
			HeroDesigner.getInstance().getPrefs().setDialogY(
					this.getSize().height);
		}
		if (!val && isVisible()) {
			super.setVisible(false);
			dispose();
		} else {
			super.setVisible(val);
		}
	}

	/**
	 * Updates all values in the dialog.
	 */
	public void updateValues() {
		if ((object.getEndUsage() == 0)
				|| ((GenericObject.findObjectByID(HeroDesigner.getActiveHero()
						.getPowers(), "ENDURANCERESERVE") == null) && (GenericObject
						.findObjectByID(HeroDesigner.getActiveHero()
								.getEquipment(), "ENDURANCERESERVE") == null))
				|| (GenericObject.findObjectByID(object.getAssignedModifiers(),
						"ENDRESERVEOREND") != null)) {
			endSourceLbl.setVisible(false);
			endSourceCombo.setVisible(false);
		} else {
			endSourceLbl.setVisible(true);
			endSourceCombo.setVisible(true);
		}
		if ((object.getNotes() != null)
				&& (object.getNotes().trim().length() > 0)) {
			notesBtn.setText("Edit Notes...");
		} else {
			notesBtn.setText("Create Notes...");
		}
		if (levelTF != null) {
			levelTF.setMin(object.getMinimumLevel());
			levelTF.setMax(object.getMaxLevel());
			levelTF.setCurrent(object.getLevels());
		}
		if (((object.getActiveCost() != object.getTotalCost()) || (object
				.getRealCost() != object.getTotalCost()))
				&& !(object instanceof Modifier)
				&& !(object instanceof Disadvantage)) {
			displayActivePointsCB.setVisible(true);
		} else {
			displayActivePointsCB.setVisible(false);
		}
		customAdderBtn.setVisible(object.allowsOtherAdders());
		addModifierBtn.setVisible(object.allowsOtherModifiers());
		if ((pointsTF != null) && pointsTF.isShowing()) {
			pointsTF.setMin((int) Rounder.roundHalfUp(object.getMinimumCost()));
			pointsTF.setMax((int) Rounder.roundHalfUp(object.getMaxCost()));
			pointsTF
					.setCurrent((int) Rounder.roundHalfUp(object.getBaseCost()));
		}
		activePointsLbl.setText("Active Points: "
				+ Rounder.roundUp(object.getActiveCost()));
		realCostLbl.setText("Real Cost: "
				+ Rounder.roundUp(object.getRealCost()));
		if (!object.getAlias().equals(displayTF.getText())) {
			try {
				displayTF.setText(object.getAlias());
			} catch (Exception ex) {
			}
		}
		ArrayList<String> excludedIDs = new ArrayList<String>();
		for (int i = 0; i < adderPanels.size(); i++) {
			AdderPanel panel = adderPanels.get(i);
			if (panel.isSelected()) {
				for (int j = 0; j < panel.getAdder().getExcludes().size(); j++) {
					String xmlid = panel.getAdder().getExcludes().get(j);
					excludedIDs.add(xmlid.toUpperCase().trim());
				}
			}
		}
		for (int i = 0; i < adderPanels.size(); i++) {
			AdderPanel panel = adderPanels.get(i);
			if ((excludedIDs.size() > 0)
					&& excludedIDs.contains(panel.getAdder().getXMLID()
							.toUpperCase().trim())) {
				panel.setSelectionLocked(true);
				panel.setSelected(false);
			} else {
				panel.setSelectionLocked(false);
			}
			boolean requirementsMet = panel.getAdder().getRequires().size() == 0;
			LOOP: for (int j = 0; j < panel.getAdder().getRequires().size(); j++) {
				String id = panel.getAdder().getRequires().get(j).toUpperCase()
						.trim();
				Adder requirement = getAdderFromList(id, object
						.getAssignedAdders());
				if ((requirement != null) && requirement.isSelected()) {
					panel.setSelectionLocked(false);
					panel.setEnabled(true);
					requirementsMet = true;
					break LOOP;
				} else {
					Modifier req = getModifierFromList(id, object
							.getAssignedModifiers());
					if (req != null) {
						panel.setSelectionLocked(false);
						panel.setEnabled(true);
						requirementsMet = true;
						break LOOP;
					}
				}
			}
			if (!requirementsMet) {
				panel.setSelectionLocked(true);
				panel.setSelected(false);
				panel.setEnabled(false);
			}
			panel.updateValues();
		}
		excludedIDs = new ArrayList<String>();
		for (int i = 0; i < modifierPanels.size(); i++) {
			ModifierPanel panel = modifierPanels.get(i);
			if (panel.isSelected()) {
				for (int j = 0; j < panel.getModifier().getExcludes().size(); j++) {
					String xmlid = panel.getModifier().getExcludes().get(j);
					excludedIDs.add(xmlid.toUpperCase().trim());
				}
			}
		}
		for (int i = 0; i < modifierPanels.size(); i++) {
			ModifierPanel panel = modifierPanels.get(i);
			if ((excludedIDs.size() > 0)
					&& excludedIDs.contains(panel.getModifier().getXMLID()
							.toUpperCase().trim())) {
				panel.setSelectionLocked(true);
				panel.setSelected(false);
			} else {
				panel.setSelectionLocked(false);
			}
			boolean requirementsMet = panel.getModifier().getRequires().size() == 0;
			LOOP: for (int j = 0; j < panel.getModifier().getRequires().size(); j++) {
				String id = panel.getModifier().getRequires().get(j)
						.toUpperCase().trim();
				String optionId = null;
				if (id.indexOf(".") > 0 && id.indexOf(".") < id.length() - 1) {
					optionId = id.substring(id.indexOf(".") + 1, id.length());
					if (optionId.trim().length() == 0)
						optionId = null;
					id = id.substring(0, id.indexOf("."));
				}
				GenericObject requirement = null;
				INNER: for (int k = 0; k < object.getAssignedModifiers().size(); k++) {
					Modifier mod = object.getAssignedModifiers().get(k);
					if (mod.getXMLID().equals(id)) {
						if (optionId == null
								|| (mod.getSelectedOption() != null && mod
										.getSelectedOption().getXMLID()
										.equalsIgnoreCase(optionId))) {
							requirement = mod;
							break INNER;
						}
					}
				}
				if (requirement == null) {
					INNER: for (int k = 0; k < object.getAssignedAdders().size(); k++) {
						Adder mod = object.getAssignedAdders().get(k);
						if (mod.getXMLID().equals(id)) {
							if (optionId == null
									|| (mod.getSelectedOption() != null && mod
											.getSelectedOption().getXMLID()
											.equalsIgnoreCase(optionId))) {
								requirement = mod;
								break INNER;
							}
						}
					}
				}
				if (requirement != null) {
					panel.setSelectionLocked(false);
					panel.setEnabled(true);
					requirementsMet = true;
					break LOOP;
				}
			}
			if (!requirementsMet) {
				panel.setSelectionLocked(true);
				panel.setSelected(false);
				panel.setEnabled(false);
			}
			panel.updateValues();
		}
		if (object instanceof SenseAdder) {
			optionsCB.removeItemListener(optionListener);
			((JTextField) optionsCB.getEditor().getEditorComponent())
					.getDocument().removeDocumentListener(optionAliasListener);
			ArrayList<Adder> options = object.getOptions();
			optionsCB.removeAllItems();
			Adder selected = object.getSelectedOption();
			for (int i = 0; i < options.size(); i++) {
				optionsCB.addItem(options.get(i));
			}
			optionsCB.setSelectedItem(selected);
			optionsCB.addItemListener(optionListener);
			((JTextField) optionsCB.getEditor().getEditorComponent())
					.getDocument().addDocumentListener(optionAliasListener);
		}
		if (object.getExamples() != null && object.getExamples().size() > 0) {
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					String text = ((JTextField) exampleCombo.getEditor()
							.getEditorComponent()).getText();
					exampleCombo.removeAllItems();
					for (String s : object.getExamples()) {
						exampleCombo.addItem(s);
					}
					((JTextField) exampleCombo.getEditor()
							.getEditorComponent()).setText(text);
				}
			});
		}
		Enumeration<JLabel> en = requiredLevelAdderLabels.keys();
		while (en.hasMoreElements()) {
			JLabel l = en.nextElement();
			Adder ad = requiredLevelAdderLabels.get(l);
			l.setText(ad.getAlias());
		}
		validate();
	}
}