package com.planet_ink.coffee_mud.Abilities.Traps;
import com.planet_ink.coffee_mud.Abilities.StdAbility;
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.*;
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-2022 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.
*/
public class Trap_Trap extends StdAbility implements Trap
{
	@Override
	public String ID()
	{
		return "Trap_Trap";
	}

	private final static String	localizedName	= CMLib.lang().L("a Trap!");

	@Override
	public String name()
	{
		return localizedName;
	}

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

	@Override
	protected int canTargetCode()
	{
		return 0;
	}

	protected static MOB	benefactor	= CMClass.getMOB("StdMOB");
	protected boolean		sprung		= false;
	protected Room			myPit		= null;
	protected Room			myPitUp		= null;
	protected int			reset		= 60;						// 5 minute
																	// reset is
																	// standard

	protected int trapType()
	{
		return CMLib.dice().roll(1, 3, -1);
	}

	public Trap_Trap()
	{
		super();
		if(benefactor==null)
			benefactor=CMClass.getMOB("StdMOB");
	}

	@Override
	public void activateBomb()
	{
	}

	@Override
	public boolean isABomb()
	{
		return false;
	}

	@Override
	public boolean sprung()
	{
		return sprung;
	}

	@Override
	public boolean disabled()
	{
		return sprung;
	}

	@Override
	public void disable()
	{
		sprung = true;
	}

	@Override
	public void setReset(final int Reset)
	{
		reset = Reset;
	}

	@Override
	public int getReset()
	{
		return reset;
	}

	// as these are not standard traps, we return this!
	@Override
	public boolean maySetTrap(final MOB mob, final int asLevel)
	{
		return false;
	}

	@Override
	public boolean canSetTrapOn(final MOB mob, final Physical P)
	{
		return false;
	}

	@Override
	public boolean canReSetTrap(final MOB mob)
	{
		return false;
	}

	@Override
	public List<Item> getTrapComponents()
	{
		return new Vector<Item>(0);
	}

	@Override
	public String requiresToSet()
	{
		return "";
	}

	@Override
	public Trap setTrap(final MOB mob, final Physical P, final int trapBonus, final int qualifyingClassLevel, final boolean perm)
	{
		if(P==null)
			return null;
		int level=mob.phyStats().level();
		if(level<qualifyingClassLevel)
			level=qualifyingClassLevel;
		level+=trapBonus;
		if(level>=100)
			level=99;
		final int rejuv=((100-level)*30);
		final Trap T=(Trap)copyOf();
		T.setReset(rejuv);
		T.setInvoker(mob);
		P.addEffect(T);
		T.setAbilityCode(trapBonus);
		if(perm)
		{
			T.setSavable(true);
			T.makeNonUninvokable();
		}
		else
		{
			T.setSavable(false);
			CMLib.threads().startTickDown(T,Tickable.TICKID_TRAP_DESTRUCTION,level*30);
		}
		return T;
	}

