package com.hero.objects.modifiers;

import java.text.NumberFormat;

import org.jdom.Element;

import com.hero.HeroDesigner;
import com.hero.objects.Adder;
import com.hero.objects.GenericObject;
import com.hero.objects.powers.Absorption;
import com.hero.objects.powers.LifeSupport;
import com.hero.objects.powers.NakedModifier;
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 AreaEffect extends Modifier {

	private static String xmlID = "AOE";

	public AreaEffect(Element root) {
		super(root, AreaEffect.xmlID);
	}

	@Override
	public String included(GenericObject o) {
		String ret = super.included(o);
		if (ret.trim().length() > 0) {
			return ret;
		}

        if (forceAllow()) return ret;
		if (o instanceof NakedModifier || o instanceof LifeSupport
				|| o instanceof Absorption) {
			return "";
		}
		if (o.getXMLID().equals("POSSESSION")) {
			return getDisplay()+" cannot be applied to Possession.";
		}
		if (o.getXMLID().equals("IMAGES")
				&& HeroDesigner.getActiveTemplate().is6E()) {
			return "";
		}
		if (o.getTarget().equals("HEX")
				&& o.findObjectByID(o.getAssignedModifiers(), "AOE") == null) {
			return getDisplay()
					+ " cannot be applied to Powers which already affect an area.";
		} else if (!(o.getTarget().equals("DCV") || o.getTarget().equals("ECV")
				|| o.getTarget().equals("OCV") || o.getTarget().equals("OMCV")
				|| o.getTarget().equals("MCV") || o.getTarget().equals("DMCV"))
				&& o.findObjectByID(o.getAssignedModifiers(), "AOE") == null) {
			return getDisplay()
					+ " can only be applied to Powers which are targeted on others.";
		} else if (GenericObject.findObjectByID(o.getAssignedModifiers(),
				"EXPLOSION") != null) {
			return getDisplay()
					+ " cannot be applied to abilities which already affect an area.";
		} else {
			return "";
		}
	}

	public String getLevelInfo() {
		return getArea(null);
	}

	/**
	 * Returns the area that is effected
	 * 
	 * @param o
	 * @return
	 */
	public String getArea(GenericObject o) {
		String ret = "";
		if (HeroDesigner.getActiveTemplate().is6E()) {
			Adder option = getSelectedOption();
			if (option == null)
				return ret;
			String lvlString = ""
					+ NumberFormat.getInstance().format((getLevels()));
			if (option.getXMLID().equals("RADIUS")) {
				ret += lvlString + "m Radius";
			} else if (option.getXMLID().equals("CONE")) {
				ret += lvlString + "m Cone";
			} else if (option.getXMLID().equals("LINE")) {
				ret += lvlString + "m";

				if (GenericObject.findObjectByID(getAssignedAdders(),
						"DOUBLEHEIGHT") != null) {
					Adder ad = (Adder) GenericObject.findObjectByID(
							getAssignedAdders(), "DOUBLEHEIGHT");
					ad.setDisplayInString(false);
					multiplier = (ad.getLevels());
					ret += " Long, "
							+ NumberFormat.getInstance().format(multiplier)
							+ "m Tall";
				}
				if (GenericObject.findObjectByID(getAssignedAdders(),
						"DOUBLEWIDTH") != null) {
					Adder ad = (Adder) GenericObject.findObjectByID(
							getAssignedAdders(), "DOUBLEWIDTH");
					ad.setDisplayInString(false);
					multiplier = (ad.getLevels());
					if (GenericObject.findObjectByID(getAssignedAdders(),
							"DOUBLEHEIGHT") != null) {
						ret += ", "
								+ NumberFormat.getInstance().format(multiplier)
								+ "m Wide";
					} else {
						ret += " Long, 2m Tall, "
								+ NumberFormat.getInstance().format(multiplier)
								+ "m Wide";
					}
				} else if (GenericObject.findObjectByID(getAssignedAdders(),
						"DOUBLEHEIGHT") != null) {
					ret += ", 2m Wide";
				}
				ret += " Line";
			} else if (option.getXMLID().equals("ANY")) {
				ret += lvlString + " 2m Areas";
			} else if (option.getXMLID().equals("SURFACE")) {
				ret += lvlString + "m Surface";
			}
			for (Adder ad : getAssignedAdders()) {
				if (ad.getXMLID().equals("EXPLOSION")) {
					ad.setDisplayInString(false);
					ret += " Explosion";
				}
			}
			return ret;
		}
		if (o == null) {
			return "";
		}
		Adder option = getSelectedOption();
		if (option == null) {
			return "";
		} else if (option.getXMLID().equals("TRAIL")) {
			return option.getAlias();
		}
		int multiplier = 1;
		double effectiveActive = parent.getActiveCost(getXMLID());
		if (GenericObject.findObjectByID(parent.getAssignedModifiers(),
				"AUTOFIRE") != null) {
			Autofire af = (Autofire) GenericObject.findObjectByID(parent
					.getAssignedModifiers(), "AUTOFIRE");
			if (af.surchargeIncluded()) {
				effectiveActive -= parent.getTotalCost();
			}
		}
		if (parent instanceof NakedModifier) {
			NakedModifier nm = (NakedModifier) parent;
			effectiveActive = nm.getLevels();
			ret = "up to ";
		}
		double limitationTotal = 0d;
		for (int i = 0; i < parent.getAssignedModifiers().size(); i++) {
			Modifier mod = parent.getAssignedModifiers().get(i);
			if (mod.getTotalValue() < 0) {
				limitationTotal += mod.getTotalValue();
			}
		}
		Adder doubleArea = null;
		for (Adder ad : getAssignedAdders()) {
			if (ad.getXMLID().equals("DOUBLEAREA")) {
				doubleArea = ad;
				ad.setDisplayInString(false);
				multiplier = (int) Math.pow(ad.getLevelPower(), ad.getLevels());
				break;
			}
		}
		int straightRadius = 1;
		int baseArea = (int) Rounder.roundHalfUp((effectiveActive / 10d));
		if (baseArea < 1) {
			baseArea = 1;
		}
		int level1 = (int) Rounder
				.roundHalfUp(((double) multiplier * baseArea));
		straightRadius = baseArea;
		if (level1 < 1) {
			level1 = 1;
			straightRadius = 1;
		}
		int level2 = (int) Rounder
				.roundHalfUp((multiplier * effectiveActive / 5d));
		if (effectiveActive < 5) {
			level2 = (int) Rounder.roundHalfUp(((double) multiplier * 1));
		}
		if (option.getXMLID().equals("HEX")) {
			if (multiplier > 1) {
				if (multiplier >= straightRadius) {
					if (doubleArea != null && doubleArea.getLevels() == 1) {
						getAssignedAdders().remove(doubleArea);
					}
					doubleArea.setLevels(doubleArea.getLevels() - 1);
					return getArea(o);
				}
				ret += multiplier + "\" radius";
			} else {
				ret += "One Hex";
			}
		} else if (option.getXMLID().equals("RADIUS")) {
			ret += level1 + "\" Radius";
		} else if (option.getXMLID().equals("CONE")) {
			ret += 1 + level2 + "\" Cone";
		} else if (option.getXMLID().equals("LINE")) {
			ret += level2 * 2 + "\"";
			if (GenericObject.findObjectByID(getAssignedAdders(),
					"DOUBLELENGTH") != null) {
				Adder ad = (Adder) GenericObject.findObjectByID(
						getAssignedAdders(), "DOUBLELENGTH");
				ad.setDisplayInString(false);
				if (multiplier == 1) {
					multiplier = 0;
				}
				multiplier += (int) Math
						.pow(ad.getLevelPower(), ad.getLevels());
				level2 = (int) Rounder.roundHalfUp((multiplier
						* effectiveActive / 5d));
				ret = level2 * 2 + "\"";
			}
			if (GenericObject.findObjectByID(getAssignedAdders(),
					"DOUBLEHEIGHT") != null) {
				Adder ad = (Adder) GenericObject.findObjectByID(
						getAssignedAdders(), "DOUBLEHEIGHT");
				ad.setDisplayInString(false);
				multiplier = (int) Math.pow(ad.getLevelPower(), ad.getLevels());
				ret += " Long, " + multiplier + "\" Tall";
			}
			if (GenericObject
					.findObjectByID(getAssignedAdders(), "DOUBLEWIDTH") != null) {
				Adder ad = (Adder) GenericObject.findObjectByID(
						getAssignedAdders(), "DOUBLEWIDTH");
				ad.setDisplayInString(false);
				multiplier = (int) Math.pow(ad.getLevelPower(), ad.getLevels());
				if (GenericObject.findObjectByID(getAssignedAdders(),
						"DOUBLEHEIGHT") != null) {
					ret += ", " + multiplier + "\" Wide";
				} else {
					ret += " Long, 1\" Tall, " + multiplier + "\" Wide";
				}
			} else if (GenericObject.findObjectByID(getAssignedAdders(),
					"DOUBLEHEIGHT") != null) {
				ret += ", 1\" Wide";
			}
			ret += " Line";
		} else if (option.getXMLID().equals("ANY")) {
			ret += level1 + "\" Any Area";
		}
		return ret;
	}

	@Override
	public double getTotalValue() {
		if (HeroDesigner.getActiveTemplate().is6E()) {
			double val = getBaseCost();
			for (Adder ad : getAvailableAdders()) {
				if (ad.getXMLID().equals("DOUBLEHEIGHT")
						|| ad.getXMLID().equals("DOUBLEWIDTH")) {
					ad.setDisplayInString(false);
					int lvls = (int) Math.ceil((Math.log(((double) ad
							.getLevels())
							/ (double) ad.getLevelMultiplier()))
							/ Math.log(ad.getLevelPower()));
					lvls -= 1;
					if (lvls < 1)
						lvls = 1;
					ad.setBaseCost(0);
					ad.setBaseCost((lvls * ad.getLevelCost())
							- ad.getDoubleTotal());
				}
			}
			for (Adder ad : getAssignedAdders()) {
				if (ad.getXMLID().equals("DOUBLEHEIGHT")
						|| ad.getXMLID().equals("DOUBLEWIDTH")) {
					ad.setDisplayInString(false);
					int lvls = (int) Math.ceil((Math.log(((double) ad
							.getLevels())
							/ (double) ad.getLevelMultiplier()))
							/ Math.log(ad.getLevelPower()));
					lvls -= 1;
					if (lvls < 1)
						lvls = 1;
					val += lvls * ad.getLevelCost();
					ad.setBaseCost(0);
					ad.setBaseCost((lvls * ad.getLevelCost())
							- ad.getDoubleTotal());
				} else if (ad.getXMLID().equals("MOBILE")) {
					int lvls = (int) Math.ceil(Math.log(((double) ad
							.getLevels() / (double) ad.getLevelMultiplier()))
							/ Math.log(ad.getLevelPower()));
					if (lvls < 1)
						lvls = 1;
					val += lvls * ad.getLevelCost() + ad.getOrigBaseCost();
					ad.setBaseCost(0);
					ad.setBaseCost(ad.getOrigBaseCost()
							+ (lvls * ad.getLevelCost()) - ad.getDoubleTotal());
				} else {
					val += ad.getDoubleTotal();
				}
			}

			// figure out how many powers of 2 we've used for the defined area
			int mults = (int) Math
					.ceil(Math
							.log(((double) getLevels() / (double) getLevelMultiplier()))
							/ Math.log(getLevelPower()));
			if (mults < 1)
				mults = 1;
			if (getProgenitor() != null
					&& getProgenitor().getXMLID().equals("IMAGES")
					&& getLevels() == 1) {
				mults = 0;
			}
			val += mults * getLevelCost();

			double advantageTotal = 0d;
			for (Modifier mod : getAssignedModifiers()) {
				if (mod.getTotalValue() > 0) {
					advantageTotal += mod.getTotalValue();
				}
			}
			double active = val * (1 + advantageTotal);
			double limitationTotal = 0d;
			for (Modifier mod : getAssignedModifiers()) {
				if (mod.getTotalValue() < 0) {
					limitationTotal += Math.abs(mod.getTotalValue());
				}
			}
			val = active / (1 + limitationTotal);

			// the following effectively "rounds" the value to the closest 1/4
			val = val * 4d;
			int mult = 1;
			if (val < 0) {
				mult = -1;
			}
			val = val * mult;
			val = Rounder.roundHalfUp(val);
			val = val * mult;
			val = val / 4d;

			if ((val < getMinimumCost()) && isMinSet()) {
				return getMinimumCost();
			} else if ((val > getMaxCost()) && isMaxSet()) {
				return getMaxCost();
			} else {
				return val;
			}
		} else {
			return super.getTotalValue();
		}
	}

	@Override
	public void setSelectedOption(Adder option) {
		boolean same = false;
		if (getSelectedOption() != null && getSelectedOption().equals(option)) {
			same = true;
		}
		super.setSelectedOption(option);
		if (same) {
			return;
		}
		if (GenericObject.findObjectByID(getAssignedAdders(), "DOUBLEAREA") != null) {
			getAssignedAdders().remove(
					GenericObject.findObjectByID(getAssignedAdders(),
							"DOUBLEAREA"));
		}
		if (GenericObject.findObjectByID(getAssignedAdders(), "DOUBLELENGTH") != null) {
			getAssignedAdders().remove(
					GenericObject.findObjectByID(getAssignedAdders(),
							"DOUBLELENGTH"));
		}
		if (GenericObject.findObjectByID(getAssignedAdders(), "DOUBLEWIDTH") != null) {
			getAssignedAdders().remove(
					GenericObject.findObjectByID(getAssignedAdders(),
							"DOUBLEWIDTH"));
		}
		if (GenericObject.findObjectByID(getAssignedAdders(), "DOUBLEHEIGHT") != null) {
			getAssignedAdders().remove(
					GenericObject.findObjectByID(getAssignedAdders(),
							"DOUBLEHEIGHT"));
		}
	}

	@Override
	public String getColumn2Output() {
		String adderString = "";
		String area = getArea(parent);
		String ret = getAlias();
		double val = getTotalValue();
		for (Adder ad : getAssignedAdders()) {
			if (ad.getXMLID().equals("ACCURATE")) {
				ret += " " + ad.getAlias();
				ad.setDisplayInString(false);
			} else if (ad.getXMLID().equals("NONSELECTIVETARGET")) {
				ret += " " + ad.getAlias();
			} else if (ad.getXMLID().equals("DOUBLEAREA")) {
				continue;
			} else if (ad.getXMLID().equals("MOBILE")) {
				if (adderString.trim().length() > 0)
					adderString += ", ";
				adderString += ad.getAlias();
				int lvls = (int) Math.ceil(Math
						.log(((double) ad.getLevels() / (double) ad
								.getLevelMultiplier()))
						/ Math.log(ad.getLevelPower()));
				if (lvls < 1)
					lvls = 1;
				adderString += " (" + ad.getLevels() + "m per Phase; "
						+ getFraction(ad.getDoubleTotal()) + ")";
				ad.setDisplayInString(false);
			} else if (ad.displayInString()) {
				val -= ad.getBaseCost();
				if (adderString.trim().length() > 0) {
					adderString += ", ";
				}
				adderString += ad.getAlias() + " ("
						+ getFraction(ad.getBaseCost()) + ")";
			}
		}
		if (getInput() != null && getInput().trim().length() > 0) {
			if (ret.trim().length() > 0) {
				ret += ":  ";
			}
			ret += getInput();
		}
		ret += " (" + area + "; ";
		if (getComments().trim().length() > 0) {
			ret += getComments() + "; ";
		}
		ret += getFraction(val) + ")";
		if (adderString.trim().length() > 0) {
			ret += ", ";
			ret += adderString;
		}
		return ret;
	}
}