package com.planet_ink.coffee_mud.Abilities.Songs;
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 2003-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 Song_Ode extends Song
{
	@Override
	public String ID()
	{
		return "Song_Ode";
	}

	private final static String	localizedName	= CMLib.lang().L("Ode");

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

	@Override
	public int abstractQuality()
	{
		return Ability.QUALITY_BENEFICIAL_OTHERS;
	}

	public MOB							whom		= null;
	public Hashtable<Integer, Integer>	benefits	= null;

	protected String					song		= null;
	protected Hashtable<String, String>	songs		= null;
	protected StringBuffer				trail		= null;

	@Override
	protected String songOf()
	{
		return (whom == null) ? L("Ode") : (L("Ode to ") + whom.name());
	}

	@Override
	protected boolean skipStandardSongTick()
	{
		return (song == null);
	}

	protected static final Hashtable<String, String[]>	cmds	= new Hashtable<String, String[]>();
	protected static final String[][] stuff={
		{""+CMMsg.TYP_EAT,"s","h","<O-NAME> knows our hunger pains!"},
		{""+CMMsg.TYP_GET,"cs",""+CharStats.STAT_STRENGTH,"The strength of <O-NAME> astounds us all!"},
		{""+CMMsg.TYP_GAS,"ca",""+CharStats.STAT_SAVE_GAS,"<O-NAME> has gas, and saves us from it!"},
		{""+CMMsg.TYP_FIRE,"ca",""+CharStats.STAT_SAVE_FIRE,"We learn from firey <O-NAME> to keep our cool!"},
		{""+CMMsg.TYP_DRINK,"s","t","<O-NAME> quenches our thirst for health!"},
		{""+CMMsg.TYP_DISEASE,"ca",""+CharStats.STAT_SAVE_DISEASE,"<O-NAME> worries so about our health!"},
		{""+CMMsg.TYP_UNDEAD,"ca",""+CharStats.STAT_SAVE_UNDEAD,"<O-NAME>, the deadly one, fills our hearts with life!"},
		{""+CMMsg.TYP_COLD,"ca",""+CharStats.STAT_SAVE_COLD,"Cold-hearted <O-NAME> has shown us how to keep warm!"},
		{""+CMMsg.TYP_CAST_SPELL,"ca",""+CharStats.STAT_SAVE_MAGIC,"<O-NAME>, the enchanting, helps us avoid magic spells!"},
		{""+CMMsg.TYP_ACID,"ca",""+CharStats.STAT_SAVE_ACID,"<O-NAME> drips with warnings about acid!"},
		{""+CMMsg.TYP_ELECTRIC,"ca",""+CharStats.STAT_SAVE_ELECTRIC,"The electric one, <O-NAME>, has shown us how to stay grounded!"},
		{""+CMMsg.TYP_LOOK,"es",""+(PhyStats.CAN_SEE_DARK|PhyStats.CAN_SEE_HIDDEN),"<O-NAME> is ever watchful, and has opened our eyes!"},
		{""+CMMsg.TYP_EXAMINE,"es",""+(PhyStats.CAN_SEE_BONUS|PhyStats.CAN_SEE_METAL),"<O-NAME> is a carefuly observer, and never misses the finest detail!"},
		{""+CMMsg.TYP_JUSTICE,"ca",""+CharStats.STAT_SAVE_JUSTICE,"The dreadful <O-NAME> shows us the way out of danger!"},
		{""+CMMsg.TYP_MIND,"ca",""+CharStats.STAT_SAVE_MIND,"<O-NAME>, the deceiver, reminds us to keep our wits!"},
		{""+CMMsg.TYP_PARALYZE,"ca",""+CharStats.STAT_SAVE_PARALYSIS,"<O-NAME> paralyzes <O-HIS-HER> enemies, that we may stay free!"},
		{""+CMMsg.TYP_POISON,"ca",""+CharStats.STAT_SAVE_POISON,"<O-NAME> is poison to others, but protects <O-HIS-HER> friends!"},
		{""+CMMsg.TYP_SLEEP,"s","i","<O-NAME>, the sleepy, reminds us to stay fit and rested!"},
		{""+CMMsg.TYP_SIT,"s","m","<O-NAME>, the contemplative, teaches us <O-HIS-HER> power!"},
		{""+CMMsg.TYP_SPEAK,"cs",""+CharStats.STAT_WISDOM,"<O-NAME>, the chatty one, fills us with <O-HIS-HER> wisdom!"},
		{""+CMMsg.TYP_WATER,"ca",""+CharStats.STAT_SAVE_WATER,"<O-NAME>, the wet one, keeps us so dry!"},
		{""+CMMsg.TYP_WEAPONATTACK,"e","a","<O-NAME> the viscious has shown us to take arms!"},
		{""+CMMsg.TYP_LEAVE,"s","v","<O-NAME>, the wanderer, gives us our second wind!"}
	};

	public String composition()
	{
		String comp="";
		if(trail!=null)
		{
			String t=trail.toString();
			final int[] counts=new int[stuff.length];
			int x=t.indexOf(';');
			while(x>=0)
			{
				final int q=CMath.s_int(t.substring(0,x));
				t=t.substring(x+1);
				if(q>=0)
				{
					for(int i=0;i<stuff.length;i++)
					{
						if(CMath.s_int(stuff[i][0])==q)
							counts[i]++;
					}
				}
				x=t.indexOf(';');
			}
			int wa=-1;
			for(int i=0;i<stuff.length;i++)
			{
				if(CMath.s_int(stuff[i][0])==CMMsg.TYP_WEAPONATTACK)
				{
					wa=i;
					break;
				}

			}
			if(wa>=0)
				counts[wa]=counts[wa]/25;

			final Vector<Integer> V=new Vector<Integer>();
			while(V.size()<counts.length)
			{
				int high=-1;
				int which=-1;
				for(int i=0;i<counts.length;i++)
				{
					if((counts[i]>high)&&(!V.contains(Integer.valueOf(i))))
					{
						high=counts[i];
						which=i;
					}
				}
				if(which>=0)
					V.addElement(Integer.valueOf(which));
			}
			final Vector<Integer> V2=new Vector<Integer>();
			for(int i=0;i<3;i++)
			{
				final Integer ref=V.elementAt(i);
				Integer which=null;
				while((which==null)||(V2.contains(which)))
				{
					final Integer w=V.elementAt(CMLib.dice().roll(1,V.size(),-1));
					if(counts[w.intValue()]==counts[ref.intValue()])
						which=w;
				}
				V2.addElement(which);
				comp+=which.intValue()+";"+counts[which.intValue()]+";";
			}
		}
		return comp;
	}

	@Override
	public void affectPhyStats(final Physical affected, final PhyStats affectableStats)
	{
		if((whom!=null)&&(song!=null))
		{
			final Hashtable<Integer,Integer> H=getSongBenefits(song);
			for(final Enumeration<Integer> e=H.keys();e.hasMoreElements();)
			{
				final Integer I=e.nextElement();
				final String[] chk=stuff[I.intValue()];
				if((chk!=null)&&(chk[1].startsWith("e")))
				{
					int ticks=H.get(I).intValue();
					if(ticks<=0)
						ticks=1;
					switch(chk[2].charAt(0))
					{
					case 'a':
						if(ticks>25)
							ticks=25;
						affectableStats.setAttackAdjustment(affectableStats.attackAdjustment()+ticks+getXLEVELLevel(invoker()));
						break;
					default:
						break;
					}
				}
			}
		}
	}

	@Override
	public void affectCharStats(final MOB affected, final CharStats affectableStats)
	{
		if((whom!=null)&&(song!=null))
		{
			final Hashtable<Integer,Integer> H=getSongBenefits(song);
			for(final Enumeration<Integer> e=H.keys();e.hasMoreElements();)
			{
				final Integer I=e.nextElement();
				final String[] chk=stuff[I.intValue()];
				if((chk!=null)&&(chk[1].startsWith("c")))
				{
					int ticks=H.get(I).intValue();
					if(ticks>50)
						ticks=50;
					if(ticks<=0)
						ticks=1;
					final int stat=CMath.s_int(chk[2]);
					if(CharStats.CODES.isBASE(stat))
					{
						if(ticks>5)
							ticks=5;
					}
					affectableStats.setStat(stat,affectableStats.getStat(stat)+ticks+getXLEVELLevel(invoker()));
				}
			}
		}
	}

	@Override
	public void affectCharState(final MOB affected, final CharState affectableStats)
	{
		if((whom!=null)&&(song!=null))
		{
			final Hashtable<Integer,Integer> H=getSongBenefits(song);
			for(final Enumeration<Integer> e=H.keys();e.hasMoreElements();)
			{
				final Integer I=e.nextElement();
				final String[] chk=stuff[I.intValue()];
				if((chk!=null)&&(chk[1].startsWith("s")))
				{
					int ticks=H.get(I).intValue();
					if(ticks>50)
						ticks=50;
					if(ticks<=0)
						ticks=1;
					switch(chk[2].charAt(0))
					{
					case 'h':
						affectableStats.setHunger(affectableStats.getHunger() + ticks + getXLEVELLevel(invoker()));
						break;
					case 't':
						affectableStats.setThirst(affectableStats.getThirst() + ticks + getXLEVELLevel(invoker()));
						break;
					case 'v':
						affectableStats.setMovement(affectableStats.getMovement() + ticks + getXLEVELLevel(invoker()));
						break;
					case 'm':
						affectableStats.setMana(affectableStats.getMana() + ticks + getXLEVELLevel(invoker()));
						break;
					case 'i':
						affectableStats.setHitPoints(affectableStats.getHitPoints() + ticks + getXLEVELLevel(invoker()));
						break;
					}
				}
			}
		}
	}

	public void ensureCmds()
	{
		synchronized(cmds)
		{
			if(cmds.size()==0)
			{
				for (final String[] element : stuff)
					cmds.put(element[0],element);
			}
		}
	}

	@Override
	public void executeMsg(final Environmental myHost, final CMMsg msg)
	{
		if((whom!=null)
		&&(song==null)
		&&(msg.amISource(whom))
		&&(CMLib.flags().canBeSeenBy(whom,invoker())))
		{
			if(trail==null)
				trail=new StringBuffer("");
			ensureCmds();
			if(cmds.containsKey(""+msg.sourceMinor()))
				trail.append(msg.sourceMinor()+";");
		}
		super.executeMsg(myHost,msg);
	}

	@Override
	public int castingQuality(final MOB mob, final Physical target)
	{
		if(mob!=null)
		{
			if((mob.isMonster())&&(mob.isInCombat()))
				return Ability.QUALITY_INDIFFERENT;
		}
		return super.castingQuality(mob,target);
	}

	@Override
	public boolean tick(final Tickable ticking, final int tickID)
	{
		final MOB mob=(MOB)affected;
		if(mob==null)
			return false;
		if(song==null)
		{
			if((whom==null)
			||(commonRoomSet==null)
			||(!commonRoomSet.contains(whom.location()))
			||(CMLib.flags().isSleeping(invoker))
			||(!CMLib.flags().canBeSeenBy(whom,invoker)))
				return unSingMe(mob,null, false);
		}

		if((whom!=null)&&(song!=null)&&(affected==invoker())
		   &&(CMLib.dice().rollPercentage()<10))
		{
			final Hashtable<Integer,Integer> H=getSongBenefits(song);
			final Vector<Integer> V=new Vector<Integer>();
			for(final Enumeration<Integer> e=H.keys();e.hasMoreElements();)
				V.addElement(e.nextElement());
			final Integer I=V.elementAt(CMLib.dice().roll(1,V.size(),-1));
			final String[] chk=stuff[I.intValue()];
			invoker().location().show(invoker(),this,whom,CMMsg.MSG_SPEAK,L("<S-NAME> sing(s) '@x1'.",chk[3]));
		}

		if(!super.tick(ticking,tickID))
			return false;

		return true;
	}

	public Hashtable<Integer,Integer> getSongBenefits(String s)
	{
		if(benefits!=null)
			return benefits;
		benefits=new Hashtable<Integer,Integer>();

		int x=s.indexOf(';');
		while(x>=0)
		{
			final int code=CMath.s_int(s.substring(0,x));
			s=s.substring(x+1);
			x=s.indexOf(';');
			if(x>=0)
			{
				final int tick=CMath.s_int(s.substring(0,x));
				s=s.substring(x+1);
				benefits.put(Integer.valueOf(code),Integer.valueOf(tick));
			}
			x=s.indexOf(';');
		}
		return benefits;
	}

	@Override
	public String text()
	{
		final StringBuffer x=new StringBuffer("");
		for(final Enumeration<String> e=getSongs().keys();e.hasMoreElements();)
		{
			final String key=e.nextElement();
			final String notkey=getSongs().get(key);
			x.append(key+"|~|"+notkey+"[|]");
		}
		miscText=x.toString();
		return x.toString();
	}

	public Hashtable<String,String> getSongs()
	{
		if(songs!=null)
			return songs;
		String t=miscText;
		int x=t.indexOf("|~|");
		songs=new Hashtable<String,String>();
		while(x>=0)
		{
			final String n=t.substring(0,x);
			t=t.substring(x+3);
			x=t.indexOf("[|]");
			String y="";
			if(x>=0)
			{
				y=t.substring(0,x);
				t=t.substring(x+3);
				songs.put(n,y);
			}
			x=t.indexOf("|~|");
		}
		return songs;
	}

	@Override
	public boolean invoke(final MOB mob, final List<String> commands, final Physical givenTarget, final boolean auto, final int asLevel)
	{
		timeOut=0;
		if(auto)
			return false;

		final Hashtable<String,String> H=getSongs();
		if(commands.size()==0)
		{
			final Song_Ode A=(Song_Ode)mob.fetchEffect(ID());
			if((A!=null)&&(A.whom!=null)&&(A.song==null))
			{
				final String str=L("^S<S-NAME> finish(es) composing the @x1.^?",A.songOf());
				final CMMsg msg=CMClass.getMsg(mob,null,this,(auto?CMMsg.MASK_ALWAYS:0)|CMMsg.MSG_DELICATE_SMALL_HANDS_ACT,str);
				if(mob.location().okMessage(mob,msg))
				{
					mob.location().send(mob,msg);
					mob.delEffect(A);
					getSongs().put(A.whom.name(),A.composition());
					whom=null;
					return true;
				}
				return false;
			}

			final StringBuffer str=new StringBuffer("");
			for(final Enumeration<String> e=H.keys();e.hasMoreElements();)
				str.append(e.nextElement()+" ");
			mob.tell(L("Compose or sing an ode about whom?"));
			if(str.length()>0)
				mob.tell(L("You presently have odes written about: @x1.",str.toString().trim()));
			return false;
		}
		String name=CMParms.combine(commands,0);
		for(final Enumeration<String> e=H.keys();e.hasMoreElements();)
		{
			final String key=e.nextElement();
			if(CMLib.english().containsString(key,name))
			{
				invoker=mob;
				originRoom=mob.location();
				commonRoomSet=getInvokerScopeRoomSet(0);
				name=key;
				song=H.get(name);
				benefits=null;
				whom=mob.location().fetchInhabitant(name);
				if((whom==null)||(!whom.name().equals(name)))
					whom=CMLib.players().getPlayer(name);
				if((whom==null)||(!whom.name().equals(name)))
				{
					whom=CMClass.getMOB("StdMOB");
					whom.setName(name);
					whom.setLocation(mob.location());
				}
				return super.invoke(mob,commands,givenTarget,auto,asLevel);
			}
		}

		final MOB target=getTarget(mob,commands,givenTarget);
		if(target==null)
			return false;
		if(target==mob)
		{
			mob.tell(L("You may not compose an ode about yourself!"));
			return false;
		}

		final boolean success=proficiencyCheck(mob,0,auto);
		if(success)
		{
			unSingAll(mob,mob,false,false);
			invoker=mob;
			originRoom=mob.location();
			commonRoomSet=getInvokerScopeRoomSet(0);
			whom=target;
			final String str=L("^S<S-NAME> begin(s) to compose an @x1.^?",songOf());
			final CMMsg msg=CMClass.getMsg(mob,null,this,(auto?CMMsg.MASK_ALWAYS:0)|CMMsg.MSG_DELICATE_SMALL_HANDS_ACT,str);
			if(mob.location().okMessage(mob,msg))
			{
				mob.location().send(mob,msg);
				invoker=mob;
				final Song_Ode newOne=(Song_Ode)copyOf();
				newOne.whom=target;
				newOne.trail=new StringBuffer("");
				newOne.song=null;
				mob.addEffect(newOne);
			}
		}
		else
			mob.location().show(mob,null,CMMsg.MSG_NOISE,L("<S-NAME> lose(s) <S-HIS-HER> inspiration."));
		return success;
	}
}
