package com.planet_ink.coffee_mud.Commands;
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.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;

import java.util.*;

/*
   Copyright 2004-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 Train extends StdCommand
{
	public Train()
	{
	}

	private final String[] access=I(new String[]{"TRAIN","TR","TRA"});
	@Override
	public String[] getAccessWords()
	{
		return access;
	}

	private static final int	TRAIN_HITPOINTS	= 101;
	private static final int	TRAIN_MANA		= 102;
	private static final int	TRAIN_MOVE		= 103;
	private static final int	TRAIN_GAIN		= 104;
	private static final int	TRAIN_PRACTICES	= 105;
	private static final int	TRAIN_CCLASS	= 106;

	public static List<String> getAllPossibleThingsToTrainFor()
	{
		final List<String> V=new Vector<String>();
		V.add("HIT POINTS");
		V.add("MANA");
		V.add("MOVEMENT");
		V.add("GAIN");
		V.add("PRACTICES");
		for(final int i: CharStats.CODES.BASECODES())
			V.add(CharStats.CODES.DESC(i));
		for(final Enumeration<CharClass> c=CMClass.charClasses();c.hasMoreElements();)
		{
			final CharClass C=c.nextElement();
			if(CMLib.login().isAvailableCharClass(C))
				V.add(C.name().toUpperCase().trim());
		}
		return V;
	}

	@Override
	public boolean execute(final MOB mob, final List<String> commands, final int metaFlags)
		throws java.io.IOException
	{
		final List<String> origCmds=new StringXVector(commands);
		if(commands.size()<2)
		{
			CMLib.commands().postCommandFail(mob,origCmds,L("You have @x1 training sessions. Enter HELP TRAIN for more information.",""+mob.getTrains()));
			return false;
		}
		commands.remove(0);
		String teacherName=null;
		if(commands.size()>1)
		{
			teacherName=commands.get(commands.size()-1);
			if(teacherName.length()>1)
				commands.remove(commands.size()-1);
			else
				teacherName=null;
		}

		final String abilityName=CMParms.combine(commands,0).toUpperCase();
		final StringBuffer thingsToTrainFor=new StringBuffer("");
		for(final int i: CharStats.CODES.BASECODES())
			thingsToTrainFor.append(CharStats.CODES.DESC(i)+", ");

		int trainsRequired=1;
		int abilityCode=mob.baseCharStats().getStatCode(abilityName);
		int curStat=-1;
		if((abilityCode>=0)&&(CharStats.CODES.isBASE(abilityCode)))
		{
			curStat=mob.baseCharStats().getRacialStat(mob, abilityCode);
			trainsRequired=CMLib.login().getTrainingCost(mob, abilityCode, false);
			if(trainsRequired<0)
				return false;
		}
		else
			abilityCode=-1;
		CharClass theClass=null;
		if((!CMSecurity.isDisabled(CMSecurity.DisFlag.CLASSTRAINING))&&(abilityCode<0))
		{
			for(final Enumeration<CharClass> c=CMClass.charClasses();c.hasMoreElements();)
			{
				final CharClass C=c.nextElement();
				int classLevel=mob.charStats().getClassLevel(C);
				int trainCost = CMProps.getIntVar(CMProps.Int.CLASSSWITCHCOST);
				if(classLevel<0)
				{
					classLevel=0;
					trainCost = CMProps.getIntVar(CMProps.Int.CLASSTRAINCOST);
				}
				if((C.name().toUpperCase().startsWith(abilityName.toUpperCase()))
				||(C.name(classLevel).toUpperCase().startsWith(abilityName.toUpperCase())))
				{
					if((C.qualifiesForThisClass(mob,false))
					&&(CMLib.login().isAvailableCharClass(C)))
					{
						abilityCode=TRAIN_CCLASS;
						theClass=C;
						trainsRequired=trainCost;
						if(trainsRequired<0)
							return false;
					}
					break;
				}
				else
				if((C.qualifiesForThisClass(mob,true))
				&&(CMLib.login().isAvailableCharClass(C)))
					thingsToTrainFor.append(C.name()+", ");
			}
		}

		if(abilityCode<0)
		{
			if("HIT POINTS".startsWith(abilityName.toUpperCase()))
				abilityCode=TRAIN_HITPOINTS;
			else
			if("MANA".startsWith(abilityName.toUpperCase()))
				abilityCode=TRAIN_MANA;
			else
			if("MOVE".startsWith(abilityName.toUpperCase()))
				abilityCode=TRAIN_MOVE;
			else
			if("GAIN".startsWith(abilityName.toUpperCase()))
				abilityCode=TRAIN_GAIN;
			else
			if("PRACTICES".startsWith(abilityName.toUpperCase()))
				abilityCode=TRAIN_PRACTICES;
			else
			{
				if(abilityCode<0)
				{
					CMLib.commands().postCommandFail(mob,origCmds,L("You can't train for '@x1'. Try @x2HIT POINTS, MANA, MOVE, GAIN, or PRACTICES.",abilityName,thingsToTrainFor.toString()));
					return false;
				}
			}
		}

		if(abilityCode==TRAIN_GAIN)
		{
			if(mob.getPractices()<7)
			{
				CMLib.commands().postCommandFail(mob,origCmds,L("You don't seem to have enough practices to do that."));
				return false;
			}
		}
		else
		if((mob.getTrains()<=0)&&(trainsRequired>0))
		{
			CMLib.commands().postCommandFail(mob,origCmds,L("You don't seem to have enough training sessions to do that."));
			return false;
		}
		else
		if(mob.getTrains()<trainsRequired)
		{
			if(trainsRequired>1)
			{
				CMLib.commands().postCommandFail(mob,origCmds,L("Training that ability further will require @x1 training points.",""+trainsRequired));
				return false;
			}
			else
			if(trainsRequired==1)
			{
				CMLib.commands().postCommandFail(mob,origCmds,L("Training that ability further will require @x1 training points.",""+trainsRequired));
				return false;
			}
		}

		MOB teacher=null;
		if(teacherName!=null)
			teacher=mob.location().fetchInhabitant(teacherName);
		if(teacher==null)
		{
			for(int i=0;i<mob.location().numInhabitants();i++)
			{
				final MOB possTeach=mob.location().fetchInhabitant(i);
				if((possTeach!=null)&&(possTeach!=mob))
				{
					teacher=possTeach;
					break;
				}
			}
		}
		if((teacher==null)||(!CMLib.flags().canBeSeenBy(teacher,mob)))
		{
			mob.tell(teacher,null,null,L("You can't see <S-NAME>!"));
			return false;
		}
		if(!CMLib.flags().canBeSeenBy(mob,teacher))
		{
			mob.tell(teacher,null,null,L("<S-NAME> can't see you!"));
			return false;
		}
		if(teacher==mob)
		{
			CMLib.commands().postCommandFail(mob,origCmds,L("You cannot train with yourself!"));
			return false;
		}
		if(teacher.isAttributeSet(MOB.Attrib.NOTEACH))
		{
			CMLib.commands().postCommandFail(mob,origCmds,L("@x1 is refusing to teach right now.",teacher.name()));
			return false;
		}
		if(mob.isAttributeSet(MOB.Attrib.NOTEACH))
		{
			CMLib.commands().postCommandFail(mob,origCmds,L("You are refusing training at this time."));
			return false;
		}
		if(CMLib.flags().isSleeping(mob)||CMLib.flags().isSitting(mob))
		{
			CMLib.commands().postCommandFail(mob,origCmds,L("You need to stand up for your training."));
			return false;
		}
		if(CMLib.flags().isSleeping(teacher)||CMLib.flags().isSitting(teacher))
		{
			if(teacher.isMonster())
				CMLib.commands().postStand(teacher,true, false);
			if(CMLib.flags().isSleeping(teacher)||CMLib.flags().isSitting(teacher))
			{
				CMLib.commands().postCommandFail(mob,origCmds,L("@x1 looks a bit too relaxed to train with you.",teacher.name()));
				return false;
			}
		}
		if(mob.isInCombat())
		{
			CMLib.commands().postCommandFail(mob,origCmds,L("Not while you are fighting!"));
			return false;
		}
		if(teacher.isInCombat())
		{
			CMLib.commands().postCommandFail(mob,origCmds,L("Your teacher seems busy right now."));
			return false;
		}

		if(abilityCode==TRAIN_CCLASS)
		{
			boolean canTeach=false;
			for(int c=0;c<teacher.charStats().numClasses();c++)
			{
				if(teacher.charStats().getMyClass(c).baseClass().equals(mob.charStats().getCurrentClass().baseClass()))
					canTeach=true;
			}
			if((!canTeach)
			&&(teacher.charStats().getClassLevel(theClass)<1))
			{
				if((!CMProps.getVar(CMProps.Str.MULTICLASS).startsWith("MULTI"))
				&&(!CMProps.getVar(CMProps.Str.MULTICLASS).endsWith("MULTI")))
				{
					final CharClass C=CMClass.getCharClass(mob.charStats().getCurrentClass().baseClass());
					final String baseClassName=(C!=null)?C.name():mob.charStats().getCurrentClass().baseClass();
					CMLib.commands().postCommandFail(mob,origCmds,L("You can only learn that from another @x1.",baseClassName));
				}
				else
				if(theClass!=null)
				{
					int classLevel=mob.charStats().getClassLevel(theClass);
					if(classLevel<0)
						classLevel=0;
					CMLib.commands().postCommandFail(mob,origCmds,L("You can only learn that from another @x1.",theClass.name(classLevel)));
				}
				return false;
			}
		}

		if(abilityCode<100)
		{
			final int teachStat=teacher.charStats().getStat(abilityCode);
			if(curStat>=teachStat)
			{
				CMLib.commands().postCommandFail(mob,origCmds,L("You can only train with someone whose score is higher than yours."));
				return false;
			}
			curStat=mob.baseCharStats().getStat(abilityCode);
		}

		final CMMsg msg=CMClass.getMsg(teacher,mob,null,CMMsg.MSG_NOISYMOVEMENT,L("<S-NAME> train(s) with <T-NAMESELF>."));
		if(!mob.location().okMessage(mob,msg))
			return false;
		mob.location().send(mob,msg);
		switch(abilityCode)
		{
		case CharStats.STAT_STRENGTH:
			mob.tell(L("You feel stronger!"));
			mob.baseCharStats().setStat(CharStats.STAT_STRENGTH,curStat+1);
			mob.recoverCharStats();
			mob.setTrains(mob.getTrains()-trainsRequired);
			break;
		case CharStats.STAT_INTELLIGENCE:
			mob.tell(L("You feel smarter!"));
			mob.baseCharStats().setStat(CharStats.STAT_INTELLIGENCE,curStat+1);
			mob.recoverCharStats();
			mob.setTrains(mob.getTrains()-trainsRequired);
			break;
		case CharStats.STAT_DEXTERITY:
			mob.tell(L("You feel more dextrous!"));
			mob.baseCharStats().setStat(CharStats.STAT_DEXTERITY,curStat+1);
			mob.recoverCharStats();
			mob.setTrains(mob.getTrains()-trainsRequired);
			break;
		case CharStats.STAT_CONSTITUTION:
			mob.tell(L("You feel healthier!"));
			mob.baseCharStats().setStat(CharStats.STAT_CONSTITUTION,curStat+1);
			mob.recoverCharStats();
			mob.setTrains(mob.getTrains()-trainsRequired);
			break;
		case CharStats.STAT_CHARISMA:
			mob.tell(L("You feel more charismatic!"));
			mob.baseCharStats().setStat(CharStats.STAT_CHARISMA,curStat+1);
			mob.recoverCharStats();
			mob.setTrains(mob.getTrains()-trainsRequired);
			break;
		case CharStats.STAT_WISDOM:
			mob.tell(L("You feel wiser!"));
			mob.baseCharStats().setStat(CharStats.STAT_WISDOM,curStat+1);
			mob.recoverCharStats();
			mob.setTrains(mob.getTrains()-trainsRequired);
			break;
		case TRAIN_HITPOINTS:
			mob.tell(L("You feel even healthier!"));
			mob.baseState().setHitPoints(mob.baseState().getHitPoints()+10);
			mob.maxState().setHitPoints(mob.maxState().getHitPoints()+10);
			mob.curState().setHitPoints(mob.curState().getHitPoints()+10);
			mob.setTrains(mob.getTrains()-1);
			break;
		case TRAIN_MANA:
			mob.tell(L("You feel more powerful!"));
			mob.baseState().setMana(mob.baseState().getMana()+20);
			mob.maxState().setMana(mob.maxState().getMana()+20);
			mob.curState().setMana(mob.curState().getMana()+20);
			mob.setTrains(mob.getTrains()-1);
			break;
		case TRAIN_MOVE:
			mob.tell(L("You feel more rested!"));
			mob.baseState().setMovement(mob.baseState().getMovement()+20);
			mob.maxState().setMovement(mob.maxState().getMovement()+20);
			mob.curState().setMovement(mob.curState().getMovement()+20);
			mob.setTrains(mob.getTrains()-1);
			break;
		case TRAIN_GAIN:
			mob.tell(L("You feel more trainable!"));
			mob.setTrains(mob.getTrains()+1);
			mob.setPractices(mob.getPractices()-7);
			break;
		case TRAIN_PRACTICES:
			mob.tell(L("You feel more educatable!"));
			mob.setTrains(mob.getTrains()-1);
			mob.setPractices(mob.getPractices()+5);
			break;
		default:
			if(CMParms.contains(CharStats.CODES.BASECODES(), abilityCode))
			{
				mob.tell(L("You feel more @x1!",CharStats.CODES.NAME(abilityCode)));
				mob.baseCharStats().setStat(abilityCode,curStat+1);
				mob.recoverCharStats();
				mob.setTrains(mob.getTrains()-trainsRequired);
			}
			break;
		case TRAIN_CCLASS:
			if(theClass!=null)
			{
				int classLevel=mob.charStats().getClassLevel(theClass);
				if(classLevel<0)
					classLevel=0;
				mob.tell(L("You have undergone @x1 training!",theClass.name(classLevel)));
				mob.setTrains(mob.getTrains()-trainsRequired);
				mob.baseCharStats().getCurrentClass().endCharacter(mob);
				mob.baseCharStats().setCurrentClass(theClass);
				if((!mob.isMonster())&&(mob.soulMate()==null))
					CMLib.coffeeTables().bump(mob,CoffeeTableRow.STAT_CLASSCHANGE);
				mob.recoverCharStats();
				mob.charStats().getCurrentClass().startCharacter(mob,false,true);
			}
			break;
		}
		return false;
	}

	@Override
	public double combatActionsCost(final MOB mob, final List<String> cmds)
	{
		return CMProps.getCommandCombatActionCost(ID());
	}

	@Override
	public double actionsCost(final MOB mob, final List<String> cmds)
	{
		return CMProps.getCommandActionCost(ID());
	}

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