package com.planet_ink.coffee_mud.Abilities.Common;
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.Common.CraftingSkill.CraftParms;
import com.planet_ink.coffee_mud.Abilities.Common.CraftingSkill.CraftingActivity;
import com.planet_ink.coffee_mud.Abilities.Common.CraftingSkill.EnhancedExpertise;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.ItemCraftor.CraftorType;
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.AchievementLibrary;
import com.planet_ink.coffee_mud.Libraries.interfaces.ExpertiseLibrary;
import com.planet_ink.coffee_mud.Libraries.interfaces.ListingLibrary;
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 2002-2023 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 Cooking extends EnhancedCraftingSkill implements ItemCraftor
{
	@Override
	public String ID()
	{
		return "Cooking";
	}

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

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

	private static final String[]	triggerStrings	= I(new String[] { "COOK", "COOKING" });

	@Override
	public String[] triggerStrings()
	{
		return triggerStrings;
	}

	@Override
	public CraftorType getCraftorType()
	{
		return CraftorType.Consumables;
	}

	public String cookWordShort()
	{
		return "cook";
	}

	public String cookWord()
	{
		return "cooking";
	}

	public boolean honorHerbs()
	{
		return true;
	}

	public boolean requireFire()
	{
		return true;
	}

	public boolean requireLid()
	{
		return false;
	}

	@Override
	public String supportedResourceString()
	{
		return "MISC";
	}

	@Override
	public int classificationCode()
	{
		return Ability.ACODE_COMMON_SKILL | Ability.DOMAIN_EPICUREAN;
	}

	@Override
	public String getRecipeFormat()
	{
		return
		"ITEM_NAME\tFOOD_DRINK||RESOURCE_NAME\tSMELL_LIST||CODED_SPELL_LIST\tITEM_LEVEL\t"
		+"RESOURCE_OR_KEYWORD\tOPTIONAL_AMOUNT_REQUIRED\t"
		+"RESOURCE_OR_KEYWORD\tOPTIONAL_AMOUNT_REQUIRED\t"
		+"RESOURCE_OR_KEYWORD\tOPTIONAL_AMOUNT_REQUIRED\t"
		+"RESOURCE_OR_KEYWORD\tOPTIONAL_AMOUNT_REQUIRED\t"
		+"RESOURCE_OR_KEYWORD\tOPTIONAL_AMOUNT_REQUIRED\t"
		+"RESOURCE_OR_KEYWORD\tOPTIONAL_AMOUNT_REQUIRED\t"
		+"RESOURCE_OR_KEYWORD\tOPTIONAL_AMOUNT_REQUIRED";
	}

	public static int	RCP_FINALFOOD	= 0;
	public static int	RCP_FOODDRINK	= 1;
	public static int	RCP_BONUSSPELL	= 2;
	public static int	RCP_LEVEL		= 3;
	public static int	RCP_MAININGR	= 4;
	public static int	RCP_MAINAMNT	= 5;

	protected Container				cookingPot			= null;
	protected String				finalDishName		= null;
	protected int					finalAmount			= 0;
	protected List<String>			finalRecipe			= null;
	protected Map<String, Integer>	oldPotContents		= null;
	protected String				defaultFoodSound	= "sizzle.wav";
	protected String				defaultDrinkSound	= "liquid.wav";

	public Cooking()
	{
		super();
		displayText=L("You are @x1...",cookWord());
		verb=cookWord();
	}

	protected int getDuration(final MOB mob, final int level)
	{
		return getDuration(40,mob,level,5);
	}

	protected boolean isMineForCooking(final MOB mob, final Container cooking)
	{
		for(int a=0;a<mob.numEffects();a++)
		{
			final Ability A=mob.fetchEffect(a);
			if((A instanceof Cooking) && (((Cooking)A).cookingPot==cooking) && (A!=this))
				return false;
		}
		if(mob.isMine(cooking))
			return true;
		if((mob.location()==cooking.owner())
		&&((CMLib.flags().isOnFire(cooking))
			||(!requireFire())
			||((cooking.container()!=null)&&(CMLib.flags().isOnFire(cooking.container())))))
		   return true;
		return false;
	}

	protected boolean meetsLidRequirements(final MOB mob, final Container cooking)
	{
		if(!requireLid())
			return true;
		if((cooking.hasADoor())&&(!cooking.isOpen()))
			return true;
		if((cooking.container()!=null)
		&&(cooking.container().hasADoor())
		&&(!cooking.container().isOpen()))
		   return true;
		return false;
	}

	@Override
	public boolean okMessage(final Environmental myHost, final CMMsg msg)
	{
		if(((msg.target()==cookingPot)||(msg.tool()==cookingPot))
		&&(CMath.bset(msg.targetMajor(), CMMsg.MASK_HANDS))
		&&(!CMath.bset(msg.targetMajor(), CMMsg.MASK_ALWAYS))
		&&(requireFire())
		&&(affected instanceof MOB))
		{
			msg.source().tell(L("Ouch! That's HOT!"));
			return false;
		}
		return super.okMessage(myHost, msg);
	}

	protected void stirThePot(final MOB mob)
	{
		if(buildingI!=null)
		{
			if((tickUp % 5)==1)
			{
				final Room R=mob.location();
				if(R==activityRoom)
				{
					R.show(mob,cookingPot,buildingI,CMMsg.MASK_ALWAYS|getActivityMessageType(),
							L("<S-NAME> taste-test(s) the <O-NAME> in <T-NAME>."));
				}
			}
		}
	}

	@Override
	public boolean tick(final Tickable ticking, final int tickID)
	{
		if((affected!=null)
		&&(affected instanceof MOB)
		&&(tickID==Tickable.TICKID_MOB))
		{
			final MOB mob=(MOB)affected;
			if((cookingPot==null)
			||(buildingI==null)
			||(finalRecipe==null)
			||(finalAmount<=0)
			||(!isMineForCooking(mob,cookingPot))
			||(!meetsLidRequirements(mob,cookingPot))
			||(!contentsSame(potContents(cookingPot),oldPotContents))
			||(requireFire()&&(getRequiredFire(mob,0)==null)))
			{
				aborted=true;
				unInvoke();
			}
			else
			if(tickUp==0)
			{
				commonTelL(mob,"You start @x1 up some @x2.",cookWord(),finalDishName);
				displayText=L("You are @x1 @x2",cookWord(),finalDishName);
				verb=cookWord()+" "+finalDishName;
			}
		}
		return super.tick(ticking,tickID);
	}

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

	@Override
	public String getRecipeFilename()
	{
		return "recipes.txt";
	}

	@Override
	protected List<List<String>> loadRecipes()
	{
		return super.loadRecipes(getRecipeFilename());
	}

	@Override
	public String getDecodedComponentsDescription(final MOB mob, final List<String> recipe)
	{
		final StringBuilder desc = new StringBuilder("");
		for(int vr=RCP_MAININGR;vr<recipe.size();vr+=2)
		{
			final String ingredient=recipe.get(vr);
			if(ingredient.length()>0)
				desc.append(ingredient).append(", ");
		}
		if(desc.length()<3)
			return "Nothing";
		return desc.toString().substring(0,desc.length()-2);
	}

	@Override
	public void unInvoke()
	{
		if(canBeUninvoked())
		{
			if(affected instanceof MOB)
			{
				final MOB mob=(MOB)affected;
				final Container cookingPot=this.cookingPot;
				final List<String> finalRecipe = this.finalRecipe;
				final Item buildingI=this.buildingI;
				if((cookingPot!=null)
				&&(finalRecipe!=null)
				&&(buildingI!=null)
				&&(mob!=null))
				{
					final List<Item> V=cookingPot.getDeepContents();
					for(int v=0;v<V.size();v++)
						V.get(v).destroy();
					if(cookingPot instanceof Drink)
					{
						if(buildingI.phyStats().weight()>0)
						{
							if(((Drink)cookingPot).liquidRemaining()>buildingI.phyStats().weight())
								((Drink)cookingPot).setLiquidRemaining(((Drink)cookingPot).liquidRemaining()-buildingI.phyStats().weight());
							else
								((Drink)cookingPot).setLiquidRemaining(0);
						}
						else
							((Drink)cookingPot).setLiquidRemaining(0);
					}
					if(!aborted)
					{
						final CMMsg msg=CMClass.getMsg(mob,buildingI,this,CMMsg.TYP_ITEMGENERATED|CMMsg.MASK_ALWAYS,null);
						setMsgXPValue(mob,msg);
						if(mob.location().okMessage(mob,msg))
						{
							mob.location().send(mob,msg);
							buildingI.basePhyStats().setLevel(1); // the newbie exception
							buildingI.phyStats().setLevel(1); // the newbie exception
							for(int i=0;i<finalAmount*(baseYield()+abilityCode());i++)
							{
								final Item food=((Item)buildingI.copyOf());
								food.setMiscText(buildingI.text());
								food.recoverPhyStats();
								if(cookingPot.owner() instanceof Room)
									((Room)cookingPot.owner()).addItem(food,ItemPossessor.Expire.Player_Drop);
								else
								if(cookingPot.owner() instanceof MOB)
									((MOB)cookingPot.owner()).addItem(food);
								CMLib.achievements().possiblyBumpAchievement(mob, AchievementLibrary.Event.CRAFTING, 1, this, buildingI);
								food.setContainer(cookingPot);
								if(((food.material()&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_LIQUID)
								&&(cookingPot instanceof Drink))
									((Drink)cookingPot).setLiquidRemaining(0);
							}
						}
					}
				}
			}
		}
		super.unInvoke();
	}

	protected boolean contentsSame(final Map<String,Integer> h1, final Map<String,Integer> h2)
	{
		if(h1.size()!=h2.size())
			return false;
		for(final String key : h1.keySet())
		{
			final Integer INT1=h1.get(key);
			final Integer INT2=h2.get(key);
			if((INT1==null)||(INT2==null))
				return false;
			if(INT1.intValue()!=INT2.intValue())
				return false;
		}
		return true;
	}

	protected Map<String,Integer> potContents(final Container pot)
	{
		final Map<String,Integer> h=new Hashtable<String,Integer>();
		if((pot instanceof Drink)&&(((Drink)pot).liquidRemaining()>0))
		{
			if(pot instanceof RawMaterial)
				h.put(RawMaterial.CODES.NAME(((RawMaterial)pot).material())+"/",Integer.valueOf(((Drink)pot).liquidRemaining()/10));
			else
				h.put(RawMaterial.CODES.NAME(((Drink)pot).liquidType())+"/",Integer.valueOf(((Drink)pot).liquidRemaining()/10));
		}
		if(pot.owner()==null)
			return h;
		final List<Item> V=pot.getDeepContents();
		for(int v=0;v<V.size();v++)
		{
			final Item I=V.get(v);
			String ing="Unknown";
			if(I instanceof RawMaterial)
			{
				ing=RawMaterial.CODES.NAME(I.material());
				if(CMParms.indexOf( RawMaterial.CODES.FISHES(), I.material())>=0)
					ing+="/FISH";
				else
				if(CMParms.indexOf( RawMaterial.CODES.BERRIES(), I.material())>=0)
					ing+="/BERRIES";
			}
			else
			if((((I.material()&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_VEGETATION)
				||((I.material()&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_LIQUID)
				||((I.material()&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_FLESH))
			&&(CMParms.parse(I.name()).size()>0))
				ing=CMParms.parse(I.name()).lastElement().toUpperCase();
			else
				ing=I.name();
			Integer INT=h.get(ing+"/"+I.rawSecretIdentity().toUpperCase()+"/"+I.Name().toUpperCase()+"/");
			if(INT==null)
				INT=Integer.valueOf(0);
			if(I instanceof RawMaterial)
				INT=Integer.valueOf(INT.intValue()+((RawMaterial)I).phyStats().weight());
			else
				INT=Integer.valueOf(INT.intValue()+1);
			h.put(ing+"/"+I.rawSecretIdentity().toUpperCase()+"/"+I.Name().toUpperCase()+"/",INT);
		}
		return h;
	}

	private static class Ingredients
	{
		final int amount;
		final List<String> list;
		public Ingredients(final int amount, final List<String> list)
		{
			this.amount=amount;
			this.list=list;
		}
	}

	protected Ingredients countIngredients(final List<String> Vr)
	{
		final String[] contents=new String[oldPotContents.size()];
		final int[] amounts=new int[oldPotContents.size()];
		int numIngredients=0;
		for(final String ingr  : oldPotContents.keySet())
		{
			contents[numIngredients]=ingr;
			amounts[numIngredients]=oldPotContents.get(contents[numIngredients]).intValue();
			numIngredients++;
		}

		int amountMade=0;

		final Ingredients codedList;
		boolean RanOutOfSomething=false;
		boolean NotEnoughForThisRun=false;
		while((!RanOutOfSomething)&&(!NotEnoughForThisRun))
		{
			for(int vr=RCP_MAININGR;vr<Vr.size();vr+=2)
			{
				final String ingredient=Vr.get(vr).toUpperCase();
				if(ingredient.length()>0)
				{
					int amount=1;
					if(vr<Vr.size()-1)
						amount=CMath.s_int(Vr.get(vr+1));
					if(amount==0)
						amount=1;
					if(amount<0)
						amount=amount*-1;
					if(ingredient.equalsIgnoreCase("water"))
						amount=amount*10;
					for(int i=0;i<contents.length;i++)
					{
						final String ingredient2=contents[i].toUpperCase();
						final int amount2=amounts[i];
						final int index =ingredient2.indexOf(ingredient+"/");
						if((index==0)
						||((index>0)
							&&(!Character.isLetter(ingredient2.charAt(index-1)))
							&&(ingredient2.charAt(index-1)!='-')))
						{
							amounts[i]=amount2-amount;
							if(amounts[i]<0)
								NotEnoughForThisRun=true;
							if(amounts[i]==0)
								RanOutOfSomething=true;
						}
					}
				}
			}
			if(!NotEnoughForThisRun)
				amountMade++;
		}
		if(NotEnoughForThisRun)
		{
			final List<String> list=new ArrayList<String>();
			for(int i=0;i<contents.length;i++)
			{
				if(amounts[i]<0)
				{
					String content=contents[i];
					if(content.indexOf('/')>=0)
						content=content.substring(0,content.indexOf('/'));
					list.add(content);
				}
			}
			codedList=new Ingredients(-amountMade, list);
		}
		else
		{
			final List<String> list=new ArrayList<String>();
			for(int i=0;i<contents.length;i++)
			{
				final String ingredient2=contents[i];
				final int amount2=amounts[i];
				if((amount2>0)
				&&((!honorHerbs())||(!ingredient2.toUpperCase().startsWith("HERBS/")))
				&&(!ingredient2.toUpperCase().startsWith("WATER/")))
				{
					final int x=ingredient2.indexOf('/');
					final int rsc = (x>0)?RawMaterial.CODES.FIND_IgnoreCase(ingredient2.substring(0,x))&RawMaterial.MATERIAL_MASK:-1;
					if(rsc != RawMaterial.MATERIAL_LIQUID)
					{
						String content=contents[i];
						if(content.indexOf('/')>=0)
							content=content.substring(0,content.indexOf('/'));
						list.add(content);
					}
				}
			}
			codedList=new Ingredients(amountMade, list);
		}
		return codedList;
	}

	private List<String> extraIngredientsInOldContents(final List<String> Vr, final boolean perfectOnly)
	{
		final List<String> extra=new ArrayList<String>();
		for(final String ingredient : oldPotContents.keySet())
		{
			boolean found=false;

			if(honorHerbs()&&ingredient.toUpperCase().startsWith("HERBS/")) // herbs exception
				found=true;
			else
			for(int vr=RCP_MAININGR;vr<Vr.size();vr+=2)
			{
				final String ingredient2=Vr.get(vr).toUpperCase();
				final int index=ingredient.toUpperCase().indexOf(ingredient2+"/");
				if((ingredient2.length()>0)
				&&(((!perfectOnly)
					&&((index==0)
						||((index>0)
							&&(!Character.isLetter(ingredient.charAt(index-1)))
							&&(ingredient.charAt(index-1)!='-'))))
				||((perfectOnly)
					&&(ingredient.toUpperCase().equalsIgnoreCase(ingredient2)))))
						found=true;
			}
			if(!found)
			{
				String content=ingredient;
				if(content.indexOf('/')>=0)
					content=content.substring(0,content.indexOf('/'));
				extra.add(content);
			}
		}
		return extra;
	}

	public List<String> missingIngredientsFromOldContents(final List<String> Vr, final boolean perfectOnly)
	{
		final List<String> missing=new ArrayList<String>();

		String possiblyMissing=null;
		boolean foundOptional=false;
		boolean hasOptional=false;
		for(int vr=RCP_MAININGR;vr<Vr.size();vr+=2)
		{
			final String ingredient=Vr.get(vr);
			if(ingredient.length()>0)
			{
				int amount=1;
				if(vr<Vr.size()-1)
					amount=CMath.s_int(Vr.get(vr+1));
				boolean found=false;
				for(final String ingredient2 : oldPotContents.keySet())
				{
					final int index=ingredient2.toUpperCase().indexOf(ingredient.toUpperCase()+"/");
					if((((index==0)
						||((index>0)
							&&(!Character.isLetter(ingredient2.charAt(index-1)))
							&&(ingredient2.charAt(index-1)!='-'))))
					||((!perfectOnly)
						&&(ingredient2.equalsIgnoreCase(ingredient.toUpperCase()))))
					{
						found = true;
						break;
					}
				}
				if(amount>=0)
				{
					if(!found)
						missing.add(ingredient);
				}
				else
				if(amount<0)
				{
					foundOptional=true;
					if(found)
						hasOptional=true;
					else
						possiblyMissing=ingredient;
				}
			}
		}
		if((foundOptional)&&(!hasOptional))
			missing.add(possiblyMissing);
		return missing;
	}

	public int homeCookValue(final MOB mob, final int multiplyer)
	{
		final int hc=getX1Level(mob);
		return hc*hc*multiplyer;
	}

	public Item buildItem(final MOB mob, final List<String> finalRecipe, final List<Item> contents)
	{
		String replaceName=(finalRecipe.get(RCP_MAININGR));
		boolean rotten = false;
		final Item buildingI;
		if(contents!=null)
		{
			for(int v=0;v<contents.size();v++)
			{
				final Item I=contents.get(v);
				if((I instanceof RawMaterial)
				&&(I instanceof Decayable)
				&&(I.fetchEffect("Poison_Rotten")!=null))
				{
					rotten=true;
					break;
				}
			}
			for(int v=0;v<contents.size();v++)
			{
				final Item I=contents.get(v);
				if(I instanceof RawMaterial)
				{
					if(RawMaterial.CODES.NAME(I.material()).equalsIgnoreCase(finalRecipe.get(RCP_MAININGR)))
					{
						if((((RawMaterial)I).domainSource()!=null)
						&&(!CMath.isNumber(((RawMaterial)I).domainSource()))
						&&(CMClass.getRace(((RawMaterial)I).domainSource()))!=null)
							replaceName=CMClass.getRace(((RawMaterial)I).domainSource()).name().toLowerCase();
						else
						{
							String name=I.Name();
							if(name.endsWith(" meat"))
								name=name.substring(0,name.length()-5);
							if(name.endsWith(" flesh"))
								name=name.substring(0,name.length()-6);
							if(name.toLowerCase().endsWith(L(" bundle")))
								name = name.substring(0,name.length()-L(" bundle").length());
							name=name.trim();
							final int x=name.lastIndexOf(' ');
							if((x>0)&&(!name.substring(x+1).trim().equalsIgnoreCase("of")))
								replaceName=name.substring(x+1);
							else
								replaceName=name;
						}
						break;
					}
				}
			}
		}
		finalDishName=replacePercent(finalRecipe.get(RCP_FINALFOOD),
									CMStrings.capitalizeAndLower(replaceName));
		final String foodType=finalRecipe.get(RCP_FOODDRINK);
		double ingredientsWeight = 0.0;
		if(contents!=null)
		{
			for(int v=0;v<contents.size();v++)
			{
				final Item I=contents.get(v);
				if((I.material()!=RawMaterial.RESOURCE_HERBS)||(!honorHerbs()))
					ingredientsWeight += I.basePhyStats().weight();
			}
		}
		if(foodType.equalsIgnoreCase("FOOD"))
		{
			buildingI=CMClass.getItem("GenFood");
			this.buildingI=buildingI;
			final Food food=(Food)buildingI;
			if(requireFire())
			{
				buildingI.setName(((messedUp)?"burnt ":"")+finalDishName);
				buildingI.setDisplayText(L("some @x1@x2 is here",((messedUp)?"burnt ":""),finalDishName));
				buildingI.setDescription(L("It looks @x1",((messedUp)?"burnt!":rotten?"rotten!":"good!")));
			}
			else
			{
				buildingI.setName(((messedUp)?"ruined ":"")+finalDishName);
				buildingI.setDisplayText(L("some @x1@x2 is here",((messedUp)?"ruined ":""),finalDishName));
				buildingI.setDescription(L("It looks @x1",((messedUp)?"ruined!":rotten?"rotten!":"good!")));
			}
			buildingI.basePhyStats().setLevel(CMath.s_int(finalRecipe.get(RCP_LEVEL)));
			buildingI.phyStats().setLevel(buildingI.basePhyStats().level());
			food.setNourishment(0);
			if(!messedUp)
			{
				boolean timesTwo=false;
				if((contents!=null)&&(contents.size()>0))
				{
					for(int v=0;v<contents.size();v++)
					{
						final Item I=contents.get(v);
						if((I.material()==RawMaterial.RESOURCE_HERBS)&&(honorHerbs()))
							timesTwo=true;
						else
						if(I instanceof Food)
							food.setNourishment(food.nourishment()+(((Food)I).nourishment()+((Food)I).nourishment())+25);
						else
							food.setNourishment(food.nourishment()+25);
					}
				}
				else
				{
					for(int vr=RCP_MAININGR;vr<finalRecipe.size();vr+=2)
					{
						final String ingredient=finalRecipe.get(vr).toUpperCase();
						if(ingredient.length()>0)
						{
							int amount=1;
							if(vr<finalRecipe.size()-1)
								amount=CMath.s_int(finalRecipe.get(vr+1));
							if(amount==0)
								amount=1;
							if(amount<0)
								amount=amount*-1;
							if(ingredient.equalsIgnoreCase("water")||ingredient.equalsIgnoreCase("milk"))
								continue;
							if(ingredient.equalsIgnoreCase("herbs"))
							{
								timesTwo=true;
								continue;
							}
							final int resourceCode=RawMaterial.CODES.FIND_IgnoreCase(ingredient);
							if((resourceCode >0)&&((resourceCode&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_LIQUID))
								continue;
							food.setNourishment(food.nourishment()+(100*amount));
						}
					}
				}
				if(timesTwo)
					food.setNourishment(food.nourishment()*2);
			}
			int material=-1;
			for(int vr=RCP_MAININGR;vr<finalRecipe.size();vr+=2)
			{
				final String ingredient=finalRecipe.get(vr).toUpperCase();
				if(ingredient.length()>0)
				{
					final int resourceCode=RawMaterial.CODES.FIND_IgnoreCase(ingredient);
					if(resourceCode >0)
					{
						if(((resourceCode&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_FLESH)
						||((resourceCode&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_VEGETATION))
						{
							material=resourceCode;
							break;
						}
					}
				}
			}
			if(contents!=null)
			{
				for(int v=0;v<contents.size();v++)
				{
					final Item I=contents.get(v);
					if((I instanceof Food)
					&&(material<0))
					{
						switch(((Food)I).material()&RawMaterial.MATERIAL_MASK)
						{
						case RawMaterial.MATERIAL_VEGETATION:
						case RawMaterial.MATERIAL_FLESH:
							material=((Food)I).material();
							break;
						}
					}
				}
				if(ingredientsWeight < finalAmount)
					ingredientsWeight = finalAmount;
				food.basePhyStats().setWeight((int)Math.round(ingredientsWeight));
			}
			if(material<0)
			{
				final String materialFoodName=replacePercent(finalRecipe.get(RCP_FINALFOOD),"").trim().toUpperCase();
				for(int i=0;i<RawMaterial.CODES.TOTAL();i++)
				{
					if(materialFoodName.equals(RawMaterial.CODES.NAME(i)))
					{
						material=i;
						break;
					}
				}
				if((contents!=null)&&(material<0))
				{
					for(int v=0;v<contents.size();v++)
					{
						final Item I=contents.get(v);
						if(I instanceof Drink)
						{
							material=((Drink)I).liquidType();
							break;
						}
					}
				}
			}
			food.setMaterial(material<0?RawMaterial.RESOURCE_BEEF:material);
			if((rotten)&&(food.nourishment()>1))
				food.setNourishment(food.nourishment()/2/(finalAmount>0?finalAmount:1));
			else
			if(mob!=null)
				food.setNourishment((food.nourishment()+homeCookValue(mob,10))/finalAmount);
			if(rotten)
			{
				final Ability A=CMClass.getAbility("Poison_Rotten");
				if(A!=null)
					food.addNonUninvokableEffect(A);
			}
			else
			if(!messedUp)
				CMLib.materials().addEffectsToResource(food);
			food.basePhyStats().setWeight((int)Math.round(CMath.div(food.basePhyStats().weight(),finalAmount)));
			if(food.basePhyStats().weight()>0)
				food.setBite(food.nourishment() / (food.basePhyStats().weight()*2));
			else
				food.setBite(food.nourishment());
			playSound=defaultFoodSound;
		}
		else
		if(foodType.equalsIgnoreCase("DRINK"))
		{
			buildingI=CMClass.getItem("GenLiquidResource");
			this.buildingI=buildingI;
			//building.setMiscText(cooking.text());
			//building.recoverPhyStats();
			buildingI.setName((messedUp?"spoiled ":"")+finalDishName);
			buildingI.setDisplayText(L("some @x1@x2 is here.",((messedUp)?"spoiled ":""),finalDishName));
			buildingI.setDescription(L("It looks @x1",((messedUp)?"spoiled!":rotten?"rotten!":"good!")));
			buildingI.basePhyStats().setLevel(CMath.s_int(finalRecipe.get(RCP_LEVEL)));
			buildingI.phyStats().setLevel(buildingI.basePhyStats().level());
			final Drink drink=(Drink)buildingI;
			int liquidType=RawMaterial.RESOURCE_FRESHWATER;
			for(int vr=RCP_MAININGR;vr<finalRecipe.size();vr+=2)
			{
				final String ingredient=finalRecipe.get(vr).toUpperCase();
				if(ingredient.length()>0)
				{
					final int resourceCode=RawMaterial.CODES.FIND_IgnoreCase(ingredient);
					if(resourceCode >0)
					{
						if((resourceCode&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_LIQUID)
						{
							liquidType=resourceCode;
							break;
						}
					}
				}
			}
			if(contents!=null)
			{
				for(int v=0;v<contents.size();v++)
				{
					final Item I=contents.get(v);
					buildingI.basePhyStats().setWeight(buildingI.basePhyStats().weight()+((I.basePhyStats().weight())/finalAmount));
					if(I instanceof Food)
						drink.setLiquidRemaining(drink.liquidRemaining()+((Food)I).nourishment()+25);
					if((I instanceof Drink)
					&&(liquidType < 0)
					&&((((Drink)I).liquidType()&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_LIQUID))
						liquidType=((Drink)I).liquidType();
				}
			}
			if(ingredientsWeight >= finalAmount)
				buildingI.basePhyStats().setWeight((int)Math.round(CMath.div(ingredientsWeight,finalAmount)));
			else
			if(ingredientsWeight>0)
				buildingI.basePhyStats().setWeight((int)Math.round(ingredientsWeight));

			if(drink.liquidRemaining()>0)
			{
				drink.setLiquidRemaining(drink.liquidRemaining()+homeCookValue(mob,10));
				drink.setLiquidHeld(drink.liquidRemaining()+homeCookValue(mob,10));
				if(buildingI.basePhyStats().weight()>0)
					drink.setThirstQuenched(drink.liquidRemaining()/(buildingI.basePhyStats().weight()*2));
				else
					drink.setThirstQuenched(drink.liquidRemaining());
			}
			else
			{
				drink.setLiquidHeld(1);
				drink.setLiquidRemaining(1);
				drink.setThirstQuenched(1);
			}
			if(messedUp)
				drink.setThirstQuenched(1);
			else
			if(rotten && (drink.thirstQuenched() > 1))
				drink.setThirstQuenched(drink.thirstQuenched()/2);
			playSound=defaultDrinkSound;
			buildingI.setMaterial(liquidType);
			drink.setLiquidType(liquidType);
			if(rotten)
			{
				final Ability A=CMClass.getAbility("Poison_Rotten");
				if(A!=null)
					buildingI.addNonUninvokableEffect(A);
			}
			else
			if(!messedUp)
				CMLib.materials().addEffectsToResource((Item)drink);
		}
		else
		if(CMClass.getItem(foodType)!=null)
		{
			buildingI=CMClass.getItem(foodType);
			this.buildingI=buildingI;
			final String ruinWord=(buildingI instanceof Drink)?"spoiled ":(requireFire()?"burnt ":"ruined ");
			buildingI.setName(((messedUp)?ruinWord:"")+finalDishName);
			buildingI.setDisplayText(L("some @x1@x2 is here",((messedUp)?ruinWord:""),finalDishName));
			if(buildingI instanceof Drink)
			{
				final Drink drink=(Drink)buildingI;
				final int rem=drink.liquidHeld();
				drink.setLiquidRemaining(0);
				int liquidType=RawMaterial.RESOURCE_FRESHWATER;
				if(contents!=null)
				for(int v=0;v<contents.size();v++)
				{
					final Item I=contents.get(v);
					buildingI.basePhyStats().setWeight(buildingI.basePhyStats().weight()+((I.basePhyStats().weight())/finalAmount));
					drink.setLiquidRemaining(drink.liquidRemaining()+rem);
					if((I instanceof Drink)&&((((Drink)I).liquidType()&RawMaterial.MATERIAL_MASK)==RawMaterial.MATERIAL_LIQUID))
						liquidType=((Drink)I).liquidType();
				}
				if((drink.liquidRemaining()>0)&&(!messedUp))
					drink.setLiquidHeld(drink.liquidRemaining());
				else
				{
					drink.setLiquidHeld(1);
					drink.setLiquidRemaining(1);
					drink.setThirstQuenched(1);
				}
				buildingI.setMaterial(liquidType);
				drink.setLiquidType(liquidType);
			}
			if(ingredientsWeight >= finalAmount)
				buildingI.basePhyStats().setWeight((int)Math.round(CMath.div(ingredientsWeight,finalAmount)));
			else
			if(ingredientsWeight>0)
				buildingI.basePhyStats().setWeight((int)Math.round(ingredientsWeight));
			if(!messedUp)
				CMLib.materials().addEffectsToResource(buildingI);
			playSound=defaultFoodSound;
		}
		else
		{
			buildingI=CMClass.getItem("GenResource");
			this.buildingI=buildingI;
			if(messedUp)
				buildingI.setMaterial(RawMaterial.RESOURCE_DUST);
			else
			{
				final int code = RawMaterial.CODES.FIND_IgnoreCase(foodType);
				if(code>=0)
					buildingI.setMaterial(code);
			}
			final String ruinWord=(buildingI instanceof Drink)?"spoiled ":(requireFire()?"burnt ":"ruined ");
			buildingI.setName(((messedUp)?ruinWord:"")+finalDishName);
			buildingI.setDisplayText(L("some @x1@x2 is here",((messedUp)?ruinWord:""),finalDishName));
			if(ingredientsWeight >= finalAmount)
				buildingI.basePhyStats().setWeight((int)Math.round(CMath.div(ingredientsWeight,finalAmount)));
			else
			if(ingredientsWeight>0)
				buildingI.basePhyStats().setWeight((int)Math.round(ingredientsWeight));
			else
				buildingI.basePhyStats().setWeight(1);
			playSound=defaultFoodSound;
		}

		if(buildingI!=null)
		{
			if(buildingI instanceof RawMaterial)
				((RawMaterial)buildingI).setSecretIdentity(buildingI.name());
			else
			if(mob!=null)
				buildingI.setSecretIdentity(L("This was prepared by @x1.",mob.Name()));
			final String spell=finalRecipe.get(RCP_BONUSSPELL);
			if((spell!=null)&&(spell.length()>0))
			{
				if(buildingI instanceof Perfume)
					((Perfume)buildingI).setSmellList(spell);
				else
					addSpellsOrBehaviors(buildingI,spell,null,null);
			}
			buildingI.recoverPhyStats();
			buildingI.text();
		}
		return buildingI;
	}

	@Override
	public boolean invoke(final MOB mob, final List<String> commands, final Physical givenTarget, final boolean auto, final int asLevel)
	{
		return autoGenInvoke(mob,commands,givenTarget,auto,asLevel,0,false,new ArrayList<CraftedItem>(0));
	}

	@Override
	protected void applyName(final Item item, final String word, final boolean hide)
	{
		super.applyName(item, word, hide);
		if(!buildingI.description().contains(L("rotten")))
		{
			buildingI.setDescription(L("It looks @x1",((messedUp)?(requireFire()?"burnt!":"ruined!"):(word+"!"))));
		}
	}

	@Override
	protected boolean autoGenInvoke(final MOB mob, final List<String> commands, final Physical givenTarget, final boolean auto,
									final int asLevel, final int autoGenerate, final boolean forceLevels, final List<CraftedItem> crafted)
	{
		if(super.checkStop(mob, commands))
			return true;
		verb=cookWord();
		cookingPot=null;
		finalRecipe=null;
		finalAmount=0;
		buildingI=null;
		finalDishName=null;
		messedUp=false;
		oldPotContents=null;
		activity = CraftingActivity.CRAFTING;
		final List<List<String>> allRecipes=addRecipes(mob,loadRecipes());
		final PairVector<EnhancedExpertise,Integer> enhancedTypes=enhancedTypes(mob,commands);
		int recipeLevel = 1;
		if(autoGenerate>0)
		{
			finalAmount=1;
			List<String> finalRecipe=null;
			String recipeName=null;
			if((commands.size()>0))
				recipeName=CMParms.combine(commands,0);
			else
			{
				final List<List<String>> recipes=loadRecipes();
				final List<String> V = recipes.get(CMLib.dice().roll(1,recipes.size(),-1));
				recipeName=replacePercent(V.get(0),"");
			}
			for(int r=0;r<allRecipes.size();r++)
			{
				final List<String> Vr=allRecipes.get(r);
				if(Vr.size()>0)
				{
					final String item=Vr.get(RCP_FINALFOOD);
					if(replacePercent(item,"").equalsIgnoreCase(recipeName))
					{
						finalRecipe = Vr;
						break;
					}
				}
			}
			if(finalRecipe==null)
			{
				for(int r=0;r<allRecipes.size();r++)
				{
					final List<String> Vr=allRecipes.get(r);
					if(Vr.size()>0)
					{
						final String item=Vr.get(RCP_FINALFOOD);
						if(replacePercent(item,"").toLowerCase().indexOf(recipeName.toLowerCase())>=0)
						{
							finalRecipe = Vr;
							break;
						}
					}
				}
			}
			if(finalRecipe==null)
				return false;
			buildingI=buildItem(mob,finalRecipe,null);
			if(forceLevels)
			{
				buildingI.basePhyStats().setLevel(CMath.s_int(finalRecipe.get(RCP_LEVEL)));
				buildingI.phyStats().setLevel(buildingI.basePhyStats().level());
			}
			final int duration=getDuration(mob, 1);
			crafted.add(new CraftedItem(buildingI,null,duration));
			return true;
		}
		randomRecipeFix(mob,allRecipes,commands,-1);
		final int colWidth1=CMLib.lister().fixColWidth(20,mob.session());
		final int colWidth2=CMLib.lister().fixColWidth(4,mob.session());
		final int lineWidth=CMLib.lister().fixColWidth(78,mob.session());
		if((commands.size()>0)
		&&(commands.get(0)).equalsIgnoreCase("list"))
		{
			String mask=CMParms.combine(commands,1);
			boolean allFlag=false;
			if(mask.equalsIgnoreCase("all"))
			{
				allFlag=true;
				mask="";
			}
			final StringBuffer buf=new StringBuffer(
				L("@x1 @x2^.^? ^B^~wIngredients required^N\n\r"
				,CMStrings.padRight(L("^xRecipe"),colWidth1)
				,CMStrings.padRight(L("^xLvl"),colWidth2))
			);
			final List<List<String>> listRecipes=((mask.length()==0) || mask.equalsIgnoreCase("all")) ? allRecipes : super.matchingRecipes(allRecipes, mask, true);
			for(int r=0;r<listRecipes.size();r++)
			{
				final List<String> Vr=listRecipes.get(r);
				if(Vr.size()>0)
				{
					final String item=Vr.get(RCP_FINALFOOD);
					if(item.length()==0)
						continue;
					final int level=CMath.s_int(Vr.get(RCP_LEVEL));
					if(((level<=xlevel(mob))||allFlag))
					{
						StringBuilder line=new StringBuilder("");
						line.append("^c"+CMStrings.padRight(CMStrings.capitalizeAndLower(replacePercent(item,"")),colWidth1)+"^w ")
							.append(CMStrings.padRight(""+level,colWidth2)+"^w ");
						for(int vr=RCP_MAININGR;vr<Vr.size();vr+=2)
						{
							String ingredient=Vr.get(vr);
							if(ingredient.length()>0)
							{
								int amount=1;
								if(vr<Vr.size()-1)
									amount=CMath.s_int(Vr.get(vr+1));
								if(amount==0)
									amount=1;
								if(amount<0)
								{
									ingredient="~"+ingredient;
									amount=amount*-1;
								}
								if(ingredient.equalsIgnoreCase("water"))
									amount=amount*10;
								final String next=ingredient.toLowerCase()+"("+amount+") ";
								if(line.length()+next.length()-2>=lineWidth)
								{
									buf.append(line).append("\n\r");
									line=new StringBuilder("^w ")
											.append(CMStrings.padRight(" ", colWidth1)).append(" ")
											.append(CMStrings.padRight(" ", colWidth2));
								}
								line.append(next);
							}
						}
						buf.append(line).append("\n\r");
					}
				}
			}
			commonTelL(mob,"@x1\n\rIngredients beginning with the ~ character are optional additives.",buf.toString());
			enhanceList(mob);
			return true;
		}
		final Item possibleContainer=possibleContainer(mob,commands,true,Wearable.FILTER_UNWORNONLY);
		final Item target=getTarget(mob,mob.location(),givenTarget,possibleContainer,commands,Wearable.FILTER_UNWORNONLY);
		if(target==null)
		{
			commonTelL(mob,"The syntax for this skill is @x1 [CONTAINER]",triggerStrings()[0]);
			return false;
		}

		if(!(target instanceof Container))
		{
			commonTelL(mob,"There's nothing in @x1 to @x2!",target.name(mob),cookWordShort());
			return false;
		}
		for(int a=0;a<mob.numEffects();a++)
		{
			final Ability A=mob.fetchEffect(a);
			if((A instanceof Cooking) && (((Cooking)A).cookingPot==target))
			{
				commonTelL(mob,"That is already in use.");
				return false;
			}
		}
		if(!isMineForCooking(mob,(Container)target))
		{
			commonTelL(mob,"You probably need to pick that up first.");
			return false;
		}
		if(!meetsLidRequirements(mob,(Container)target))
		{
			commonTelL(mob,"You need a closeable container to bake that in, and you need to close it to begin.");
			return false;
		}
		switch(target.material()&RawMaterial.MATERIAL_MASK)
		{
		case RawMaterial.MATERIAL_GLASS:
		case RawMaterial.MATERIAL_METAL:
		case RawMaterial.MATERIAL_MITHRIL:
		case RawMaterial.MATERIAL_ROCK:
		case RawMaterial.MATERIAL_PRECIOUS:
			break;
		default:
			commonTelL(mob,"@x1 is not suitable to @x2 in.",target.name(mob),cookWordShort());
			return false;
		}

		if(requireFire())
		{
			final Item fire=getRequiredFire(mob,0);
			if(fire==null)
				return false;
		}

		messedUp=!proficiencyCheck(mob,0,auto);
		int duration=getDuration(mob, 1);
		cookingPot=(Container)target;
		oldPotContents=potContents(cookingPot);

		//***********************************************
		//* figure out recipe
		//***********************************************
		List<List<String>> perfectRecipes=new ArrayList<List<String>>();
		final List<List<String>> closerRecipes=new ArrayList<List<String>>();
		final List<List<String>> closeRecipes=new ArrayList<List<String>>();
		for(int v=0;v<allRecipes.size();v++)
		{
			final List<String> Vr=allRecipes.get(v);
			boolean found=false;
			for(final String ingredient2 : oldPotContents.keySet())
			{
				final int index =ingredient2.indexOf(Vr.get(RCP_MAININGR).toUpperCase()+"/");
				if((index==0)
				||((index>0)
					&&(!Character.isLetter(ingredient2.charAt(index-1)))
					&&(ingredient2.charAt(index-1)!='-')))
				{
					found = true;
					break;
				}
			}
			if(found)
				closeRecipes.add(Vr);
			if((missingIngredientsFromOldContents(Vr,true).size()==0)
			&&(extraIngredientsInOldContents(Vr,true).size()==0))
				perfectRecipes.add(Vr);
			if((missingIngredientsFromOldContents(Vr,false).size()==0)
			&&(extraIngredientsInOldContents(Vr,false).size()==0))
				closerRecipes.add(Vr);
		}

		if(perfectRecipes.size()==0)
			perfectRecipes=closerRecipes;
		if(perfectRecipes.size()==0)
		{
			if(closeRecipes.size()==0)
			{
				commonTelL(mob,"You don't know how to make anything out of those ingredients.  Have you tried LIST as a parameter?");
				return false;
			}
			for(int vr=0;vr<closeRecipes.size();vr++)
			{
				final List<String> Vr=closeRecipes.get(vr);
				final List<String> missing=missingIngredientsFromOldContents(Vr,false);
				final List<String> extra=extraIngredientsInOldContents(Vr,false);
				final String recipeName=replacePercent(Vr.get(RCP_FINALFOOD),Vr.get(RCP_MAININGR).toLowerCase());
				if(extra.size()>0)
				{
					final StringBuffer buf=new StringBuffer(L("If you are trying to make @x1, you need to remove ",recipeName));
					for(int i=0;i<extra.size();i++)
					{
						if(i==0)
							buf.append(extra.get(i).toLowerCase());
						else
						if(i==extra.size()-1)
							buf.append(L(", and @x1",extra.get(i).toLowerCase()));
						else
							buf.append(", " + extra.get(i).toLowerCase());
					}
					commonTell(mob,buf.toString()+".");
				}
				else
				if(missing.size()>0)
				{
					final StringBuffer buf=new StringBuffer(L("If you are trying to make @x1, you need to add ",recipeName));
					for(int i=0;i<missing.size();i++)
					{
						if(i==0)
							buf.append(missing.get(i).toLowerCase());
						else
						if(i==missing.size()-1)
							buf.append(L(", and @x1",missing.get(i).toLowerCase()));
						else
							buf.append(", "+missing.get(i).toLowerCase());
					}
					commonTell(mob,buf.toString()+".");
				}
			}
			return false;
		}
		final List<String> complaints=new ArrayList<String>();
		for(int vr=0;vr<perfectRecipes.size();vr++)
		{
			final List<String> Vr=perfectRecipes.get(vr);
			final Ingredients ingrs=countIngredients(Vr);
			final Integer amountMaking=Integer.valueOf(ingrs.amount);
			final String recipeName=replacePercent(Vr.get(RCP_FINALFOOD),Vr.get(RCP_MAININGR).toLowerCase());
			if(ingrs.list.size()==0)
			{
				if(CMath.s_int(Vr.get(RCP_LEVEL))>xlevel(mob))
					complaints.add("If you are trying to make "+recipeName+", you need to wait until you are level "+CMath.s_int(Vr.get(RCP_LEVEL))+".");
				else
				{
					finalRecipe=Vr;
					finalAmount=amountMaking.intValue();
					break;
				}
			}
			else
			if(amountMaking.intValue()<=0)
			{
				final StringBuffer buf=new StringBuffer(L("If you are trying to make @x1, you need to add a little more ",recipeName));
				for(int i=0;i<ingrs.list.size();i++)
				{
					if(i==0)
						buf.append(ingrs.list.get(i).toLowerCase());
					else
					if(i==ingrs.list.size()-1)
						buf.append(L(", and @x1",ingrs.list.get(i).toLowerCase()));
					else
						buf.append(", "+ingrs.list.get(i).toLowerCase());
				}
				complaints.add(buf.toString());
			}
			else
			if(amountMaking.intValue()>0)
			{
				final StringBuffer buf=new StringBuffer(L("If you are trying to make @x1, you need to remove some of the ",recipeName));
				for(int i=0;i<ingrs.list.size();i++)
				{
					if(i==0)
						buf.append(ingrs.list.get(i).toLowerCase());
					else
					if(i==ingrs.list.size()-1)
						buf.append(L(", and @x1",ingrs.list.get(i).toLowerCase()));
					else
						buf.append(", "+ingrs.list.get(i).toLowerCase());
				}
				complaints.add(buf.toString());
			}
		}
		if(finalRecipe==null)
		{
			for(int c=0;c<complaints.size();c++)
				commonTell(mob,(complaints.get(c)));
			return false;
		}

		buildingI=buildItem(mob,finalRecipe,cookingPot.getDeepContents());
		duration=getDuration(mob, CMath.isInteger(finalRecipe.get(RCP_LEVEL))?CMath.s_int(finalRecipe.get(RCP_LEVEL)):1);
		//***********************************************
		//* done figuring out recipe
		//***********************************************
		if(!super.invoke(mob,commands,givenTarget,auto,asLevel))
			return false;

		recipeLevel=CMath.s_int(finalRecipe.get(RCP_LEVEL));
		final CMMsg msg=CMClass.getMsg(mob,cookingPot,this,getActivityMessageType(),getActivityMessageType(),getActivityMessageType(),L("<S-NAME> start(s) @x1 something in <T-NAME>.",cookWord()));
		if(mob.location().okMessage(mob,msg))
		{
			mob.location().send(mob,msg);
			cookingPot=(Container)msg.target();
			beneficialAffect(mob,mob,asLevel,duration);
			enhanceItem(mob,buildingI,recipeLevel,enhancedTypes);
		}
		return true;
	}
}
