/
com/planet_ink/coffee_mud/Abilities/Common/
com/planet_ink/coffee_mud/Abilities/Diseases/
com/planet_ink/coffee_mud/Abilities/Druid/
com/planet_ink/coffee_mud/Abilities/Fighter/
com/planet_ink/coffee_mud/Abilities/Languages/
com/planet_ink/coffee_mud/Abilities/Misc/
com/planet_ink/coffee_mud/Abilities/Prayers/
com/planet_ink/coffee_mud/Abilities/Properties/
com/planet_ink/coffee_mud/Abilities/Skills/
com/planet_ink/coffee_mud/Abilities/Songs/
com/planet_ink/coffee_mud/Abilities/Spells/
com/planet_ink/coffee_mud/Abilities/Thief/
com/planet_ink/coffee_mud/Abilities/Traps/
com/planet_ink/coffee_mud/Behaviors/
com/planet_ink/coffee_mud/CharClasses/interfaces/
com/planet_ink/coffee_mud/Commands/
com/planet_ink/coffee_mud/Commands/interfaces/
com/planet_ink/coffee_mud/Common/
com/planet_ink/coffee_mud/Common/interfaces/
com/planet_ink/coffee_mud/Exits/interfaces/
com/planet_ink/coffee_mud/Items/Armor/
com/planet_ink/coffee_mud/Items/Basic/
com/planet_ink/coffee_mud/Items/CompTech/
com/planet_ink/coffee_mud/Items/MiscMagic/
com/planet_ink/coffee_mud/Items/Weapons/
com/planet_ink/coffee_mud/Items/interfaces/
com/planet_ink/coffee_mud/Libraries/
com/planet_ink/coffee_mud/Libraries/interfaces/
com/planet_ink/coffee_mud/Locales/
com/planet_ink/coffee_mud/MOBS/
com/planet_ink/coffee_mud/Races/
com/planet_ink/coffee_mud/Races/interfaces/
com/planet_ink/coffee_mud/WebMacros/
com/planet_ink/coffee_mud/WebMacros/interfaces/
com/planet_ink/coffee_mud/core/
com/planet_ink/coffee_mud/core/collections/
com/planet_ink/coffee_mud/core/interfaces/
com/planet_ink/coffee_mud/core/intermud/
com/planet_ink/coffee_mud/core/intermud/i3/
com/planet_ink/coffee_web/server/
com/planet_ink/siplet/applet/
lib/
resources/factions/
resources/fakedb/
resources/progs/autoplayer/
resources/quests/holidays/
web/
web/admin.templates/
web/admin/grinder/
web/admin/images/
web/clan.templates/
web/pub.templates/
web/pub/images/mxp/
web/pub/sounds/
web/pub/textedit/
package com.planet_ink.coffee_mud.Abilities.Properties;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.collections.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.MaskingLibrary;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.*;

