package com.hero.objects.powers;

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

import org.jdom.Element;

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

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

public class SenseGroup extends Power {

	private static ArrayList<SenseGroup> allGroups = new ArrayList<SenseGroup>();

	private static long ownedGroupsLastCall;

	private static ArrayList<SenseGroup> ownedGroupsSaver;

	/**
	 * Adds a SenseGroup into the list of available groups.
	 * 
	 * @param group
	 */
	public static void addGroup(SenseGroup group) {
		SenseGroup.allGroups.add(group);
	}

	/**
	 * Clears all groups from memory. This is an extremely dangerous method to
	 * call and should only be called during a template load.
	 */
	public static void clear() {
		SenseGroup.allGroups.clear();
		SenseGroup unusual = new SenseGroup(new Element("SENSEGROUP"), false);
		unusual.display = "Unusual Group";
		unusual.alias = "Unusual Group";
		unusual.xmlID = "UNUSUALGROUP";
		SenseGroup.allGroups.add(unusual);
		SenseGroup nogroup = new SenseGroup(new Element("SENSEGROUP"), false);
		nogroup.display = "no Sense Group";
		nogroup.alias = "no Sense Group";
		nogroup.xmlID = "NOGROUP";
		SenseGroup.allGroups.add(nogroup);
	}

	/**
	 * Returns a Vector of SenseGroup objects defining all available Sense
	 * Groups.
	 * 
	 * @return
	 */
	public static ArrayList<SenseGroup> getAllGroups() {
		return SenseGroup.allGroups;
	}

	/**
	 * Utility method to fetch a SenseGroup by XMLID.
	 * 
	 * @param id
	 * @return
	 */
	public static SenseGroup getGroupByID(String id) {
		ArrayList<SenseGroup> all = SenseGroup.getAllGroups();
		for (SenseGroup gr : all) {
			if (gr.getXMLID().equals(id)) {
				return gr;
			}
		}
		return null;
	}

	/**
	 * Return a Vector of SenseGroup objects representing all Sense Groups that
	 * the character possesses (either built-in or purchased)
	 * 
	 * @return
	 */
	public static ArrayList<SenseGroup> getOwnedGroups() {
		long current = System.currentTimeMillis();
		if ((current - SenseGroup.ownedGroupsLastCall < 1000)
				&& (SenseGroup.ownedGroupsSaver != null)) {
			return SenseGroup.ownedGroupsSaver;
		}
		SenseGroup.ownedGroupsLastCall = current;
		ArrayList<SenseGroup> ret = new ArrayList<SenseGroup>();
		if (SenseGroup.allGroups == null) {
			SenseGroup.allGroups = new ArrayList<SenseGroup>();
		}
		ArrayList<Sense> allSenses = Sense.getOwnedSenses();
		OUTER: for (SenseGroup group : SenseGroup.allGroups) {
			if (group.getXMLID().equals("UNUSUALGROUP")
					|| group.getXMLID().equals("NOGROUP")) {
				continue OUTER;
			}
			if (GenericObject.findObjectByID(ret, group.getXMLID()) != null) {
				continue OUTER;
			}
			INNER: for (Sense sense : allSenses) {
				if (sense.getGroup() == null) {
					continue INNER;
				}
				if (sense.getGroup().getXMLID().equals(group.getXMLID())) {
					ret.add(group);
					continue OUTER;
				}
			}
			if (group.getXMLID().equals("MENTALGROUP")) {
				// check through Powers and Equipment for Mental Powers
				if (HeroDesigner.getActiveHero() == null) {
					continue OUTER;
				}
				for (GenericObject o : HeroDesigner.getActiveHero().getPowers()) {
					if (o.getTypes().contains("MENTAL")) {
						ret.add(group);
						continue OUTER;
					} else if (o instanceof CompoundPower) {
						CompoundPower cp = (CompoundPower) o;
						for (GenericObject o2 : cp.getPowers()) {
							if (o2.getTypes().contains("MENTAL")) {
								ret.add(group);
								continue OUTER;
							}
						}
					}
				}
				for (GenericObject o : HeroDesigner.getActiveHero()
						.getEquipment()) {
					if (o.getTypes().contains("MENTAL")) {
						ret.add(group);
						continue OUTER;
					} else if (o instanceof CompoundPower) {
						CompoundPower cp = (CompoundPower) o;
						for (GenericObject o2 : cp.getPowers()) {
							if (o2.getTypes().contains("MENTAL")) {
								ret.add(group);
								continue OUTER;
							}
						}
					}
				}
			}
		}
		Collections.sort(ret);
		SenseGroup.ownedGroupsSaver = ret;
		return ret;
	}

