package com.hero.ui;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyVetoException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;

import com.hero.HeroDesigner;
import com.hero.objects.Adder;
import com.hero.objects.GenericObject;
import com.hero.objects.martialarts.Maneuver;
import com.hero.objects.powers.FixedLocation;
import com.hero.objects.powers.FloatingLocation;
import com.hero.objects.skills.Skill;
import com.hero.ui.dialog.GenericDialog;
import com.hero.ui.dialog.ListDialog;
import com.hero.ui.widgets.PopupMessage;
import com.hero.util.Constants;

/**
 * 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 abstract class AbilityPanel extends JComponent {
	SelectionList availableList;

	SelectionList availablePrefabs;

	JTabbedPane availableTab;

	JMenuItem copyMI;

	JMenuItem createListMI;

	JMenuItem createSeparatorMI;

	JMenuItem cutMI;

	JDesktopPane desktop;

	JMenu editMenu;

	JMenu listMenu;

	public JSplitPane mainSplit;

	JMenu sortMenu;

	JMenu ascendingMenu;

	JMenu descendingMenu;

	JMenuItem ascendingDisplayMI;

	JMenuItem descendingDisplayMI;

	JMenuItem ascendingNameMI;

	JMenuItem descendingNameMI;

	JMenuItem ascendingCostMI;

	JMenuItem descendingCostMI;

	JMenuItem ascendingActiveMI;

	JMenuItem descendingActiveMI;

	JMenuBar menuBar;

	JMenuItem pasteMI;

	GenericObjectList selectionList;

	public AbilityPanel() {
		super();
		setLayout(new BorderLayout());
		initWidgets();
		initListeners();
		layoutComponent();
	}

	/**
	 * Overloaded constructor used by CompoundPowerPanel....doesn't run any
	 * initialization, leaving that to the child class.
	 * 
	 * @param doInit
	 */
	public AbilityPanel(boolean doInit) {
		super();
		setLayout(new BorderLayout());
		if (doInit) {
			initWidgets();
			initListeners();
			layoutComponent();
		}
	}

	protected abstract GenericObjectList createSelectionList();

	protected abstract String getAvailableLabel();

	protected abstract ArrayList<GenericObject> getAvailableObjects();

	protected abstract ArrayList<GenericObject> getAvailablePrefabs();

	protected abstract ArrayList<GenericObject> getObjects();

	/**
	 * Returns the purchase list.
	 * 
	 * @return
	 */
	public GenericObjectList getSelectionList() {
		return selectionList;
	}

	protected void initListeners() {
		ascendingDisplayMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Collections.sort(selectionList.getObjects(), new Comparator() {
					public int compare(Object o1, Object o2) {
						if ((o1 instanceof GenericObject)
								&& (o2 instanceof GenericObject)) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							String s1 = go1.getDisplay();
							String s2 = go2.getDisplay();
							
							int c1 = -1;
							int c2 = -1;
							
							if (o1 instanceof Skill) {
								Skill sk1 = (Skill) o1;
								if (sk1.getCharacteristic() >= 0) {
									c1 = sk1.getCharacteristic();
								}
							}
							if (o2 instanceof Skill) {
								Skill sk2 = (Skill) o2;
								if (sk2.getCharacteristic() >= 0) {
									c2 = sk2.getCharacteristic();
								}
							}
							
							if (go1.getParentList() != null) {
								String list1 = "";
								long id1 = go1.getParentList().getID();
								if (go1.getParentList().getDisplay().trim()
										.length() > 0) {
									list1 = go1.getParentList().getDisplay();
								} else {
									list1 = "List";
								}
								if (go1.getParentList().equals(go2)) {
									return "z".compareTo("a");
								} else if ((go2.getParentList() != null)
										&& go2.getParentList().equals(
												go1.getParentList()) && go2.getParentID() == go1.getParentID()) {
									if ((c1 >= 0 || c2 >= 0) && c1 != c2) {
										return c1 - c2;
									}
									else {
										return s1.compareTo(s2);
									}
								} else if (go2.getParentList() != null) {
									String list2 = "";
									long id2 = go2.getParentList().getID();
									if (go2.getParentList().getDisplay().trim()
											.length() > 0) {
										list2 = go2.getParentList()
												.getDisplay();
									} else {
										list2 = "List";
									}
									if (list1.equals(list2)) {
										return (int) (go1.getParentList().getPosition() - go2.getParentList().getPosition());
									} else {
										return list1.compareTo(list2);
									}
								} else {
									return go1.getParentList().compareTo(go2);
								}
							} else if (go2.getParentList() != null) {
								if (go2.getParentList().equals(go1)) {
									return "a".compareTo("z");
								} else {
									return go1.compareTo(go2.getParentList());
								}
							} else {
								if ((c1 >= 0 || c2 >= 0) && c1 != c2) {
									return c1 - c2;
								}
								else {
									return s1.compareTo(s2);
								}
							}
						} else {
							return o1.toString().compareTo(o2.toString());
						}
					}
				});

				for (int i = 0; i < selectionList.getObjects().size(); i++) {
					GenericObject o = selectionList.getObjects().get(i);
					o.setPosition(i);
				}

				selectionList.setData(selectionList.getObjects());
				HeroDesigner.getActiveHero().setDirty(true);
			}
		});
		ascendingNameMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Collections.sort(selectionList.getObjects(), new Comparator() {
					public int compare(Object o1, Object o2) {
						if ((o1 instanceof GenericObject)
								&& (o2 instanceof GenericObject)) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							String s1 = go1.getTextOutput();
							String s2 = go2.getTextOutput();
							if (go1.getParentList() != null) {
								s1 = go1.getParentList().getColumn2Output()
										+ s1;
							}
							if (go2.getParentList() != null) {
								s2 = go2.getParentList().getColumn2Output()
										+ s2;
							}
							return s1.compareTo(s2);
						} else {
							return o1.toString().compareTo(o2.toString());
						}
					}
				});
				for (int i = 0; i < selectionList.getObjects().size(); i++) {
					GenericObject o = selectionList.getObjects().get(i);
					o.setPosition(i);
				}
				selectionList.setData(selectionList.getObjects());
				HeroDesigner.getActiveHero().setDirty(true);
			}
		});
		ascendingCostMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Collections.sort(selectionList.getObjects(), new Comparator() {
					public int compare(Object o1, Object o2) {
						if ((o1 instanceof GenericObject)
								&& (o2 instanceof GenericObject)) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							double s1 = go1.getRealCost();
							double s2 = go2.getRealCost();
							if (go1.getParentList() != null) {
								if (go2.getParentList() != null) {
									if (go2.getParentList().equals(
											go1.getParentList())) {
										return (int) s1 - (int) s2;
									} else {
										s1 = go1.getParentList().getRealCost();
										s2 = go2.getParentList().getRealCost();
										return (int) s1 - (int) s2;
									}
								} else {
									if (go1.getParentList().equals(go2)) {
										return 1;
									}
									return (int) go1.getParentList()
											.getRealCost()
											- (int) s2;
								}
							} else if (go2.getParentList() != null) {
								if (go2.getParentList().equals(go1)) {
									return -1;
								} else {
									return (int) s1
											- (int) go2.getParentList()
													.getRealCost();
								}
							} else {
								return (int) s1 - (int) s2;
							}
						} else {
							return o1.toString().compareTo(o2.toString());
						}
					}
				});
				for (int i = 0; i < selectionList.getObjects().size(); i++) {
					GenericObject o = selectionList.getObjects().get(i);
					o.setPosition(i);
				}
				selectionList.setData(selectionList.getObjects());
				HeroDesigner.getActiveHero().setDirty(true);
			}
		});
		ascendingActiveMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Collections.sort(selectionList.getObjects(), new Comparator() {
					public int compare(Object o1, Object o2) {
						if ((o1 instanceof GenericObject)
								&& (o2 instanceof GenericObject)) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							double s1 = go1.getActiveCost();
							double s2 = go2.getActiveCost();
							if (go1.getParentList() != null) {
								if (go2.getParentList() != null) {
									if (go2.getParentList().equals(
											go1.getParentList())) {
										return (int) s1 - (int) s2;
									} else {
										s1 = go1.getParentList()
												.getActiveCost();
										s2 = go2.getParentList()
												.getActiveCost();
										return (int) s1 - (int) s2;
									}
								} else {
									if (go1.getParentList().equals(go2)) {
										return 1;
									}
									return (int) go1.getParentList()
											.getActiveCost()
											- (int) s2;
								}
							} else if (go2.getParentList() != null) {
								if (go2.getParentList().equals(go1)) {
									return -1;
								} else {
									return (int) s1
											- (int) go2.getParentList()
													.getActiveCost();
								}
							} else {
								return (int) s1 - (int) s2;
							}
						} else {
							return o1.toString().compareTo(o2.toString());
						}
					}
				});
				for (int i = 0; i < selectionList.getObjects().size(); i++) {
					GenericObject o = selectionList.getObjects().get(i);
					o.setPosition(i);
				}
				selectionList.setData(selectionList.getObjects());
				HeroDesigner.getActiveHero().setDirty(true);
			}
		});
		descendingDisplayMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Collections.sort(selectionList.getObjects(), new Comparator() {
					public int compare(Object o1, Object o2) {
						if ((o1 instanceof GenericObject)
								&& (o2 instanceof GenericObject)) {
							GenericObject go1 = (GenericObject) o2;
							GenericObject go2 = (GenericObject) o1;
							String s1 = go1.getDisplay();
							String s2 = go2.getDisplay();
							
							int c1 = -1;
							int c2 = -1;
							
							if (o2 instanceof Skill) {
								Skill sk2 = (Skill) o2;
								if (sk2.getCharacteristic() >= 0) {
									c1 = sk2.getCharacteristic();
								}
							}
							if (o1 instanceof Skill) {
								Skill sk1 = (Skill) o1;
								if (sk1.getCharacteristic() >= 0) {
									c2 = sk1.getCharacteristic();
								}
							}
							
							if (go2.getParentList() != null) {
								String list1 = "";
								long id1 = go2.getParentList().getID();
								if (go2.getParentList().getDisplay().trim()
										.length() > 0) {
									list1 = go2.getParentList().getDisplay();
								} else {
									list1 = "List";
								}
								if (go2.getParentList().equals(go1)) {
									return "z".compareTo("a");
								} else if ((go1.getParentList() != null)
										&& go1.getParentList().equals(
												go2.getParentList())) {
									if ((c1 >= 0 || c2 >= 0) && c1 != c2) {
										return c1 - c2;
									}
									else {
										return s1.compareTo(s2);
									}
								} else if (go1.getParentList() != null) {
									String list2 = "";
									long id2 = go1.getParentList().getID();
									if (go1.getParentList().getDisplay().trim()
											.length() > 0) {
										list2 = go1.getParentList()
												.getDisplay();
									} else {
										list2 = "List";
									}
									if (list1.equals(list2)) {
										return (int) (go2.getParentList().getPosition() - go1.getParentList().getPosition());
									} else {
										return list1.compareTo(list2);
									}
								} else {
									return compare(go2.getParentList(), go1);
								}
							} else if (go1.getParentList() != null) {
								if (go1.getParentList().equals(go1)) {
									return "a".compareTo("z");
								} else {
									return compare(go2, go1.getParentList());
								}
							} else {
								if ((c1 >= 0 || c2 >= 0) && c1 != c2) {
									return c1 - c2;
								}
								else {
									return s1.compareTo(s2);
								}
							}
						} else {
							return o1.toString().compareTo(o2.toString());
						}
					}
				});
				for (int i = 0; i < selectionList.getObjects().size(); i++) {
					GenericObject o = selectionList.getObjects().get(i);
					o.setPosition(i);
				}
				selectionList.setData(selectionList.getObjects());
				HeroDesigner.getActiveHero().setDirty(true);
			}
		});
		descendingNameMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Collections.sort(selectionList.getObjects(), new Comparator() {
					public int compare(Object o1, Object o2) {
						if ((o1 instanceof GenericObject)
								&& (o2 instanceof GenericObject)) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							String s1 = go1.getTextOutput();
							String s2 = go2.getTextOutput();
							if (go1.getParentList() != null) {
								s1 = go1.getParentList().getColumn2Output()
										+ "a" + s1;
								if (go1.getParentList().equals(go2)) {
									return 1;
								}
							}
							if (go2.getParentList() != null) {
								s2 = go2.getParentList().getColumn2Output()
										+ "a" + s2;
								if (go2.getParentList().equals(go1)) {
									return -1;
								}
							}
							if (go1 instanceof com.hero.objects.List) {
								s1 = s1 + "Z";
							}
							if (go2 instanceof com.hero.objects.List) {
								s2 = s2 + "Z";
							}

							return s2.compareTo(s1);
						} else {
							return o2.toString().compareTo(o1.toString());
						}
					}
				});
				for (int i = 0; i < selectionList.getObjects().size(); i++) {
					GenericObject o = selectionList.getObjects().get(i);
					o.setPosition(i);
				}
				selectionList.setData(selectionList.getObjects());
				HeroDesigner.getActiveHero().setDirty(true);
			}
		});
		descendingCostMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Collections.sort(selectionList.getObjects(), new Comparator() {
					public int compare(Object o1, Object o2) {
						if ((o1 instanceof GenericObject)
								&& (o2 instanceof GenericObject)) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							double s1 = go1.getRealCost();
							double s2 = go2.getRealCost();
							if (go1.getParentList() != null) {
								if (go2.getParentList() != null) {
									if (go2.getParentList().equals(
											go1.getParentList())) {
										return (int) s2 - (int) s1;
									} else {
										s1 = go1.getParentList().getRealCost();
										s2 = go2.getParentList().getRealCost();
										return (int) s2 - (int) s1;
									}
								} else {
									if (go1.getParentList().equals(go2)) {
										return 1;
									}
									return (int) s2
											- (int) go1.getParentList()
													.getRealCost();
								}
							} else if (go2.getParentList() != null) {
								if (go2.getParentList().equals(go1)) {
									return -1;
								} else {
									return (int) go2.getParentList()
											.getRealCost()
											- (int) s1;
								}
							} else {
								return (int) s2 - (int) s1;
							}
						} else {
							return o2.toString().compareTo(o1.toString());
						}
					}
				});
				for (int i = 0; i < selectionList.getObjects().size(); i++) {
					GenericObject o = selectionList.getObjects().get(i);
					o.setPosition(i);
				}
				selectionList.setData(selectionList.getObjects());
				HeroDesigner.getActiveHero().setDirty(true);
			}
		});
		descendingActiveMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Collections.sort(selectionList.getObjects(), new Comparator() {
					public int compare(Object o1, Object o2) {
						if ((o1 instanceof GenericObject)
								&& (o2 instanceof GenericObject)) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							double s1 = go1.getActiveCost();
							double s2 = go2.getActiveCost();
							if (go1.getParentList() != null) {
								if (go2.getParentList() != null) {
									if (go2.getParentList().equals(
											go1.getParentList())) {
										return (int) s2 - (int) s1;
									} else {
										s1 = go1.getParentList()
												.getActiveCost();
										s2 = go2.getParentList()
												.getActiveCost();
										return (int) s2 - (int) s1;
									}
								} else {
									if (go1.getParentList().equals(go2)) {
										return 1;
									}
									return (int) s2
											- (int) go1.getParentList()
													.getActiveCost();
								}
							} else if (go2.getParentList() != null) {
								if (go2.getParentList().equals(go1)) {
									return -1;
								} else {
									return (int) go2.getParentList()
											.getActiveCost()
											- (int) s1;
								}
							} else {
								return (int) s2 - (int) s1;
							}
						} else {
							return o2.toString().compareTo(o1.toString());
						}
					}
				});
				for (int i = 0; i < selectionList.getObjects().size(); i++) {
					GenericObject o = selectionList.getObjects().get(i);
					o.setPosition(i);
				}
				selectionList.setData(selectionList.getObjects());
				HeroDesigner.getActiveHero().setDirty(true);
			}
		});
		selectionList.setPasteAction(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				GenericObject obj = HeroDesigner.getCopyBuffer();
				if (obj instanceof Adder) {
					return;
				}
				obj = obj.clone();
				obj.resetID();
				obj.setParent(null);
				obj.setMainPower(null);
				int insertionIndex = selectionList.getInsertionIndex();
				if (selectionList.insertBefore && (insertionIndex > 0)) {
					insertionIndex -= 1;
				}

				ArrayList<GenericObject> holder = new ArrayList<GenericObject>();
				if (obj instanceof com.hero.objects.List) {
					com.hero.objects.List list = (com.hero.objects.List) obj;
					holder = list.getObjects();
					list.setObjects(new ArrayList<GenericObject>());
				}
				obj.setIsEquipment(isEquipment());
				obj.setPower(isPower());
				for (GenericObject o : holder) {
					o = o.clone();
					o.resetID();
					o.setParent((com.hero.objects.List) obj);
					o.setIsEquipment(isEquipment());
					o.setPower(isPower());
					com.hero.objects.List list = (com.hero.objects.List) obj;
					list.addObject(o);
				}
				selectionList.addObject(obj);
				selectionList.checkIndices();
				HeroDesigner.getActiveHero().setDirty(true);
			}
		});
		pasteMI.addActionListener(selectionList.getPasteAction());
		copyMI.addActionListener(selectionList.getCopyAction());
		cutMI.addActionListener(selectionList.getCutAction());
		editMenu.addMenuListener(new MenuListener() {
			public void menuCanceled(MenuEvent e) {

			}

			public void menuDeselected(MenuEvent e) {

			}

			public void menuSelected(MenuEvent e) {
				pasteMI.setEnabled(selectionList.checkPaste());
			}
		});
		selectionList
				.addSelectionListener(selectionList.new SelectionListener() {
					@Override
					public void selectionChanged(GenericObject selection) {
						if ((selection != null)
								&& !(selection instanceof Adder)) {
							copyMI.setEnabled(true);
							cutMI.setEnabled(true);
						} else {
							copyMI.setEnabled(false);
							cutMI.setEnabled(false);
						}
					}
				});
		createListMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				com.hero.objects.List list = new com.hero.objects.List("");
				list.setIsEquipment(isEquipment());
				list.setPower(isPower());
				ListDialog dialog = new ListDialog(list, true);
				dialog.setLocationRelativeTo(AbilityPanel.this);
				dialog.setVisible(true);
				if (dialog.okButtonClicked) {
					selectionList.addObject(list);
				}
			}
		});
		createSeparatorMI.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				com.hero.objects.List list = new com.hero.objects.List("");
				list.setDisplay(" ");
				list.setAlias(" ");
				selectionList.addObject(list);
			}
		});

		availableList.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if ((e.getClickCount() >= 2)
						|| (e.getButton() != MouseEvent.BUTTON1)) {
					if (e.getButton() != MouseEvent.BUTTON1) {
						availableList.listTree
								.setSelectionRow(availableList.listTree
										.getRowForLocation((int) e.getPoint()
												.getX(), (int) e.getPoint()
												.getY()));
					}
					if (availableList.getSelection() != null) {
						if (!(availableList.getSelection() instanceof com.hero.objects.List)) {
							if ((availableList.getSelection() instanceof FloatingLocation)
									|| (availableList.getSelection() instanceof FixedLocation)) {
								if (GenericObject.findObjectByID(selectionList
										.getObjects(), "TELEPORTATION") == null) {
									PopupMessage popup = PopupMessage
											.getInstance(
													HeroDesigner.getAppFrame(),
													availableList,
													"You must purchase Teleportation before you can purchase this Power",
													false);
									popup.setVisible(true);
									return;
								}
							}
							GenericObject o = availableList.getSelection()
									.clone();
							o.setDisplayActiveCost(HeroDesigner.getInstance()
									.getPrefs().displayActivePoints());
							if (o.isExclusive()
									&& HeroDesigner.getInstance().getPrefs()
											.warnOnMultiple()) {
								ArrayList<GenericObject> purchased = selectionList
										.getObjects();
								LOOP: for (GenericObject go : purchased) {
									if (go.getXMLID().equals(o.getXMLID())) {
										if (go instanceof Maneuver) {
											if (!go.getDisplay().equals(
													o.getDisplay())) {
												continue LOOP;
											}
										} else if (o instanceof Maneuver) {
											continue LOOP;
										}
										int response = JOptionPane
												.showConfirmDialog(
														availableList,
														o
																+ " has already been assigned to this character.\nAre you sure you want to purchase it again?",
														"Duplicate Purchase",
														JOptionPane.YES_NO_OPTION,
														JOptionPane.WARNING_MESSAGE);
										if (response != JOptionPane.YES_OPTION) {
											return;
										} else {
											break;
										}
									}
								}
							}
							o.setID(System.currentTimeMillis()
									+ (int) Math.random() * 1000);
							o.setParent(selectionList.getInsertionParent());
							o.setIsEquipment(isEquipment());
							o.setPower(isPower());
							GenericDialog dialog = o.getDialog(true, isPower());
							if (dialog == null) {
								JOptionPane
										.showMessageDialog(
												AbilityPanel.this,
												o
														+ " has not had an assignment dialog implemented yet.",
												"Under Construction",
												JOptionPane.ERROR_MESSAGE);
								return;
							}
							if (o.showBuildDialog()
									&& (e.getButton() == MouseEvent.BUTTON1)) {
								dialog.setLocationRelativeTo(HeroDesigner
										.getAppFrame());
								dialog.setVisible(true);
								if (dialog.okButtonClicked) {
									selectionList.addObject(o);
									HeroDesigner.getActiveHero().setDirty(true);
								}
								dialog.dispose();
							} else {
								dialog.dispose();
								selectionList.addObject(o);
								HeroDesigner.getActiveHero().setDirty(true);
							}
						}
					}
				}
			}
		});
		availableList.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (availableList.getSelection() != null) {
					if (!(availableList.getSelection() instanceof com.hero.objects.List)) {
						if ((availableList.getSelection() instanceof FloatingLocation)
								|| (availableList.getSelection() instanceof FixedLocation)) {
							if (GenericObject.findObjectByID(selectionList
									.getObjects(), "TELEPORTATION") == null) {
								PopupMessage popup = PopupMessage
										.getInstance(
												HeroDesigner.getAppFrame(),
												availableList,
												"You must purchase Teleportation before you can purchase this Power",
												false);
								popup.setVisible(true);
								return;
							}
						}
						GenericObject obj = availableList.getSelection()
								.clone();
						obj.setDisplayActiveCost(HeroDesigner.getInstance()
								.getPrefs().displayActivePoints());
						if (obj.isExclusive()
								&& HeroDesigner.getInstance().getPrefs()
										.warnOnMultiple()) {
							ArrayList<GenericObject> purchased = selectionList
									.getObjects();
							LOOP: for (GenericObject o : purchased) {
								if (o.getXMLID().equals(obj.getXMLID())) {
									if (o instanceof Maneuver) {
										if (!o.getDisplay().equals(
												obj.getDisplay())) {
											continue LOOP;
										}
									} else if (obj instanceof Maneuver) {
										continue LOOP;
									}
									int response = JOptionPane
											.showConfirmDialog(
													availableList,
													obj
															+ " has already been assigned to this character.\nAre you sure you want to purchase it again?",
													"Duplicate Purchase",
													JOptionPane.YES_NO_OPTION,
													JOptionPane.WARNING_MESSAGE);
									if (response != JOptionPane.YES_OPTION) {
										return;
									} else {
										break;
									}
								}
							}
						}
						obj.setID(System.currentTimeMillis()
								+ (int) Math.random() * 1000);
						obj.setIsEquipment(isEquipment());
						obj.setPower(isPower());
						obj.setParent(selectionList.getInsertionParent());
						GenericDialog dialog = obj.getDialog(true, isPower());
						if (obj.showBuildDialog()) {
							dialog.setLocationRelativeTo(HeroDesigner
									.getAppFrame());
							dialog.setVisible(true);
							if (dialog.okButtonClicked) {
								selectionList.addObject(obj);
								HeroDesigner.getActiveHero().setDirty(true);
							}
							dialog.dispose();
						} else {
							dialog.dispose();
							selectionList.addObject(obj);
							HeroDesigner.getActiveHero().setDirty(true);
						}
					}
				}
			}
		});
		availablePrefabs.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (availablePrefabs.getSelection() != null) {
					if ((availablePrefabs.getSelection() instanceof FloatingLocation)
							|| (availablePrefabs.getSelection() instanceof FixedLocation)) {
						if (GenericObject.findObjectByID(selectionList
								.getObjects(), "TELEPORTATION") == null) {
							PopupMessage popup = PopupMessage
									.getInstance(
											HeroDesigner.getAppFrame(),
											availablePrefabs,
											"You must purchase Teleportation before you can purchase this Power",
											false);
							popup.setVisible(true);
							return;
						}
					}
					GenericObject o = availablePrefabs.getSelection();
					GenericObject obj = o.clone();
					obj.setDisplayActiveCost(HeroDesigner.getInstance()
							.getPrefs().displayActivePoints());
					obj.setParent(null);
					if (obj instanceof com.hero.objects.List) {
						com.hero.objects.List list = (com.hero.objects.List) obj;
						ArrayList<GenericObject> vec = (ArrayList<GenericObject>) list
								.getObjects().clone();
						list.setObjects(new ArrayList<GenericObject>());
						for (GenericObject child : vec) {
							if (child instanceof com.hero.objects.List) {
								continue;
							}
							child = child.clone();
							child.setParent(list);
							child.setIsEquipment(isEquipment());
							child.setPower(isPower());
							list.getObjects().add(child);
						}
					}
					obj.setIsEquipment(isEquipment());
					obj.setPower(isPower());
					selectionList.addObject(obj);
					selectionList.setSelectedIndex(selectionList.getObjects()
							.indexOf(obj), false);
					HeroDesigner.getActiveHero().setDirty(true);
				}
			}
		});
		availablePrefabs.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2) {
					if (availablePrefabs.getSelection() != null) {
						if (availablePrefabs.getSelection() instanceof com.hero.objects.List) {
							return;
						}
						if ((availablePrefabs.getSelection() instanceof FloatingLocation)
								|| (availablePrefabs.getSelection() instanceof FixedLocation)) {
							if (GenericObject.findObjectByID(selectionList
									.getObjects(), "TELEPORTATION") == null) {
								PopupMessage popup = PopupMessage
										.getInstance(
												HeroDesigner.getAppFrame(),
												availablePrefabs,
												"You must purchase Teleportation before you can purchase this Power",
												false);
								popup.setVisible(true);
								return;
							}
						}
						GenericObject o = availablePrefabs.getSelection();
						GenericObject obj = o.clone();
						obj.setIsEquipment(isEquipment());
						obj.setPower(isPower());
						obj.setDisplayActiveCost(HeroDesigner.getInstance()
								.getPrefs().displayActivePoints());
						obj.setParent(null);
						if (obj instanceof com.hero.objects.List) {
							com.hero.objects.List list = (com.hero.objects.List) obj;
							ArrayList<GenericObject> vec = (ArrayList<GenericObject>) list
									.getObjects().clone();
							list.setObjects(new ArrayList<GenericObject>());
							for (GenericObject child : vec) {
								if (child instanceof com.hero.objects.List) {
									continue;
								}
								child = child.clone();
								child.setParent(list);
								child.setIsEquipment(isEquipment());
								child.setPower(isPower());
								list.getObjects().add(child);
							}
						}
						selectionList.addObject(obj);
						selectionList.setSelectedIndex(selectionList
								.getObjects().indexOf(obj), false);
						HeroDesigner.getActiveHero().setDirty(true);
					}
				}
			}
		});
		availablePrefabs.setTreeListener(new TreeSelectionListener() {
			public void valueChanged(TreeSelectionEvent e) {
				if (availablePrefabs.getSelection() != null) {
					if (!(availablePrefabs.getSelection() instanceof com.hero.objects.List)) {
						GenericObject obj = availablePrefabs.getSelection();
						if ((obj.getNotes() != null)
								&& (obj.getNotes().trim().length() > 0)) {
							availablePrefabs.defineBtn.setEnabled(true);
						} else {
							availablePrefabs.defineBtn.setEnabled(false);
						}
						availablePrefabs.selectBtn.setEnabled(true);
					} else {
						GenericObject obj = availablePrefabs.getSelection();
						if ((obj.getNotes() != null)
								&& (obj.getNotes().trim().length() > 0)) {
							availablePrefabs.defineBtn.setEnabled(true);
						} else {
							availablePrefabs.defineBtn.setEnabled(false);
						}
						com.hero.objects.List list = (com.hero.objects.List) obj;
						for (int i = 0; i < list.getObjects().size(); i++) {
							if (list.getObjects().get(i) instanceof com.hero.objects.List) {
								availablePrefabs.selectBtn.setEnabled(false);
								return;
							}
						}
						availablePrefabs.selectBtn.setEnabled(true);
					}
				} else {
					availablePrefabs.defineBtn.setEnabled(false);
					availablePrefabs.selectBtn.setEnabled(false);
				}
			}
		});
	}

	protected void initWidgets() {
		selectionList = createSelectionList();
		availableList = new SelectionList(getAvailableObjects());
		availablePrefabs = new SelectionList(getAvailablePrefabs(), true);
		availableTab = new JTabbedPane();
		availableTab.addTab(getAvailableLabel(), availableList);
		availableTab.addTab("Prefabs", availablePrefabs);
		desktop = new JDesktopPane();
		desktop.add(selectionList);
		try {
			desktop.selectFrame(true);
		} catch (Throwable e) {
		}
		try {
			selectionList.setMaximum(true);
			selectionList.setSelected(true);
		} catch (PropertyVetoException ex) {
			ex.printStackTrace();
		}
		mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, desktop,
				availableTab);
		mainSplit.setDividerLocation(Constants.COL_1_WIDTH
				+ Constants.COL_2_WIDTH + Constants.COL_3_WIDTH + 60);
		menuBar = new JMenuBar();

		editMenu = new JMenu("Edit");
		cutMI = new JMenuItem("Cut");
		cutMI.setEnabled(false);
		editMenu.add(cutMI);
		copyMI = new JMenuItem("Copy");
		copyMI.setEnabled(false);
		editMenu.add(copyMI);
		pasteMI = new JMenuItem("Paste");
		pasteMI.setEnabled(false);
		editMenu.add(pasteMI);
		sortMenu = new JMenu("Sorting");
		ascendingMenu = new JMenu("Ascending");
		descendingMenu = new JMenu("Descending");
		sortMenu.add(ascendingMenu);
		sortMenu.add(descendingMenu);
		ascendingDisplayMI = new JMenuItem("by Type");
		ascendingMenu.add(ascendingDisplayMI);
		ascendingNameMI = new JMenuItem("by Display");
		ascendingMenu.add(ascendingNameMI);
		descendingDisplayMI = new JMenuItem("by Type");
		descendingMenu.add(descendingDisplayMI);
		descendingNameMI = new JMenuItem("by Display");
		descendingMenu.add(descendingNameMI);
		ascendingCostMI = new JMenuItem("by Real Cost");
		ascendingMenu.add(ascendingCostMI);
		ascendingActiveMI = new JMenuItem("by Active Cost");
		ascendingMenu.add(ascendingActiveMI);
		descendingCostMI = new JMenuItem("by Real Cost");
		descendingMenu.add(descendingCostMI);
		descendingActiveMI = new JMenuItem("by Active Cost");
		descendingMenu.add(descendingActiveMI);
		listMenu = new JMenu("List");
		createListMI = new JMenuItem("New List...");
		createSeparatorMI = new JMenuItem("New Separator");
		listMenu.add(createListMI);
		listMenu.add(createSeparatorMI);
		menuBar.add(editMenu);
		menuBar.add(listMenu);
		menuBar.add(sortMenu);
		selectionList.setJMenuBar(menuBar);

	}

	protected abstract boolean isEquipment();

	protected abstract boolean isPower();

	protected void layoutComponent() {
		add(mainSplit, BorderLayout.CENTER);
	}

	/**
	 * Resets the panel's contents (available list, purchase list, etc).
	 */
	public abstract void reset();

	protected void updatePrefabs() {
		availablePrefabs.setAvailableList(getAvailablePrefabs());
		if (getAvailablePrefabs().size() == 0) {
			availableTab.setEnabledAt(1, false);
		} else {
			availableTab.setEnabledAt(1, true);
		}
	}

	/**
	 * Updates the title bar display of the point totals (in the selection
	 * list).
	 */
	public void updateTotal() {
		selectionList.updateTotal();
		pasteMI.setEnabled(selectionList.checkPaste());
		try {
			selectionList.setSelected(true);
		} catch (PropertyVetoException e) {
			e.printStackTrace();
		}
	}
}