/*
   Copyright 2001-2016 Bo Zimmerman

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
@SuppressWarnings({"unchecked","rawtypes"})
public class Prop_SpellAdder extends Property implements AbilityContainer, TriggeredAffect
{
	@Override
	public String ID()
	{
		return "Prop_SpellAdder";
	}

	@Override
	public String name()
	{
		return "Casting spells on oneself";
	}

	@Override
	protected int canAffectCode()
	{
		return Ability.CAN_ITEMS | Ability.CAN_ROOMS | Ability.CAN_AREAS | Ability.CAN_MOBS;
	}

	protected Physical		lastMOB			= null;
	protected MOB			invokerMOB		= null;
	protected boolean		processing		= false;
	protected boolean		uninvocable		= true;
	protected short			level			= -1;
	protected short			maxTicks		= -1;
	protected short			chanceToHappen	= -1;
	protected List<Ability>	spellV			= null;
	
	protected MaskingLibrary.CompiledZapperMask compiledMask=null;
	
	protected List<Ability> unrevocableSpells = null;

	@Override
	public long flags()
	{
		return Ability.FLAG_CASTER;
	}

	@Override
	public int triggerMask()
	{
		return TriggeredAffect.TRIGGER_ALWAYS;
	}

	@Override
	protected void finalize()
	{
		spellV=null;
		compiledMask=null;
		chanceToHappen=-1;
		unrevocableSpells=null;
		if((invokerMOB!=null)&&(invokerMOB.Name().equals("invoker")))
			invokerMOB.destroy();
	}

	public String getMaskString(String newText)
	{
		final int maskindex=newText.toUpperCase().indexOf("MASK=");
		if(maskindex>0)
			return newText.substring(maskindex+5).trim();
		return "";
	}

	public String getParmString(String newText)
	{
		final int maskindex=newText.toUpperCase().indexOf("MASK=");
		if(maskindex>0)
			return newText.substring(0,maskindex).trim();
		return newText;
	}

	@Override
	public void setMiscText(String newText)
	{
		super.setMiscText(newText);
		spellV=null;
		compiledMask=null;
		lastMOB=null;
		chanceToHappen=-1;
		maxTicks=-1;
		final String maskString=getMaskString(newText);
		if(maskString.length()>0)
			compiledMask=CMLib.masking().getPreCompiledMask(maskString);
	}

	public List<Ability> getMySpellsV()
	{
		if(spellV!=null)
			return spellV;
		spellV=new Vector<Ability>();
		final String names=getParmString(text());
		final List<String> set=CMParms.parseSemicolons(names,true);
		String thisOne=null;
		for(int s=0;s<set.size();s++)
		{
			thisOne=set.get(s);
			if(thisOne.equalsIgnoreCase("NOUNINVOKE"))
			{
				this.uninvocable=false;
				continue;
			}
			if(thisOne.toUpperCase().startsWith("LEVEL"))
			{
				level=(short)CMParms.getParmInt(thisOne,"LEVEL",-1);
				if(level>=0)
					continue;
			}
			if(thisOne.toUpperCase().startsWith("MAXTICKS"))
			{
				maxTicks=(short)CMParms.getParmInt(thisOne,"MAXTICKS",-1);
				if(maxTicks!=-1)
					continue;
			}
			final int pctDex=thisOne.indexOf("% ");
			if((pctDex>0) && (thisOne.substring(pctDex+1).trim().length()>0))
				thisOne=thisOne.substring(pctDex+1).trim();
			String parm="";
			if((thisOne!=null)&&(thisOne.endsWith(")")))
			{
				final int x=thisOne.indexOf('(');
				if(x>0)
				{
					parm=thisOne.substring(x+1,thisOne.length()-1);
					thisOne=thisOne.substring(0,x).trim();
				}
			}

			Ability A=CMClass.getAbility(thisOne);
			if((A!=null)&&((A.classificationCode()&Ability.ALL_DOMAINS)!=Ability.DOMAIN_ARCHON))
			{
				A=(Ability)A.copyOf();
				A.setMiscText(parm);
				spellV.add(A);
			}
		}
		return spellV;
	}

	public boolean didHappen()
	{
		if(chanceToHappen<0)
		{
			final String parmString=getParmString(text());
			int x=parmString.indexOf('%');
			if(x<0)
			{
				chanceToHappen=100;
				return true;
			}
			int mul=1;
			int tot=0;
			while((--x)>=0)
			{
				if(Character.isDigit(parmString.charAt(x)))
					tot+=CMath.s_int(""+parmString.charAt(x))*mul;
				else
					x=-1;
				mul=mul*10;
			}
			chanceToHappen=(short)tot;
		}
		if(CMLib.dice().rollPercentage()<=chanceToHappen)
			return true;
		return false;
	}

	public Map<String, String> makeMySpellsH(List<Ability> V)
	{
		final Hashtable<String, String> spellH=new Hashtable<String, String>();
		for(int v=0;v<V.size();v++)
			spellH.put(V.get(v).ID(),V.get(v).ID());
		return spellH;
	}


	public MOB getBestInvokerMOB(Environmental target)
	{
		if(target instanceof MOB)
			return (MOB)target;
		if((target instanceof Item)&&(((Item)target).owner()!=null)&&(((Item)target).owner() instanceof MOB))
			return (MOB)((Item)target).owner();
		return null;
	}

	public MOB getInvokerMOB(Environmental source, Environmental target)
	{
		MOB mob=getBestInvokerMOB(affected);
		if(mob==null)
			mob=getBestInvokerMOB(source);
		if(mob==null)
			mob=getBestInvokerMOB(target);
		if(mob==null)
			mob=invokerMOB;
		if(mob==null)
		{
			Room R=CMLib.map().roomLocation(target);
			if(R==null)
				R=CMLib.map().roomLocation(target);
			if(R==null)
				R=CMLib.map().getRandomRoom();
			mob=CMLib.map().getFactoryMOB(R);
			mob.setName(L("invoker"));
			mob.basePhyStats().setLevel(affected.phyStats().level());
			mob.phyStats().setLevel(affected.phyStats().level());
		}
		invokerMOB=mob;
		return invokerMOB;
	}

	public List<Object> convertToV2(List<Ability> spellsV, Physical target)
	{
		final List<Object> VTOO=new Vector<Object>();
		for(int v=0;v<spellsV.size();v++)
		{
			Ability A=spellsV.get(v);
			final Ability EA=(target!=null)?target.fetchEffect(A.ID()):null;
			if((EA==null)&&(didHappen()))
			{
				final String t=A.text();
				A=(Ability)A.copyOf();
				Vector<String> V2=new Vector<String>();
				if(t.length()>0)
				{
					final int x=t.indexOf('/');
					if(x<0)
					{
						V2=CMParms.parse(t);
						A.setMiscText("");
					}
					else
					{
						V2=CMParms.parse(t.substring(0,x));
						A.setMiscText(t.substring(x+1));
					}
				}
				VTOO.add(A);
				VTOO.add(V2);
			}
		}
		return VTOO;
	}

	public boolean addMeIfNeccessary(PhysicalAgent source, Physical target, boolean makeLongLasting, int asLevel, short maxTicks)
	{
		final List<Ability> V=getMySpellsV();
		if((target==null)
		||(V.size()==0)
		||((compiledMask!=null)
			&&(!CMLib.masking().maskCheck(compiledMask,target,true))))
				return false;
		final List VTOO=convertToV2(V,target);
		if(VTOO.size()==0)
			return false;
		final MOB qualMOB=getInvokerMOB(source,target);
		for(int v=0;v<VTOO.size();v+=2)
		{
			final Ability A=(Ability)VTOO.get(v);
			final Vector V2=(Vector)VTOO.get(v+1);
			if(level >= 0)
				asLevel = level;
			else
			if(asLevel <=0)
				asLevel = (affected!=null)?affected.phyStats().level():0;
			A.invoke(qualMOB,V2,target,true,asLevel);
			final Ability EA=target.fetchEffect(A.ID());
			lastMOB=target;
			// this needs to go here because otherwise it makes non-item-invoked spells long lasting,
			// which means they dont go away when item is removed.
			if(EA!=null)
			{
				if((maxTicks>0)&&(maxTicks<Short.MAX_VALUE)&&(CMath.s_int(EA.getStat("TICKDOWN"))>maxTicks))
					EA.setStat("TICKDOWN", Short.toString(maxTicks));
				else
				if(makeLongLasting)
				{
					EA.makeLongLasting();
					if(!uninvocable)
					{
						EA.makeNonUninvokable();
						if(unrevocableSpells == null)
							unrevocableSpells = new Vector<Ability>();
						unrevocableSpells.add(EA);
					}
				}
			}
		}
		return true;
	}

	@Override
	public String accountForYourself()
	{
		return spellAccountingsWithMask("Casts ", " on the first one who enters.");
	}

	public void removeMyAffectsFromLastMOB()
	{
		removeMyAffectsFrom(lastMOB);
		lastMOB=null;
	}

	@Override
	public void setAffectedOne(Physical P)
	{
		super.setAffectedOne(P);
		if(P == null)
		{
			removeMyAffectsFromLastMOB();
			finalize();
		}
	}

	public void removeMyAffectsFrom(Physical P)
	{
		if(P==null)
			return;

		int x=0;
		final Vector<Ability> eff=new Vector<Ability>();
		Ability thisAffect=null;
		for(x=0;x<P.numEffects();x++) // personal
		{
			thisAffect=P.fetchEffect(x);
			if(thisAffect!=null)
				eff.addElement(thisAffect);
		}
		if(eff.size()>0)
		{
			final Map<String,String> h=makeMySpellsH(getMySpellsV());
			if(unrevocableSpells != null)
			{
				for(int v=unrevocableSpells.size()-1;v>=0;v--)
				{
					thisAffect = unrevocableSpells.get(v);
					if(h.containsKey(thisAffect.ID()))
						P.delEffect(thisAffect);
				}
			}
			else
			for(x=0;x<eff.size();x++)
			{
				thisAffect=eff.elementAt(x);
				final String ID=h.get(thisAffect.ID());
				if((ID!=null)
				&&(thisAffect.invoker()==getInvokerMOB(P,P)))
				{
					thisAffect.unInvoke();
					if((!uninvocable)&&(!thisAffect.canBeUninvoked()))
						P.delEffect(thisAffect);
				}
			}
			unrevocableSpells = null;
		}
	}

	@Override
	public void executeMsg(Environmental host, CMMsg msg)
	{
		if((affected instanceof Room)||(affected instanceof Area))
		{
			if((msg.targetMinor()==CMMsg.TYP_LEAVE)
			||(msg.sourceMinor()==CMMsg.TYP_RECALL))
				removeMyAffectsFrom(msg.source());
			if(msg.targetMinor()==CMMsg.TYP_ENTER)
				addMeIfNeccessary(msg.source(),msg.source(),true,0,maxTicks);
		}
		super.executeMsg(host,msg);
	}

	@Override
	public void affectPhyStats(Physical host, PhyStats affectableStats)
	{
		if(processing)
			return;
		if((affected instanceof MOB)
		   ||(affected instanceof Item))
		{
			processing=true;
			if((lastMOB!=null)
			&&(host!=lastMOB))
				removeMyAffectsFrom(lastMOB);

			if((lastMOB==null)&&(host instanceof PhysicalAgent))
				addMeIfNeccessary((PhysicalAgent)host,host,true,0,maxTicks);
			processing=false;
		}
	}

	public String spellAccountingsWithMask(String pre, String post)
	{
		final List<Ability> spellList=getMySpellsV();
		String id="";
		for(int v=0;v<spellList.size();v++)
		{
			final Ability A=spellList.get(v);
			if(spellList.size()==1)
				id+=A.name();
			else
			if(v==(spellList.size()-1))
				id+="and "+A.name();
			else
				id+=A.name()+", ";
		}
		if(spellList.size()>0)
			id=pre+id+post;
		final String maskString=getMaskString(text());
		if(maskString.length()>0)
			id+="  Restrictions: "+CMLib.masking().maskDesc(maskString);
		return id;
	}
	
	@Override
	public void addAbility(Ability to)
	{
	}

	@Override
	public void delAbility(Ability to)
	{
	}

	@Override
	public int numAbilities()
	{
		return getMySpellsV().size();
	}

	@Override
	public Ability fetchAbility(int index)
	{
		final List<Ability> spellsV = getMySpellsV();
		if (spellsV.size() == 0)
			return null;
		if ((index < 0) || (index >= spellsV.size()))
			return null;
		try
		{
			return spellsV.get(index);
		}
		catch (final Exception e)
		{
			return null;
		}
	}

	@Override
	public Ability fetchAbility(String ID)
	{
		for (final Enumeration<Ability> a = abilities(); a.hasMoreElements();)
		{
			final Ability A = a.nextElement();
			if (A == null)
				continue;
			if (A.ID().equalsIgnoreCase(ID))
				return A;
		}
		return null;
	}

	@Override
	public Ability fetchRandomAbility()
	{
		final List<Ability> spellsV = getMySpellsV();
		if (spellsV.size() == 0)
			return null;
		return spellsV.get(CMLib.dice().roll(1, spellsV.size(), -1));
	}

	@Override
	public Enumeration<Ability> abilities()
	{
		return new FilteredEnumeration<Ability>(new IteratorEnumeration<Ability>(getMySpellsV().iterator()),new Filterer<Ability>()
		{
			@Override
			public boolean passesFilter(Ability obj)
			{
				return didHappen();
			}
		});
	}

	@Override
	public void delAllAbilities()
	{
		setMiscText("");
	}

	@Override
	public int numAllAbilities()
	{
		return numAbilities();
	}

	@Override
	public Enumeration<Ability> allAbilities()
	{
		return new IteratorEnumeration<Ability>(getMySpellsV().iterator());
	}

	@Override
	public boolean invoke(MOB mob, List<String> commands, Physical givenTarget, boolean auto, int asLevel)
	{
		final String s=CMParms.combine(commands,0);
		if(s.length()>0)
			setMiscText(s);
		if(givenTarget!=null)
			addMeIfNeccessary(mob,givenTarget,false,asLevel,maxTicks);
		return true;
	}
}