	private ArrayList<String> senseAdders;

	private long senseAddersLastCall;

	private ArrayList<String> senseAddersSaver;

	public SenseGroup(Element root) {
		super(root);
		if (SenseGroup.allGroups.size() == 0) {
			SenseGroup.clear();
		}
		if ((xmlID != null) && (xmlID.trim().length() > 0)
				&& includedInTemplate()) {
			while (SenseGroup.allGroups.contains(this)) {
				SenseGroup.allGroups.remove(this);
			}
			SenseGroup.allGroups.add(this);
		}
	}

	protected SenseGroup(Element root, boolean initGroups) {
		super(root);
		if (initGroups) {
			if (SenseGroup.allGroups.size() == 0) {
				SenseGroup.clear();
			}
			if ((xmlID != null) && (xmlID.trim().length() > 0)
					&& includedInTemplate()) {
				while (SenseGroup.allGroups.contains(this)) {
					SenseGroup.allGroups.remove(this);
				}
				SenseGroup.allGroups.add(this);
			}
		}
	}

	@Override
	public boolean equals(Object o) {
		if (o instanceof SenseGroup) {
			SenseGroup g = (SenseGroup) o;
			if (g.getXMLID().equals(getXMLID())) {
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	/**
	 * Returns a Vector of Strings defining the XMLIDs of the default/built-in
	 * Sense Modifiers for this Sense Group.
	 * 
	 * @return
	 */
	public ArrayList<String> getDefaultSenseAdders() {
		if (senseAdders == null) {
			senseAdders = new ArrayList<String>();
		}
		return senseAdders;
	}

	/**
	 * Returns a Vector of Strings defining the XMLIDs of the currently
	 * purchased (including built-in) Sense Modifiers for this Sense Group.
	 * 
	 * @return
	 */
	public ArrayList<String> getSenseAdders() {
		long current = System.currentTimeMillis();
		if ((current - senseAddersLastCall < 1000)
				&& (senseAddersSaver != null)) {
			return senseAddersSaver;
		}
		senseAddersLastCall = current;
		if (senseAdders == null) {
			senseAdders = new ArrayList<String>();
		}
		ArrayList<String> ret = (ArrayList<String>) senseAdders.clone();
		if (HeroDesigner.getActiveHero() == null) {
			return ret;
		}
		for (GenericObject o : HeroDesigner.getActiveHero().getPowers()) {
			if (o instanceof SenseAdder) {
				SenseAdder ad = (SenseAdder) o;
				if (ad.getSenseGroups().contains(getXMLID())
						&& !ret.contains(ad.getXMLID())) {
					ret.add(ad.getXMLID());
				}
			} else if (o instanceof CompoundPower) {
				CompoundPower cp = (CompoundPower) o;
				for (GenericObject o2 : cp.getPowers()) {
					if (o2 instanceof SenseAdder) {
						SenseAdder ad = (SenseAdder) o2;
						if (ad.getSenseGroups().contains(getXMLID())
								&& !ret.contains(ad.getXMLID())) {
							ret.add(ad.getXMLID());
						}
					}
				}
			}
		}
		for (GenericObject o : HeroDesigner.getActiveHero().getEquipment()) {
			if (o instanceof SenseAdder) {
				SenseAdder ad = (SenseAdder) o;
				if (ad.getSenseGroups().contains(getXMLID())
						&& !ret.contains(ad.getXMLID())) {
					ret.add(ad.getXMLID());
				}
			} else if (o instanceof CompoundPower) {
				CompoundPower cp = (CompoundPower) o;
				for (GenericObject o2 : cp.getPowers()) {
					if (o2 instanceof SenseAdder) {
						SenseAdder ad = (SenseAdder) o2;
						if (ad.getSenseGroups().contains(getXMLID())
								&& !ret.contains(ad.getXMLID())) {
							ret.add(ad.getXMLID());
						}
					}
				}
			}
		}
		senseAddersSaver = ret;
		return ret;
	}

	/**
	 * Returns a Vector of Strings defining the XMLIDs of the Sense Modifiers
	 * possessed by this Sense Group. If the Sense Modifier is gained through an
	 * ability with the specified ID, it is ignored.
	 * 
	 * @param excludeID
	 * @return
	 */
	public ArrayList<String> getSenseAdders(long excludeID) {
		if (senseAdders == null) {
			senseAdders = new ArrayList<String>();
		}
		ArrayList<String> ret = (ArrayList<String>) senseAdders.clone();
		if (HeroDesigner.getActiveHero() == null) {
			return ret;
		}
		for (GenericObject o : HeroDesigner.getActiveHero().getPowers()) {
			if (o instanceof SenseAdder) {
				SenseAdder ad = (SenseAdder) o;
				if (ad.getID() == excludeID) {
					continue;
				}
				if (ad.getSenseGroups().contains(getXMLID())
						&& !ret.contains(ad.getXMLID())) {
					ret.add(ad.getXMLID());
				}
			} else if (o instanceof CompoundPower) {
				CompoundPower cp = (CompoundPower) o;
				for (GenericObject o2 : cp.getPowers()) {
					if (o2 instanceof SenseAdder) {
						SenseAdder ad = (SenseAdder) o2;
						if (ad.getID() == excludeID) {
							continue;
						}
						if (ad.getSenseGroups().contains(getXMLID())
								&& !ret.contains(ad.getXMLID())) {
							ret.add(ad.getXMLID());
						}
					}
				}
			}
		}
		for (GenericObject o : HeroDesigner.getActiveHero().getEquipment()) {
			if (o instanceof SenseAdder) {
				SenseAdder ad = (SenseAdder) o;
				if (ad.getID() == excludeID) {
					continue;
				}
				if (ad.getSenseGroups().contains(getXMLID())
						&& !ret.contains(ad.getXMLID())) {
					ret.add(ad.getXMLID());
				}
			} else if (o instanceof CompoundPower) {
				CompoundPower cp = (CompoundPower) o;
				for (GenericObject o2 : cp.getPowers()) {
					if (o2 instanceof SenseAdder) {
						SenseAdder ad = (SenseAdder) o2;
						if (ad.getID() == excludeID) {
							continue;
						}
						if (ad.getSenseGroups().contains(getXMLID())
								&& !ret.contains(ad.getXMLID())) {
							ret.add(ad.getXMLID());
						}
					}
				}
			}
		}
		return ret;
	}

	@Override
	public int hashCode() {
		return getXMLID().hashCode();
	}

	@Override
	protected void init(Element element) {
		super.init(element);
		String id = XMLUtility.getValue(element, "XMLID");
		if (id != null) {
			xmlID = id;
		}
		Iterator iter = element.getChildren("PROVIDES").iterator();
		while (iter.hasNext()) {
			Element next = (Element) iter.next();
			String val = next.getTextTrim().toUpperCase();
			if (senseAdders == null) {
				senseAdders = new ArrayList<String>();
			}
			if (!senseAdders.contains(val)) {
				senseAdders.add(val);
			}
		}
	}

}