	public void gas(final MOB mob)
	{
		if(CMLib.dice().rollPercentage()<mob.charStats().getSave(CharStats.STAT_SAVE_TRAPS))
			mob.location().show(mob,affected,this,CMMsg.MSG_OK_ACTION,L("<S-NAME> avoid(s) a gas trap set in <T-NAME>."));
		else
		if(mob.location().show(mob,affected,this,CMMsg.MSG_OK_ACTION,L("<S-NAME> trigger(s) a trap set in <T-NAME>!")))
		{
			if(mob.phyStats().level()>15)
			{
				mob.location().showHappens(CMMsg.MSG_OK_ACTION,L("The room fills with gas!"));
				for(int i=0;i<mob.location().numInhabitants();i++)
				{
					final MOB target=mob.location().fetchInhabitant(i);
					if(target==null)
						break;

					int dmg=CMLib.dice().roll(target.phyStats().level(),10,1);
					final CMMsg msg=CMClass.getMsg(invoker(),target,this,CMMsg.MSG_OK_ACTION,CMMsg.MSK_MALICIOUS_MOVE|CMMsg.TYP_GAS,CMMsg.MSG_NOISYMOVEMENT,null);
					if(target.location().okMessage(target,msg))
					{
						target.location().send(target,msg);
						if(msg.value()>0)
							dmg=(int)Math.round(CMath.div(dmg,2.0));
						CMLib.combat().postDamage(invoker(),target,this,dmg,CMMsg.MASK_ALWAYS|CMMsg.MASK_MALICIOUS|CMMsg.TYP_GAS,Weapon.TYPE_GASSING,L("The gas <DAMAGE> <T-NAME>!"));
					}
				}
			}
			else
			{
				final MOB target=mob;
				int dmg=CMLib.dice().roll(target.phyStats().level(),10,1);
				final CMMsg msg=CMClass.getMsg(invoker(),target,this,CMMsg.MSG_OK_ACTION,CMMsg.MSK_MALICIOUS_MOVE|CMMsg.TYP_GAS,CMMsg.MSG_NOISYMOVEMENT,null);
				if(target.location().okMessage(target,msg))
				{
					target.location().send(target,msg);
					if(msg.value()>0)
						dmg=(int)Math.round(CMath.div(dmg,2.0));
					CMLib.combat().postDamage(invoker(),target,this,dmg,CMMsg.MASK_ALWAYS|CMMsg.MASK_MALICIOUS|CMMsg.TYP_GAS,Weapon.TYPE_GASSING,L("A sudden blast of gas <DAMAGE> <T-NAME>!"));
				}
			}
		}
	}

	public void needle(final MOB mob)
	{
		if(CMLib.dice().rollPercentage()<mob.charStats().getSave(CharStats.STAT_SAVE_TRAPS))
			mob.location().show(mob,affected,this,CMMsg.MSG_OK_ACTION,L("<S-NAME> avoid(s) a needle trap set in <T-NAME>."));
		else
		if(mob.location().show(mob,affected,this,CMMsg.MSG_OK_ACTION,L("<S-NAME> trigger(s) a needle trap set in <T-NAME>!")))
		{
			final MOB target=mob;
			int dmg=CMLib.dice().roll(target.phyStats().level(),5,1);
			final CMMsg msg=CMClass.getMsg(invoker(),target,this,CMMsg.MSG_OK_ACTION,CMMsg.MSK_MALICIOUS_MOVE|CMMsg.TYP_JUSTICE,CMMsg.MSG_NOISYMOVEMENT,null);
			if(target.location().okMessage(target,msg))
			{
				target.location().send(target,msg);
				if(msg.value()>0)
					dmg=(int)Math.round(CMath.div(dmg,2.0));
				CMLib.combat().postDamage(invoker(),target,this,dmg,CMMsg.MASK_ALWAYS|CMMsg.MASK_MALICIOUS|CMMsg.TYP_JUSTICE,Weapon.TYPE_PIERCING,L("The needle <DAMAGE> <T-NAME>!"));

				final Ability P=CMClass.getAbility("Poison");
				if(P!=null)
					P.invoke(invoker(),target,true,0);
			}
		}
	}

	public void blade(final MOB mob)
	{
		if(CMLib.dice().rollPercentage()<mob.charStats().getSave(CharStats.STAT_SAVE_TRAPS))
			mob.location().show(mob,affected,this,CMMsg.MSG_OK_ACTION,L("<S-NAME> avoid(s) a blade trap set in <T-NAME>."));
		else
		if(mob.location().show(mob,affected,this,CMMsg.MSG_OK_ACTION,L("<S-NAME> trigger(s) a blade trap set in <T-NAME>!")))
		{
			final MOB target=mob;
			int dmg=CMLib.dice().roll(target.phyStats().level(),2,0);
			final CMMsg msg=CMClass.getMsg(invoker(),target,this,CMMsg.MSG_OK_ACTION,CMMsg.MSK_MALICIOUS_MOVE|CMMsg.TYP_JUSTICE,CMMsg.MSG_NOISYMOVEMENT,null);
			if(target.location().okMessage(target,msg))
			{
				target.location().send(target,msg);
				if(msg.value()>0)
					dmg=(int)Math.round(CMath.div(dmg,2.0));
				final Ability P=CMClass.getAbility("Poison");
				if(P!=null)
					P.invoke(invoker(),target,true,0);
				CMLib.combat().postDamage(invoker(),target,this,dmg,CMMsg.MASK_ALWAYS|CMMsg.MASK_MALICIOUS|CMMsg.TYP_JUSTICE,Weapon.TYPE_PIERCING,L("The blade <DAMAGE> <T-NAME>!"));
			}
		}
	}

