@Override
	public CMObject findCommand(MOB mob, List<String> commands)
	{
		if((mob==null)
		||(commands==null)
		||(mob.location()==null)
		||(commands.isEmpty()))
			return null;

		String firstWord=commands.get(0).toUpperCase();

		if((firstWord.length()>1)&&(!Character.isLetterOrDigit(firstWord.charAt(0))))
		{
			commands.add(1,commands.get(0).substring(1));
			commands.set(0,""+firstWord.charAt(0));
			firstWord=""+firstWord.charAt(0);
		}

		// first, exacting pass
		Command C=CMClass.findCommandByTrigger(firstWord,true);
		if((C!=null)
		&&(C.securityCheck(mob))
		&&(!CMSecurity.isCommandDisabled(CMClass.classID(C).toUpperCase())))
			return C;

		Ability A=getToEvoke(mob,new XVector<String>(commands));
		if((A!=null)
		&&(!CMSecurity.isAbilityDisabled(A.ID().toUpperCase())))
			return A;

		if(getAnEvokeWord(mob,firstWord)!=null)
			return null;

		Social social=CMLib.socials().fetchSocial(commands,true,true);
		if(social!=null)
			return social;

		for(int c=0;c<CMLib.channels().getNumChannels();c++)
		{
			final ChannelsLibrary.CMChannel chan=CMLib.channels().getChannel(c);
			if(chan.name().equalsIgnoreCase(firstWord))
			{
				C=CMClass.getCommand("Channel");
				if((C!=null)&&(C.securityCheck(mob)))
					return C;
			}
			else
			if(("NO"+chan.name()).equalsIgnoreCase(firstWord))
			{
				C=CMClass.getCommand("NoChannel");
				if((C!=null)&&(C.securityCheck(mob)))
					return C;
			}
		}

		for(final Enumeration<JournalsLibrary.CommandJournal> e=CMLib.journals().commandJournals();e.hasMoreElements();)
		{
			final JournalsLibrary.CommandJournal CMJ=e.nextElement();
			if(CMJ.NAME().equalsIgnoreCase(firstWord))
			{
				C=CMClass.getCommand("CommandJournal");
				if((C!=null)&&(C.securityCheck(mob)))
					return C;
			}
		}

		// second, inexacting pass
		for(final Enumeration<Ability> a=mob.allAbilities();a.hasMoreElements();)
		{
			A=a.nextElement();
			final HashSet<String> tried=new HashSet<String>();
			if((A!=null)&&(A.triggerStrings()!=null))
			{
				for(int t=0;t<A.triggerStrings().length;t++)
				{
					if((A.triggerStrings()[t].toUpperCase().startsWith(firstWord))
					&&(!tried.contains(A.triggerStrings()[t])))
					{
						final Vector<String> commands2=new XVector<String>(commands);
						commands2.setElementAt(A.triggerStrings()[t],0);
						final Ability A2=getToEvoke(mob,commands2);
						if((A2!=null)&&(!CMSecurity.isAbilityDisabled(A2.ID().toUpperCase())))
						{
							commands.set(0,A.triggerStrings()[t]);
							return A;
						}
					}
				}
			}
		}
		//commands comes inexactly after ables
		//because of CA, PR, etc..
		C=CMClass.findCommandByTrigger(firstWord,false);
		if((C!=null)
		&&(C.securityCheck(mob))
		&&(!CMSecurity.isCommandDisabled(CMClass.classID(C).toUpperCase())))
			return C;


		social=CMLib.socials().fetchSocial(commands,false,true);
		if(social!=null)
		{
			commands.set(0,social.baseName());
			return social;
		}

		for(int c=0;c<CMLib.channels().getNumChannels();c++)
		{
			final ChannelsLibrary.CMChannel chan=CMLib.channels().getChannel(c);
			if(chan.name().startsWith(firstWord))
			{
				commands.set(0,chan.name());
				C=CMClass.getCommand("Channel");
				if((C!=null)&&(C.securityCheck(mob)))
					return C;
			}
			else
			if(("NO"+chan.name()).startsWith(firstWord))
			{
				commands.set(0,"NO"+chan.name());
				C=CMClass.getCommand("NoChannel");
				if((C!=null)&&(C.securityCheck(mob)))
					return C;
			}
		}

		for(final Enumeration<JournalsLibrary.CommandJournal> e=CMLib.journals().commandJournals();e.hasMoreElements();)
		{
			final JournalsLibrary.CommandJournal CMJ=e.nextElement();
			if(CMJ.NAME().startsWith(firstWord))
			{
				C=CMClass.getCommand("CommandJournal");
				if((C!=null)&&(C.securityCheck(mob)))
					return C;
			}
		}
		return null;
	}