package com.hero.objects.powers;

import org.jdom.Element;

import com.hero.HeroDesigner;
import com.hero.objects.Adder;
import com.hero.objects.CharAffectingObject;
import com.hero.objects.GenericObject;
import com.hero.ui.dialog.GenericDialog;
import com.hero.ui.dialog.PowerDialog;
import com.hero.util.Rounder;
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 Power extends CharAffectingObject {
	/**
	 * This is a utility to help reduce the number of calculations required to
	 * keep Senses and Sense Modifiers in sync. Its value should be updated to
	 * the current system time any time a Sense, Sense Affecting Power, or Sense
	 * Modifier is altered, created, or deleted.
	 */
	public static long lastSenseEdit;

	protected boolean canAffectPrimary;

	protected int end;

	protected boolean resistantDefenses;

	protected boolean standardEffectAllowed;

	protected boolean useStandardEffect;

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

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

	public boolean canAffectPrimary() {
		return canAffectPrimary;
	}

	public boolean checkFigured(GenericObject o) {
		return true;
	}

	@Override
	public String getColumn2Output() {
		String ret = getAlias() + " " + getDamageDisplay();
		ret = ret.trim();
		if (getName().trim().length() > 0) {
			ret = "<i>" + getName() + ":</i>  " + ret;
		}
		if ((getInput() != null) && (getInput().trim().length() > 0)) {
			ret += ":  " + getInput();
		}
		if (getSelectedOption() != null) {
			ret += " (";
			ret += getSelectedOption().getAlias();
			ret += ")";
		}
		String adderString = getAdderString();
		if (adderString.trim().length() > 0) {
			ret += ", " + adderString;
		}
		ret += getModifierString();
		if ((getEndUsage() > 0)
				&& (GenericObject.findObjectByID(HeroDesigner.getActiveHero()
						.getPowers(), "ENDURANCERESERVE") != null)
				&& (GenericObject.findObjectByID(getAllAssignedModifiers(),
						"ENDRESERVEOREND") == null)
				&& !HeroDesigner.getInstance().getPrefs().useWG()) {
			if (getUseENDReserve()) {
				ret += " (uses END Reserve)";
			} else {
				ret += " (uses Personal END)";
			}
		}
		return ret;
	}

	@Override
	public String getColumn3Output() {
		if (getEndUsage() > 0) {
			return "" + getEndUsage();
		} else if (GenericObject.findObjectByID(getAssignedModifiers(),
				"CHARGES") == null) {
			return "0";
		} else {
			GenericObject charges = GenericObject.findObjectByID(
					getAssignedModifiers(), "CHARGES");
			String ret = charges.getSelectedOption().getAlias();
			if (GenericObject.findObjectByID(charges.getAssignedAdders(),
					"BOOSTABLE") != null) {
				return "[" + ret + " bc]";
			} else if (GenericObject.findObjectByID(
					charges.getAssignedAdders(), "RECOVERABLE") != null) {
				return "[" + ret + " rc]";
			} else if (GenericObject.findObjectByID(
					charges.getAssignedAdders(), "CONTINUING") != null) {
				return "[" + ret + " cc]";
			} else if (GenericObject.findObjectByID(
					charges.getAssignedAdders(), "NEVERRECOVER") != null) {
				return "[" + ret + " nr]";
			} else {
				return "[" + ret + "]";
			}
		}
	}

	/**
	 * Vary this method by Power...some will be 1d6 +1, 1 1/2d6, etc....some
	 * will be 1d6, 2d6, etc.. ...some will be 1d6, 1 1/2d6, etc....
	 * 
	 * @return The display for the damage (if any)
	 */
	public String getDamageDisplay() {
		if (display.toUpperCase().indexOf("[LVL]") >= 0) {
			return "";
		}
		if ((getLevelValue() == 0) || (getLevelCost() == 0)) {
			return "";
		}
		String ret = "" + getLevels() + "d6";
		int additional = 0;
		for (Adder ad : getAssignedAdders()) {
			if (ad.getXMLID().equals("PLUSONEPIP")) {
				ad.setDisplayInString(false);
				if (getLevels() > 0) {
					ret = getLevels() + "d6+1";
				} else {
					ret = "1 point";
				}
				additional += 1;
			}
			if (ad.getXMLID().equals("PLUSONEHALFDIE")) {
				ad.setDisplayInString(false);
				ret = getLevels() + " 1/2d6";
				additional += 1;
			}
			if (ad.getXMLID().equals("MINUSONEPIP")) {
				ad.setDisplayInString(false);
				ret = "" + (getLevels() + 1);
				ret = getLevels() + 1 + "d6-1";
				additional += 1;
			}
		}
		if (useStandardEffect()) {
			ret += " (standard effect: " + (getLevels() * 3 + additional)
					+ " point" + (getLevels() > 0 ? "s" : "") + ")";
		}
		return ret;
	}

	@Override
	public GenericDialog getDialog(boolean isNew, boolean isPower) {
		return new PowerDialog(this, isNew, isPower);
	}

	/**
	 * Returns any Mental Defense levels gained by this Power.
	 * 
	 * @return
	 */
	public int getMdLevels() {
		if (getMDIncreaseLevels() != 0) {
			return (int) Rounder.roundHalfUp(getLevels()
					/ getMDIncreaseLevels() * getMDIncrease());
		}
		return 0;
	}

	@Override
	public Element getSaveXML() {
		Element root = getGeneralSaveXML();
		root.setName("POWER");
		if (standardEffectAllowed) {
			root.setAttribute("USESTANDARDEFFECT", useStandardEffect ? "Yes"
					: "No");
		}
		root.setAttribute("QUANTITY", getQuantity() + "");

		root.setAttribute("AFFECTS_PRIMARY", getAffectPrimary() ? "Yes" : "No");
		root.setAttribute("AFFECTS_TOTAL", getAffectTotal() ? "Yes" : "No");
		return root;
	}

	@Override
	protected void init(Element element) {
		duration = "INSTANT";
		end = 0;
		canAffectPrimary = true;
		resistantDefenses = false;
		standardEffectAllowed = false;
		quantity = 1;

		super.init(element);
		affectsPrimary = false;
		exclusive = false;

		String check = XMLUtility.getValue(element, "EXCLUSIVE");
		if ((check != null) && check.trim().toUpperCase().startsWith("Y")) {
			exclusive = true;
		}
		check = XMLUtility.getValue(element, "STANDARDEFFECTALLOWED");
		if ((check != null) && check.trim().toUpperCase().startsWith("Y")) {
			standardEffectAllowed = true;
		}
	}

	/**
	 * Whether the Power should have the summation options (add to Primary, add
	 * to Secondary, do not add) available. If the Power affects
	 * Characteristics, this returns true, false otherwise.
	 * 
	 * @return
	 */
	public boolean isSummable() {
		if (affectsCharacteristics()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Whether any defenses provided by this Power are resistant.
	 * 
	 * @return
	 */
	public boolean resistantDefenses() {
		if (findObjectByID(getAssignedModifiers(), "RESISTANT") != null) {
			return true;
		}
		return resistantDefenses;
	}

	@Override
	public void restoreFromSave(Element root) {
		super.restoreFromSave(root);
		String check = XMLUtility.getValue(root, "AFFECTS_PRIMARY");
		if ((check != null) && (check.trim().length() > 0)) {
			affectsPrimary = check.trim().toUpperCase().startsWith("Y");
		}
		check = XMLUtility.getValue(root, "AFFECTS_TOTAL");
		if ((check != null) && (check.trim().length() > 0)) {
			affectsTotal = check.trim().toUpperCase().startsWith("Y");
		}
		check = XMLUtility.getValue(root, "USESTANDARDEFFECT");
		if ((check != null) && (check.trim().length() > 0)) {
			useStandardEffect = check.trim().toUpperCase().startsWith("Y");
		}
		check = XMLUtility.getValue(root, "QUANTITY");
		if ((check != null) && (check.trim().length() > 0)) {
			try {
				quantity = Integer.parseInt(check);
			} catch (Exception exp) {
				quantity = 1;
			}
		} else {
			quantity = 1;
		}
	}

	/**
	 * Sets whether any defenses derived from this Power are resistant.
	 * 
	 * @param val
	 */
	public void setResistantDefenses(boolean val) {
		resistantDefenses = val;
	}

	/**
	 * Whether or not to use Standard Effect.
	 * 
	 * @param val
	 */
	public void setUseStandardEffect(boolean val) {
		useStandardEffect = val;
	}

	/**
	 * Whether to allow the selection of Standard Effect.
	 * 
	 * @return
	 */
	public boolean standardEffectAllowed() {
		return standardEffectAllowed;
	}

	/**
	 * Whether Standard Effect has been selected.
	 * 
	 * @return
	 */
	public boolean useStandardEffect() {
		if (!standardEffectAllowed) {
			return false;
		}
		if ((HeroDesigner.getActiveHero() != null)
				&& !HeroDesigner.getActiveHero().getRules()
						.isStandardEffectAllowed()) {
			return false;
		}
		return useStandardEffect;
	}

}