	public void victimOfSpell(final MOB mob)
	{
		if(CMLib.dice().rollPercentage()<mob.charStats().getSave(CharStats.STAT_SAVE_TRAPS))
			mob.location().show(mob,affected,this,CMMsg.MSG_OK_ACTION,L("<S-NAME> avoid(s) a magic trap set in <T-NAME>."));
		else
		if(mob.location().show(mob,affected,this,CMMsg.MSG_OK_ACTION,L("<S-NAME> trigger(s) a trap set in <T-NAME>!")))
		{
			String spell=text();
			final int x=spell.indexOf(';');
			Vector<String> V=new Vector<String>();
			V.add(mob.name());
			if(x>0)
			{
				V=CMParms.parse(spell.substring(x+1));
				V.add(0,mob.name());
				spell=spell.substring(0,x);
			}
			final Ability A=CMClass.findAbility(spell);
			if(A==null)
			{
				mob.location().showHappens(CMMsg.MSG_OK_VISUAL,L("But nothing happened..."));
				return;
			}
			A.invoke(invoker(),V,mob,true,0);
		}
	}

	public void fallInPit(final MOB mob)
	{
		if(CMLib.flags().isInFlight(mob))
		{
			mob.location().show(mob,null,CMMsg.MSG_OK_ACTION,L("<S-NAME> trigger(s) a trap door beneath <S-HIS-HER> feet! <S-NAME> pause(s) over it in flight."));
			return;
		}
		else
		if(CMLib.dice().rollPercentage()<mob.charStats().getSave(CharStats.STAT_SAVE_TRAPS))
			mob.location().show(mob,affected,this,CMMsg.MSG_OK_ACTION,L("<S-NAME> avoid(s) a trap door beneath <S-HIS-HER> feet."));
		else
		if(mob.location().show(mob,affected,this,CMMsg.MSG_OK_ACTION,L("<S-NAME> trigger(s) a trap door beneath <S-HIS-HER> feet! <S-NAME> fall(s) in!")))
		{
			if((myPit==null)||(myPitUp==null))
			{
				myPitUp=CMClass.getLocale("ClimbableSurface");
				myPitUp.setRoomID("");
				myPitUp.setSavable(false);
				myPitUp.setArea(mob.location().getArea());
				myPitUp.basePhyStats().setDisposition(myPitUp.basePhyStats().disposition()|PhyStats.IS_DARK);
				myPitUp.setDisplayText(L("Inside a dark pit"));
				myPitUp.setDescription(L("The walls here are slick and tall.  The trap door is just above you."));
				myPitUp.recoverPhyStats();

				myPit=CMClass.getLocale("StdRoom");
				myPit.setSavable(false);
				myPit.setRoomID("");
				myPit.setArea(mob.location().getArea());
				myPit.basePhyStats().setDisposition(myPit.basePhyStats().disposition()|PhyStats.IS_DARK);
				myPit.setDisplayText(L("Inside a dark pit"));
				myPit.setDescription(L("The walls here are slick and tall.  You can barely see the trap door well above you."));
				myPit.setRawExit(Directions.UP,CMClass.getExit("StdOpenDoorway"));
				myPit.rawDoors()[Directions.UP]=myPitUp;
				myPitUp.recoverPhyStats();

			}
			final Exit exit=CMClass.getExit("StdClosedDoorway");
			exit.setSavable(false);
			myPitUp.setRawExit(Directions.UP,exit);
			myPitUp.rawDoors()[Directions.UP]=mob.location();
			if((mob.location().getRoomInDir(Directions.DOWN)==null)
			&&(mob.location().getExitInDir(Directions.DOWN)==null))
			{
				mob.location().setRawExit(Directions.DOWN,exit);
				mob.location().rawDoors()[Directions.DOWN]=myPitUp;
			}
			myPit.bringMobHere(mob,false);
			if(mob.phyStats().weight()<5)
				mob.location().show(mob,null,CMMsg.MSG_OK_ACTION,L("<S-NAME> float(s) gently into the pit!"));
			else
			{
				mob.location().show(mob,null,CMMsg.MSG_OK_ACTION,L("<S-NAME> hit(s) the pit floor with a THUMP!"));
				final int damage=CMLib.dice().roll(mob.phyStats().level(),3,1);
				CMLib.combat().postDamage(invoker(),mob,this,damage,CMMsg.MASK_MALICIOUS|CMMsg.TYP_JUSTICE,-1,null);
			}
			CMLib.commands().postLook(mob,true);
		}
	}

	@Override
	public MOB invoker()
	{
		if(invoker==null)
			return benefactor;
		return super.invoker();
	}

	@Override
	public int classificationCode()
	{
		return Ability.ACODE_TRAP;
	}

	@Override
	public void spring(final MOB target)
	{
		sprung=true;
		benefactor.setLocation(target.location());
		benefactor.basePhyStats().setLevel(target.phyStats().level());
		benefactor.recoverCharStats();
		benefactor.recoverPhyStats();
		switch(trapType())
		{
		case TRAP_GAS:
			gas(target);
			break;
		case TRAP_NEEDLE:
			needle(target);
			break;
		case TRAP_PIT_BLADE:
			if(affected instanceof Exit)
				fallInPit(target);
			else
				blade(target);
			break;
		case TRAP_SPELL:
			victimOfSpell(target);
			break;
		default:
			target.location().show(target,null,CMMsg.MSG_OK_ACTION,L("<S-NAME> trigger(s) a trap, but it appears to have misfired."));
			break;
		}

		if((getReset()>0)&&(getReset()<Integer.MAX_VALUE))
			CMLib.threads().startTickDown(this,Tickable.TICKID_TRAP_RESET,getReset());
		else
			unInvoke();
	}

	@Override
	public void resetTrap(final MOB mob)
	{
		if(sprung())
		{
			sprung=false;
			if(!isABomb())
				CMLib.threads().deleteTick(this, Tickable.TICKID_TRAP_RESET);
		}
	}

	@Override
	public void unInvoke()
	{
		if((trapType()==Trap.TRAP_PIT_BLADE)
		&&(affected instanceof Exit)
		&&(myPit!=null)
		&&(canBeUninvoked())
		&&(myPitUp!=null))
		{
			final Room R=myPitUp.getRoomInDir(Directions.UP);
			if((R!=null)&&(R.getRoomInDir(Directions.DOWN)==myPitUp))
			{
				R.rawDoors()[Directions.DOWN]=null;
				R.setRawExit(Directions.DOWN,null);
			}
			/**
			don't do this, cuz someone might still be down there.
			myPitUp.rawDoors()[Directions.UP]=null;
			myPitUp.getRawExit(Directions.UP]=null;
			myPitUp.rawDoors()[Directions.DOWN]=null;
			myPitUp.getRawExit(Directions.DOWN]=null;
			myPit.rawDoors()[Directions.UP]=null;
			myPit.getRawExit(Directions.UP]=null;
			*/
			if(myPit!=null)
				myPit.destroy();
			if(myPitUp!=null)
				myPitUp.destroy();
		}
		super.unInvoke();
	}

	@Override
	public boolean tick(final Tickable ticking, final int tickID)
	{
		if(!super.tick(ticking,tickID))
			return false;
		if(tickID==Tickable.TICKID_TRAP_RESET)
		{
			sprung=false;
			return false;
		}
		else
		if(tickID==Tickable.TICKID_TRAP_DESTRUCTION)
		{
			unInvoke();
			return false;
		}
		return true;
	}
}
