/
com/planet_ink/coffee_mud/Abilities/Common/
com/planet_ink/coffee_mud/Abilities/Diseases/
com/planet_ink/coffee_mud/Abilities/Druid/
com/planet_ink/coffee_mud/Abilities/Fighter/
com/planet_ink/coffee_mud/Abilities/Languages/
com/planet_ink/coffee_mud/Abilities/Misc/
com/planet_ink/coffee_mud/Abilities/Prayers/
com/planet_ink/coffee_mud/Abilities/Properties/
com/planet_ink/coffee_mud/Abilities/Skills/
com/planet_ink/coffee_mud/Abilities/Songs/
com/planet_ink/coffee_mud/Abilities/Spells/
com/planet_ink/coffee_mud/Abilities/Thief/
com/planet_ink/coffee_mud/Abilities/Traps/
com/planet_ink/coffee_mud/Behaviors/
com/planet_ink/coffee_mud/CharClasses/interfaces/
com/planet_ink/coffee_mud/Commands/
com/planet_ink/coffee_mud/Commands/interfaces/
com/planet_ink/coffee_mud/Common/
com/planet_ink/coffee_mud/Common/interfaces/
com/planet_ink/coffee_mud/Exits/interfaces/
com/planet_ink/coffee_mud/Items/Armor/
com/planet_ink/coffee_mud/Items/Basic/
com/planet_ink/coffee_mud/Items/CompTech/
com/planet_ink/coffee_mud/Items/MiscMagic/
com/planet_ink/coffee_mud/Items/Weapons/
com/planet_ink/coffee_mud/Items/interfaces/
com/planet_ink/coffee_mud/Libraries/
com/planet_ink/coffee_mud/Libraries/interfaces/
com/planet_ink/coffee_mud/Locales/
com/planet_ink/coffee_mud/MOBS/
com/planet_ink/coffee_mud/Races/
com/planet_ink/coffee_mud/Races/interfaces/
com/planet_ink/coffee_mud/WebMacros/
com/planet_ink/coffee_mud/WebMacros/interfaces/
com/planet_ink/coffee_mud/core/
com/planet_ink/coffee_mud/core/collections/
com/planet_ink/coffee_mud/core/interfaces/
com/planet_ink/coffee_mud/core/intermud/
com/planet_ink/coffee_mud/core/intermud/i3/
com/planet_ink/coffee_web/server/
com/planet_ink/siplet/applet/
lib/
resources/factions/
resources/fakedb/
resources/progs/autoplayer/
resources/quests/holidays/
web/
web/admin.templates/
web/admin/grinder/
web/admin/images/
web/clan.templates/
web/pub.templates/
web/pub/images/mxp/
web/pub/sounds/
web/pub/textedit/
package com.planet_ink.coffee_mud.MOBS;
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.Common.interfaces.Session.InputCallback;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.DatabaseEngine;
import com.planet_ink.coffee_mud.Libraries.interfaces.DatabaseEngine.PlayerData;
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 2005-2016 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 StdPostman extends StdShopKeeper implements PostOffice
{
	@Override
	public String ID()
	{
		return "StdPostman";
	}

	protected double	minimumPostage		= 1.0;
	protected double	postagePerPound		= 1.0;
	protected double	holdFeePerPound		= 1.0;
	protected double	feeForNewBox		= 50.0;
	protected int		maxMudMonthsHeld	= 12;
	private long		postalWaitTime		= -1;

	protected static Map<String,Long>	postalTimes	= new Hashtable<String,Long>();
	
	public StdPostman()
	{
		super();
		username="a postman";
		setDescription("He\\`s making a speedy delivery!");
		setDisplayText("The local postman is waiting to serve you.");
		CMLib.factions().setAlignment(this,Faction.Align.GOOD);
		setMoney(0);
		whatIsSoldMask=ShopKeeper.DEAL_POSTMAN;
		basePhyStats.setWeight(150);
		setWimpHitPoint(0);

		baseCharStats().setStat(CharStats.STAT_INTELLIGENCE,16);
		baseCharStats().setStat(CharStats.STAT_CHARISMA,25);

		basePhyStats().setArmor(0);

		baseState.setHitPoints(1000);

		recoverMaxState();
		resetToMaxState();
		recoverPhyStats();
		recoverCharStats();
	}

	@Override
	public double minimumPostage()
	{
		return minimumPostage;
	}

	@Override
	public void setMinimumPostage(double d)
	{
		minimumPostage = d;
	}

	@Override
	public double postagePerPound()
	{
		return postagePerPound;
	}

	@Override
	public void setPostagePerPound(double d)
	{
		postagePerPound = d;
	}

	@Override
	public double holdFeePerPound()
	{
		return holdFeePerPound;
	}

	@Override
	public void setHoldFeePerPound(double d)
	{
		holdFeePerPound = d;
	}

	@Override
	public double feeForNewBox()
	{
		return feeForNewBox;
	}

	@Override
	public void setFeeForNewBox(double d)
	{
		feeForNewBox = d;
	}

	@Override
	public int maxMudMonthsHeld()
	{
		return maxMudMonthsHeld;
	}

	@Override
	public void setMaxMudMonthsHeld(int months)
	{
		maxMudMonthsHeld = months;
	}

	@Override
	public void addSoldType(int mask)
	{
		setWhatIsSoldMask(CMath.abs(mask));
	}

	@Override
	public void setWhatIsSoldMask(long newSellCode)
	{
		super.setWhatIsSoldMask(newSellCode);
		if(!isSold(ShopKeeper.DEAL_CLANPOSTMAN))
			whatIsSoldMask=ShopKeeper.DEAL_POSTMAN;
		else
			whatIsSoldMask=ShopKeeper.DEAL_CLANPOSTMAN;
	}

	@Override
	public String postalChain()
	{
		return text();
	}

	@Override
	public void setPostalChain(String name)
	{
		setMiscText(name);
	}

	@Override
	public String postalBranch()
	{
		return CMLib.map().getExtendedRoomID(getStartRoom());
	}

	@Override
	public String getSenderName(MOB mob, Clan.Function func, boolean checked)
	{
		if(isSold(ShopKeeper.DEAL_CLANPOSTMAN))
		{
			final Pair<Clan,Integer> clanPair=CMLib.clans().findPrivilegedClan(mob, func);
			if(clanPair!=null)
				return clanPair.first.clanID();
			else
			if(checked)
			{
				if(mob.clans().iterator().hasNext())
				{
					CMLib.commands().postSay(this,mob,L("I'm sorry, you aren't authorized by your clan to do that."),true,false);
					return null;
				}
				else
				{
					CMLib.commands().postSay(this,mob,L("I'm sorry, I only deal with clans."),true,false);
					return null;
				}
			}
		}
		return mob.Name();
	}

	@Override
	public void addToBox(String mob, Item thisThang, String from, String to, long holdTime, double COD)
	{
		final String name=thisThang.ID();
		CMLib.catalog().updateCatalogIntegrity(thisThang);
		CMLib.database().DBCreateData(mob,
				postalChain(),
				postalBranch()+";"+thisThang+Math.random(),
				from+";"
				+to+";"
				+holdTime+";"
				+COD+";"
				+name+";"
				+CMLib.coffeeMaker().getPropertiesStr(thisThang,true));
	}

	@Override
	public boolean delFromBox(String boxName, Item thisThang)
	{
		final List<PlayerData> V=getBoxRowPDData(boxName);
		boolean found=false;
		for(int v=V.size()-1;v>=0;v--)
		{
			final DatabaseEngine.PlayerData PD=V.get(v);
			if((PD!=null)
			&&(PD.key().startsWith(postalBranch()+";")))
			{
				final Item I=makeItem(parsePostalItemData(PD.xml()));
				if(I==null)
					continue;
				if(thisThang.sameAs(I))
				{
					found=true;
					CMLib.database().DBDeleteData(PD.who(),PD.section(),PD.key());
					break;
				}
			}
		}
		return found;
	}

	@Override
	public void emptyBox(String boxName)
	{
		CMLib.database().DBDeleteData(boxName,postalChain());
	}

	@Override
	public Map<String, String> getOurOpenBoxes(String boxName)
	{
		final Hashtable<String,String> branches=new Hashtable<String,String>();
		final List<PlayerData> V=CMLib.database().DBReadData(boxName,postalChain());
		if(V==null)
			return branches;
		for(int v=0;v<V.size();v++)
		{
			final DatabaseEngine.PlayerData PD=V.get(v);
			if(PD!=null)
			{
				final String key=PD.key();
				final int x=key.indexOf('/');
				if(x>0)
					branches.put(key.substring(0,x),key.substring(x+1));
			}
		}
		return branches;
	}

	@Override
	public void createBoxHere(String boxName, String forward)
	{
		if(!getOurOpenBoxes(boxName).containsKey(postalBranch()))
		{
			CMLib.database().DBCreateData(boxName,
					postalChain(),
					postalBranch()+"/"+forward,
					"50");
		}
	}

	@Override
	public void deleteBoxHere(String boxName)
	{
		final List<PlayerData> V=getBoxRowPDData(boxName);
		if(V==null)
			return;
		for(int v=0;v<V.size();v++)
		{
			final DatabaseEngine.PlayerData PD=V.get(v);
			if((PD!=null)
			&&(PD.key().startsWith(postalBranch()+"/")))
			{
				CMLib.database().DBDeleteData(PD.who(),PD.section(),PD.key());
			}
		}
	}

	public Vector<PlayerData> getAllLocalBoxPD(String boxName)
	{
		final List<PlayerData> V=getBoxRowPDData(boxName);
		final Vector<PlayerData> mine=new Vector<PlayerData>();
		for(int v=0;v<V.size();v++)
		{
			final DatabaseEngine.PlayerData PD=V.get(v);
			if((PD!=null)
			&&(PD.key().startsWith(postalBranch()+";")))
			{
				mine.addElement(PD);
			}
		}
		return mine;
	}

	public List<PlayerData> getBoxRowPDData(String mob)
	{
		return CMLib.database().DBReadData(mob,postalChain());
	}

	@Override
	public Item findBoxContents(String boxName, String likeThis)
	{
		final List<PlayerData> V=getBoxRowPDData(boxName);
		for(int v=0;v<V.size();v++)
		{
			final DatabaseEngine.PlayerData PD=V.get(v);
			if((PD!=null)
			&&(PD.key().startsWith(postalBranch()+";")))
			{
				final Item I=makeItem(parsePostalItemData(PD.xml()));
				if(I==null)
					continue;
				if(CMLib.english().containsString(I.Name(),likeThis))
					return I;
			}
		}
		return null;
	}

	public MailPiece findExactBoxData(String boxName, Item likeThis)
	{
		final List<PlayerData> V=getBoxRowPDData(boxName);
		for(int v=0;v<V.size();v++)
		{
			final DatabaseEngine.PlayerData PD=V.get(v);
			if((PD!=null)
			&&(PD.key().startsWith(postalBranch()+";")))
			{
				final Item I=makeItem(parsePostalItemData(PD.xml()));
				if(I==null)
					continue;
				if(I.sameAs(likeThis))
					return parsePostalItemData(PD.xml());
			}
		}
		return null;
	}

	@Override
	public MailPiece parsePostalItemData(String data)
	{
		final MailPiece piece = new MailPiece();
		for(int i=0;i<5;i++)
		{
			final int x=data.indexOf(';');
			if(x<0)
			{
				Log.errOut("StdPostman","Man formed postal data: "+data);
				return null;
			}
			switch(i)
			{
			case 0:
				piece.from = data.substring(0, x);
				break;
			case 1:
				piece.to = data.substring(0, x);
				break;
			case 2:
				piece.time = data.substring(0, x);
				break;
			case 3:
				piece.cod = data.substring(0, x);
				break;
			case 4:
				piece.classID = data.substring(0, x);
				break;
			}
			data=data.substring(x+1);
		}
		piece.xml = data;
		return piece;
	}

	protected Item makeItem(MailPiece data)
	{
		if(data ==  null)
			return null;
		final Item I=CMClass.getItem(data.classID);
		if(I!=null)
		{
			CMLib.coffeeMaker().setPropertiesStr(I,data.xml,true);
			I.recoverPhyStats();
			I.text();
			return I;
		}
		return null;
	}

	protected int getChargeableWeight(Item I)
	{
		if(I==null)
			return 0;
		int chargeableWeight=0;
		if(I.phyStats().weight()>0)
			chargeableWeight=(I.phyStats().weight()-1);
		return chargeableWeight;
	}

	protected double getSimplePostage(int chargeableWeight)
	{
		if(getStartRoom()==null)
			return 0.0;
		return minimumPostage()+CMath.mul(postagePerPound(),chargeableWeight);
	}

	protected double getHoldingCost(MailPiece data, int chargeableWeight)
	{
		if(data== null)
			return 0.0;
		if(getStartRoom()==null)
			return 0.0;
		double amt=0.0;
		final TimeClock TC=CMLib.time().localClock(getStartRoom());
		final long time=System.currentTimeMillis()-CMath.s_long(data.time);
		final long millisPerMudMonth=TC.getDaysInMonth()*CMProps.getMillisPerMudHour()*TC.getHoursInDay();
		if(time<=0)
			return amt;
		amt+=CMath.mul(CMath.mul(Math.floor(CMath.div(time,millisPerMudMonth)),holdFeePerPound()),chargeableWeight);
		return amt;
	}

	protected double getCODChargeForPiece(MailPiece data)
	{
		if(data==null)
			return 0.0;
		final int chargeableWeight=getChargeableWeight(makeItem(data));
		final double COD=CMath.s_double(data.cod);
		double amt=0.0;
		if(COD>0.0)
			amt=getSimplePostage(chargeableWeight)+COD;
		return amt+getHoldingCost(data,chargeableWeight);
	}

	protected String getBranchPostableTo(String toWhom, String branch, Map<String, String> allBranchBoxes)
	{
		String forward=allBranchBoxes.get(branch);
		if(forward==null)
			return null;
		if(forward.equalsIgnoreCase(toWhom))
			return branch;
		final PostOffice P=CMLib.map().getPostOffice(postalChain(),forward);
		if(P!=null)
		{
			forward=allBranchBoxes.get(P.postalBranch());
			if((forward!=null)&&forward.equalsIgnoreCase(toWhom))
				return P.postalBranch();
		}
		return null;
	}

	@Override
	public String findProperBranch(String toWhom)
	{
		if(CMLib.players().getLoadPlayer(toWhom)!=null)
		{
			final MOB M=CMLib.players().getLoadPlayer(toWhom);
			if(M.getStartRoom()!=null)
			{
				final Map<String,String> allBranchBoxes=getOurOpenBoxes(toWhom);
				final PostOffice P=CMLib.map().getPostOffice(postalChain(),M.getStartRoom().getArea().Name());
				String branch=null;
				if(P!=null)
				{
					branch=getBranchPostableTo(toWhom,P.postalBranch(),allBranchBoxes);
					if(branch!=null)
						return branch;
					if(allBranchBoxes.size()==0)
					{
						P.createBoxHere(toWhom,toWhom);
						return P.postalBranch();
					}
				}
				branch=getBranchPostableTo(toWhom,postalBranch(),allBranchBoxes);
				if(branch!=null)
					return branch;
				for(final String tryBranch : allBranchBoxes.keySet())
				{
					branch=getBranchPostableTo(toWhom,tryBranch,allBranchBoxes);
					if(branch!=null)
						return branch;
				}
				if(P!=null)
				{
					P.deleteBoxHere(toWhom);
					P.createBoxHere(toWhom,toWhom);
					return P.postalBranch();
				}
			}
		}
		else
		if(CMLib.clans().getClan(toWhom)!=null)
		{
			final Map<String,String> allBranchBoxes=getOurOpenBoxes(toWhom);
			String branch=getBranchPostableTo(toWhom,postalBranch(),allBranchBoxes);
			if(branch!=null)
				return branch;
			for(final String tryBranch : allBranchBoxes.keySet())
			{
				branch=getBranchPostableTo(toWhom,tryBranch,allBranchBoxes);
				if(branch!=null)
					return branch;
			}
		}
		return null;
	}

	public long postalWaitTime()
	{
		if((postalWaitTime<0)&&(getStartRoom()!=null))
			postalWaitTime=10038;
		else
		if((postalWaitTime==10038)&&(getStartRoom()!=null))
			postalWaitTime=(getStartRoom().getArea().getTimeObj().getHoursInDay())*CMProps.getMillisPerMudHour();
		return postalWaitTime;
	}

	@Override
	public boolean tick(Tickable ticking, int tickID)
	{
		if(!super.tick(ticking,tickID))
			return false;
		if(!CMProps.getBoolVar(CMProps.Bool.MUDSTARTED))
			return true;

		if((tickID==Tickable.TICKID_MOB)&&(getStartRoom()!=null))
		{
			boolean proceed=false;
			// handle interest by watching the days go by...
			// each chain is handled by one branch,
			// since pending mail all looks the same.
			Long L=postalTimes.get(postalChain()+"/"+postalBranch());
			if((L==null)||(L.longValue()<System.currentTimeMillis()))
			{
				proceed=(L!=null);
				L=Long.valueOf(System.currentTimeMillis()+postalWaitTime());
				postalTimes.remove(postalChain()+"/"+postalBranch());
				postalTimes.put(postalChain()+"/"+postalBranch(),L);
			}
			if(proceed)
			{
				List<PlayerData> V=getBoxRowPDData(postalChain());
				// first parse all the pending mail,
				// and remove it from the sorter
				final Vector<MailPiece> parsed=new Vector<MailPiece>();
				if(V==null)
					V=new Vector<PlayerData>();
				for(int v=0;v<V.size();v++)
				{
					final DatabaseEngine.PlayerData PD=V.get(v);
					parsed.addElement(parsePostalItemData(PD.xml()));
					CMLib.database().DBDeleteData(PD.who(),PD.section(),PD.key());
				}
				PostOffice P=null;
				for(int v=0;v<parsed.size();v++)
				{
					final MailPiece V2=parsed.elementAt(v);
					final String toWhom=V2.to;
					String deliveryBranch=findProperBranch(toWhom);
					if(deliveryBranch!=null)
					{
						P=CMLib.map().getPostOffice(postalChain(),deliveryBranch);
						final Item I=makeItem(V2);
						if((P!=null)&&(I!=null))
						{
							P.addToBox(toWhom,I,V2.from,V2.to,CMath.s_long(V2.time),CMath.s_double(V2.cod));
							continue;
						}
					}
					final String fromWhom=V2.from;
					deliveryBranch=findProperBranch(fromWhom);
					if(deliveryBranch!=null)
					{
						P=CMLib.map().getPostOffice(postalChain(),deliveryBranch);
						final Item I=makeItem(V2);
						if((P!=null)&&(I!=null))
							P.addToBox(fromWhom,I,V2.to,"POSTMASTER",System.currentTimeMillis(),0.0);
					}
				}
				V=CMLib.database().DBReadData(postalChain());
				TimeClock TC=null;
				if(getStartRoom()!=null)
					TC=getStartRoom().getArea().getTimeObj();
				if((TC!=null)&&(maxMudMonthsHeld()>0))
				for(int v=0;v<V.size();v++)
				{
					final DatabaseEngine.PlayerData V2=V.get(v);
					if(V2.key().startsWith(postalBranch()+";"))
					{
						final MailPiece data=parsePostalItemData(V2.xml());
						if((data!=null)&&(getStartRoom()!=null))
						{
							final long time=System.currentTimeMillis()-CMath.s_long(data.time);
							final long millisPerMudMonth=TC.getDaysInMonth()*CMProps.getMillisPerMudHour()*TC.getHoursInDay();
							if(time>0)
							{
								final int months=(int)Math.round(Math.floor(CMath.div(time,millisPerMudMonth)));
								if(months>maxMudMonthsHeld())
								{
									final Item I=makeItem(data);
									CMLib.database().DBDeleteData(V2.who(),V2.section(),V2.key());
									if(I!=null)
										getShop().addStoreInventory(I);
								}
							}
						}
					}
				}
			}
		}
		return true;
	}

	public void autoGive(MOB src, MOB tgt, Item I)
	{
		CMMsg msg2=CMClass.getMsg(src,I,null,CMMsg.MSG_DROP|CMMsg.MASK_INTERMSG,null,CMMsg.MSG_DROP|CMMsg.MASK_INTERMSG,null,CMMsg.MSG_DROP|CMMsg.MASK_INTERMSG,null);
		location().send(this,msg2);
		msg2=CMClass.getMsg(tgt,I,null,CMMsg.MSG_GET|CMMsg.MASK_INTERMSG,null,CMMsg.MSG_GET|CMMsg.MASK_INTERMSG,null,CMMsg.MSG_GET|CMMsg.MASK_INTERMSG,null);
		location().send(this,msg2);
	}

	@Override
	public void executeMsg(final Environmental myHost, final CMMsg msg)
	{
		final MOB mob=msg.source();
		if(msg.amITarget(this))
		{
			switch(msg.targetMinor())
			{
			case CMMsg.TYP_GIVE:
			case CMMsg.TYP_DEPOSIT:
				if(CMLib.flags().isAliveAwakeMobileUnbound(mob,true))
				{
					if(msg.tool() instanceof Container)
						((Container)msg.tool()).emptyPlease(true);
					final Session S=msg.source().session();
					if((!msg.source().isMonster())&&(S!=null)&&(msg.tool() instanceof Item))
					{
						autoGive(msg.source(),this,(Item)msg.tool());
						if(isMine(msg.tool()))
						{
							final StdPostman me=this;
							S.prompt(new InputCallback(InputCallback.Type.PROMPT,"",0)
							{
								@Override
								public void showPrompt()
								{
									S.promptPrint(L("Address this to whom? "));
								}

								@Override
								public void timedOut()
								{
									autoGive(me,msg.source(),(Item)msg.tool());
								}

								@Override public void callBack()
								{
									if((this.input!=null)&&(this.input.length()>0)
									&&((CMLib.players().getLoadPlayer(this.input)!=null)||(CMLib.clans().findClan(this.input)!=null)))
									{
										final String fromWhom=getSenderName(msg.source(),Clan.Function.DEPOSIT,false);
										final String toWhom;
										if(CMLib.players().getLoadPlayer(this.input)!=null)
											toWhom=CMLib.players().getLoadPlayer(this.input).Name();
										else
											toWhom=CMLib.clans().findClan(this.input).name();
										final double amt=getSimplePostage(getChargeableWeight((Item)msg.tool()));
										S.prompt(new InputCallback(InputCallback.Type.CHOOSE,"P","CP\n",0)
										{
											@Override public void showPrompt() {
												S.promptPrint(L("Postage on this will be @x1.\n\rWould you like to P)ay this now, or be C)harged on delivery (c/P)?",CMLib.beanCounter().nameCurrencyShort(me,amt)));
											}

											@Override
											public void timedOut()
											{
												autoGive(me,msg.source(),(Item)msg.tool());
											}

											@Override public void callBack()
											{
												final String choice=this.input.trim().toUpperCase();
												if(choice.startsWith("C"))
												{
													S.prompt(new InputCallback(InputCallback.Type.PROMPT,"",0)
													{
														@Override
														public void showPrompt()
														{
															S.promptPrint(L("Enter COD amount (@x1): ",CMLib.beanCounter().getDenominationName(CMLib.beanCounter().getCurrency(me),CMLib.beanCounter().getLowestDenomination(CMLib.beanCounter().getCurrency(me)))));
														}

														@Override
														public void timedOut()
														{
															autoGive(me,msg.source(),(Item)msg.tool());
														}

														@Override public void callBack()
														{
															final String CODstr=this.input;
															if((CODstr.length()==0)||(!CMath.isNumber(CODstr))||(CMath.s_double(CODstr)<=0.0))
															{
																CMLib.commands().postSay(me,mob,L("That is not a valid amount."),true,false);
																autoGive(me,msg.source(),(Item)msg.tool());
															}
															else
															{
																final Coins currency=CMLib.beanCounter().makeBestCurrency(CMLib.beanCounter().getCurrency(me),CMLib.beanCounter().getLowestDenomination(CMLib.beanCounter().getCurrency(me))*(CMath.s_double(CODstr)));
																final double COD=currency.getTotalValue();
																addToBox(postalChain(),(Item)msg.tool(),fromWhom,toWhom,System.currentTimeMillis(),COD);
																CMLib.commands().postSay(me,mob,L("I'll deliver that for ya right away!"),true,false);
																((Item)msg.tool()).destroy();
															}
														}
													});
												}
												else
												if((amt>0.0)&&(CMLib.beanCounter().getTotalAbsoluteShopKeepersValue(msg.source(),me)<amt))
												{
													CMLib.commands().postSay(me,mob,L("You can't afford postage."),true,false);
													autoGive(me,msg.source(),(Item)msg.tool());
												}
												else
												{
													CMLib.beanCounter().subtractMoney(mob,CMLib.beanCounter().getCurrency(me),amt);
													addToBox(postalChain(),(Item)msg.tool(),fromWhom,toWhom,System.currentTimeMillis(),0.0);
													CMLib.commands().postSay(me,mob,L("I'll deliver that for ya right away!"),true,false);
													((Item)msg.tool()).destroy();
												}
											}
										});

									}
									else
									{
										CMLib.commands().postSay(me,mob,L("That is not a valid player or clan name."),true,false);
										autoGive(me,msg.source(),(Item)msg.tool());
									}
								}
							});
						}
						else
							CMLib.commands().postSay(this,mob,L("I can't seem to deliver @x1.",msg.tool().name()),true,false);
					}
				}
				return;
			case CMMsg.TYP_WITHDRAW:
				if(CMLib.flags().isAliveAwakeMobileUnbound(mob,true))
				{
					final String fromWhom=getSenderName(msg.source(),Clan.Function.WITHDRAW,false);
					final Item old=(Item)msg.tool();
					MailPiece data=findExactBoxData(fromWhom,(Item)msg.tool());
					if((data==null)
					&&(!isSold(ShopKeeper.DEAL_CLANPOSTMAN))
					&&(msg.source().isMarriedToLiege()))
						data=findExactBoxData(msg.source().getLiegeID(),(Item)msg.tool());
					if(data==null)
						CMLib.commands().postSay(this,mob,L("You want WHAT? Try LIST."),true,false);
					else
					{
						if((!delFromBox(fromWhom,old))
						&&(!isSold(ShopKeeper.DEAL_CLANPOSTMAN))
						&&(msg.source().isMarriedToLiege()))
							delFromBox(msg.source().getLiegeID(),old);
						final double totalCharge=getCODChargeForPiece(data);
						if((totalCharge>0.0)&&(CMLib.beanCounter().getTotalAbsoluteShopKeepersValue(msg.source(),this)>=totalCharge))
						{

							CMLib.beanCounter().subtractMoney(msg.source(),totalCharge);
							final double COD=CMath.s_double(data.cod);
							Coins returnMoney=null;
							if(COD>0.0)
								returnMoney=CMLib.beanCounter().makeBestCurrency(this,COD);
							if(returnMoney!=null)
							{
								CMLib.commands().postSay(this,mob,L("The COD amount of @x1 has been sent back to @x2.",returnMoney.Name(),data.from),true,false);
								addToBox(postalChain(),returnMoney,data.to,data.from,System.currentTimeMillis(),0.0);
								CMLib.commands().postSay(this,mob,L("The total charge on that was a COD charge of @x1 plus @x2 postage and holding fees.",returnMoney.Name(),CMLib.beanCounter().nameCurrencyShort(this,totalCharge-COD)),true,false);
							}
							else
								CMLib.commands().postSay(this,mob,L("The total charge on that was @x1 in holding/storage fees.",CMLib.beanCounter().nameCurrencyShort(this,totalCharge)),true,false);
						}
						CMLib.commands().postSay(this,mob,L("There ya go!"),true,false);
						if(location()!=null)
							location().addItem(old,ItemPossessor.Expire.Player_Drop);
						final CMMsg msg2=CMClass.getMsg(mob,old,this,CMMsg.MSG_GET,null);
						if(location().okMessage(mob,msg2))
							location().send(mob,msg2);
					}
				}
				return;
			case CMMsg.TYP_VALUE:
			case CMMsg.TYP_SELL:
			case CMMsg.TYP_VIEW:
				super.executeMsg(myHost,msg);
				return;
			case CMMsg.TYP_BUY:
				super.executeMsg(myHost,msg);
				return;
			case CMMsg.TYP_SPEAK:
			{
				super.executeMsg(myHost,msg);
				String str=CMStrings.getSayFromMessage(msg.targetMessage());
				if((str!=null)&&(str.trim().equalsIgnoreCase("open")))
				{
					final String theName=getSenderName(msg.source(),Clan.Function.DEPOSIT,false);
					if(getOurOpenBoxes(theName).containsKey(postalBranch()))
					{
						if(isSold(ShopKeeper.DEAL_CLANPOSTMAN))
							CMLib.commands().postSay(this,mob,L("@x1 already has a box open here!",CMStrings.capitalizeFirstLetter(theName)),true,false);
						else
							CMLib.commands().postSay(this,mob,L("You already have a box open here!"),true,false);
					}
					else
					if(feeForNewBox()>0.0)
					{
						if(CMLib.beanCounter().getTotalAbsoluteShopKeepersValue(msg.source(),this)<feeForNewBox())
						{
							CMLib.commands().postSay(this,mob,L("Too bad you can't afford it."),true,false);
							return;
						}
						CMLib.beanCounter().subtractMoney(msg.source(),CMLib.beanCounter().getCurrency(this),feeForNewBox());
						createBoxHere(theName,theName);
						if(isSold(ShopKeeper.DEAL_CLANPOSTMAN))
							CMLib.commands().postSay(this,mob,L("A box has been opened for @x1.",CMStrings.capitalizeFirstLetter(theName)),true,false);
						else
							CMLib.commands().postSay(this,mob,L("A box has been opened for you."),true,false);
					}
					else
					{
						createBoxHere(theName,theName);
						if(isSold(ShopKeeper.DEAL_CLANPOSTMAN))
							CMLib.commands().postSay(this,mob,L("A box has been opened for @x1.",CMStrings.capitalizeFirstLetter(theName)),true,false);
						else
							CMLib.commands().postSay(this,mob,L("A box has been opened for you."),true,false);
					}
				}
				else
				if((str!=null)&&(str.trim().equalsIgnoreCase("close")))
				{
					final String theName=getSenderName(msg.source(),Clan.Function.WITHDRAW,false);
					if(!getOurOpenBoxes(theName).containsKey(postalBranch()))
					{
						if(isSold(ShopKeeper.DEAL_CLANPOSTMAN))
							CMLib.commands().postSay(this,mob,L("@x1 does not have a box here!",CMStrings.capitalizeFirstLetter(theName)),true,false);
						else
							CMLib.commands().postSay(this,mob,L("You don't have a box open here!"),true,false);
					}
					else
					if(getAllLocalBoxPD(theName).size()>0)
						CMLib.commands().postSay(this,mob,L("That box has pending items which must be removed first."),true,false);
					else
					{
						deleteBoxHere(theName);
						CMLib.commands().postSay(this,mob,L("That box is now closed."),true,false);
					}
				}
				else
				if((str!=null)&&(str.toUpperCase().trim().startsWith("FORWARD")))
				{
					final String theName=getSenderName(msg.source(),Clan.Function.WITHDRAW,false);
					str=str.trim().substring(7).trim();
					if(!getOurOpenBoxes(theName).containsKey(postalBranch()))
					{
						if(isSold(ShopKeeper.DEAL_CLANPOSTMAN))
							CMLib.commands().postSay(this,mob,L("@x1 does not have a box here!",CMStrings.capitalizeFirstLetter(theName)),true,false);
						else
							CMLib.commands().postSay(this,mob,L("You don't have a box open here!"),true,false);
					}
					else
					if(str.toUpperCase().equalsIgnoreCase("STOP") || str.toUpperCase().equalsIgnoreCase("UNFORWARD") || str.toUpperCase().equalsIgnoreCase("END"))
					{
						final Map<String,String> allOpenBoxes = getOurOpenBoxes(theName);
						if(allOpenBoxes.containsKey(postalBranch()))
						{
							deleteBoxHere(theName);
							CMLib.commands().postSay(this,mob,L("Ok, mail send here will no longer be forwarded anywhere."),true,false);
						}
					}
					else
					{
						final Area A=CMLib.map().findAreaStartsWith(str);
						if(A==null)
						{
							CMLib.commands().postSay(this,mob,L("I don't know of an area called '@x1' to forward to.",str),true,false);
							final Map<String,String> allOpenBoxes = getOurOpenBoxes(theName);
							if(allOpenBoxes.containsKey(postalBranch()))
								CMLib.commands().postSay(this,mob,L("Top stop forwarding, try FORWARD STOP."),true,false);
						}
						else
						{
							final PostOffice P=CMLib.map().getPostOffice(postalChain(),A.Name());
							if(P==null)
								CMLib.commands().postSay(this,mob,L("I'm sorry, we don't have a branch in @x1.",A.name()),true,false);
							else
							if(!P.getOurOpenBoxes(theName).containsKey(P.postalBranch()))
							{
								if(isSold(ShopKeeper.DEAL_CLANPOSTMAN))
									CMLib.commands().postSay(this,mob,L("I'm sorry, @x1 does not have a box at our branch in @x2.",CMStrings.capitalizeFirstLetter(theName),A.name()),true,false);
								else
									CMLib.commands().postSay(this,mob,L("I'm sorry, you don't have a box at our branch in @x1.",A.name()),true,false);
							}
							else
							{
								deleteBoxHere(theName);
								createBoxHere(theName,P.postalBranch());
								CMLib.commands().postSay(this,mob,L("Ok, mail will now be forwarded to our branch in @x1.",A.name()),true,false);
							}
						}
					}
				}
				return;
			}
			case CMMsg.TYP_LIST:
			{
				super.executeMsg(myHost,msg);
				if(CMLib.flags().isAliveAwakeMobileUnbound(mob,true))
				{
					Vector<PlayerData> V=null;
					final String theName=getSenderName(msg.source(),Clan.Function.DEPOSIT_LIST,false);
					if(isSold(ShopKeeper.DEAL_CLANPOSTMAN))
						V=getAllLocalBoxPD(theName);
					else
					{
						V=getAllLocalBoxPD(theName);
						if(mob.isMarriedToLiege())
						{
							final Vector<PlayerData> PDV=getAllLocalBoxPD(mob.getLiegeID());
							if((PDV!=null)&&(PDV.size()>0))
								V.addAll(PDV);
						}
					}

					TimeClock C=CMLib.time().localClock(getStartRoom());
					boolean codCharge=false;
					if(V.size()==0)
						mob.tell(L("\n\rYour postal box is presently empty."));
					else
					{
						StringBuffer str=new StringBuffer("");
						str.append(L("\n\rItems in your postal box here:\n\r"));
						str.append(L("^x[COD     ][From           ][Sent           ][Item                        ]^.^N"));
						mob.tell(str.toString());
						for(int i=0;i<V.size();i++)
						{
							final DatabaseEngine.PlayerData PD=V.elementAt(i);
							final MailPiece pieces=parsePostalItemData(PD.xml());
							final Item I=makeItem(pieces);
							if(I==null)
								continue;
							str=new StringBuffer("^N");
							if(getCODChargeForPiece(pieces)>0.0)
							{
								codCharge=true;
								str.append("["+CMStrings.padRight(""+CMLib.beanCounter().abbreviatedPrice(this,getCODChargeForPiece(pieces)),8)+"]");
							}
							else
								str.append("[        ]");
							str.append("["+CMStrings.padRight(pieces.from,15)+"]");
							final TimeClock C2=C.deriveClock(CMath.s_long(pieces.time));
							str.append("["+CMStrings.padRight(C2.getShortestTimeDescription(),15)+"]");
							str.append("["+CMStrings.padRight(I.Name(),28)+"]");
							mob.tell(str.toString()+"^T");
						}
					}
					final StringBuffer str=new StringBuffer("\n\r^N");
					if(codCharge)
						str.append(L("* COD charges above include all shipping costs.\n\r"));
					str.append(L("* This branch charges minimum @x1 postage for first pound.\n\r",CMLib.beanCounter().nameCurrencyShort(this,minimumPostage())));
					str.append(L("* An additional @x1 per pound is charged for packages.\n\r",CMLib.beanCounter().nameCurrencyShort(this,postagePerPound())));
					str.append(L("* A charge of @x1 per pound per month is charged for holding.\n\r",CMLib.beanCounter().nameCurrencyShort(this,holdFeePerPound())));
					str.append("* To forward your mail, 'say \""+name()+"\" \"forward <areaname>\"'.\n\r");
					str.append(L("* To close your box, 'say \"@x1\" close'.\n\r",name()));
					mob.tell(str.toString());
				}
				return;
			}
			default:
				break;
			}
		}
		else
		if(msg.sourceMinor()==CMMsg.TYP_RETIRE)
			emptyBox(msg.source().Name());
		super.executeMsg(myHost,msg);
	}

	@Override
	public boolean okMessage(final Environmental myHost, final CMMsg msg)
	{
		final MOB mob=msg.source();
		if((msg.targetMinor()==CMMsg.TYP_EXPIRE)
		&&(msg.target()==location())
		&&(CMLib.flags().isInTheGame(this,true)))
			return false;
		else
		if(msg.amITarget(this))
		{
			switch(msg.targetMinor())
			{
			case CMMsg.TYP_GIVE:
			case CMMsg.TYP_DEPOSIT:
				{
					if(!CMLib.coffeeShops().ignoreIfNecessary(msg.source(),finalIgnoreMask(),this))
						return false;
					if(msg.tool()==null)
						return false;
					final String senderName=getSenderName(msg.source(),Clan.Function.DEPOSIT,true);
					if(senderName==null)
						return false;
					if(!(msg.tool() instanceof Item))
					{
						mob.tell(L("@x1 doesn't look interested.",mob.charStats().HeShe()));
						return false;
					}
					if(CMLib.flags().isEnspelled((Item)msg.tool()) || CMLib.flags().isOnFire((Item)msg.tool()))
					{
						mob.tell(this,msg.tool(),null,L("<S-HE-SHE> refuses to accept <T-NAME> for delivery."));
						return false;
					}
				}
				return true;
			case CMMsg.TYP_WITHDRAW:
				{
					if(!CMLib.coffeeShops().ignoreIfNecessary(msg.source(),finalIgnoreMask(),this))
						return false;
					final String senderName=getSenderName(msg.source(),Clan.Function.WITHDRAW,true);
					if(senderName==null)
						return false;
					if((msg.tool()==null)||(!(msg.tool() instanceof Item)))
					{
						CMLib.commands().postSay(this,mob,L("What do you want? I'm busy!"),true,false);
						return false;
					}
					if((msg.tool()!=null)&&(!msg.tool().okMessage(myHost,msg)))
						return false;
					MailPiece data=findExactBoxData(senderName,(Item)msg.tool());
					if((data==null)
					&&(!isSold(ShopKeeper.DEAL_CLANPOSTMAN))
					&&(msg.source().isMarriedToLiege()))
						data=findExactBoxData(msg.source().getLiegeID(),(Item)msg.tool());
					if(data==null)
					{
						CMLib.commands().postSay(this,mob,L("You want WHAT? Try LIST."),true,false);
						return false;
					}
					final double totalCharge=getCODChargeForPiece(data);
					if(CMLib.beanCounter().getTotalAbsoluteShopKeepersValue(msg.source(),this)<totalCharge)
					{
						CMLib.commands().postSay(this,mob,L("The total charge to receive that item is @x1. You don't have enough.",CMLib.beanCounter().nameCurrencyShort(this,totalCharge)),true,false);
						return false;
					}
				}
				return true;
			case CMMsg.TYP_VALUE:
			case CMMsg.TYP_SELL:
			case CMMsg.TYP_VIEW:
				return super.okMessage(myHost,msg);
			case CMMsg.TYP_BUY:
				return super.okMessage(myHost,msg);
			case CMMsg.TYP_LIST:
			{
				if(!CMLib.coffeeShops().ignoreIfNecessary(msg.source(),finalIgnoreMask(),this))
					return false;
				final String senderName=getSenderName(msg.source(),Clan.Function.DEPOSIT_LIST,true);
				if(senderName==null)
					return false;
				if((!getOurOpenBoxes(senderName).containsKey(postalBranch()))
				&&((isSold(ShopKeeper.DEAL_CLANPOSTMAN))
				   ||(!msg.source().isMarriedToLiege())
				   ||(!getOurOpenBoxes(msg.source().getLiegeID()).containsKey(postalBranch()))))
				{
					if((!isSold(ShopKeeper.DEAL_CLANPOSTMAN))
					&&(msg.source().getStartRoom().getArea()==getStartRoom().getArea()))
					{
						createBoxHere(msg.source().Name(),msg.source().Name());
						return true;
					}
					final StringBuffer str=new StringBuffer("");
					if(isSold(ShopKeeper.DEAL_CLANPOSTMAN))
						str.append(L("@x1 does not have a postal box at this branch, I'm afraid.",CMStrings.capitalizeFirstLetter(senderName)));
					else
						str.append(L("You don't have a postal box at this branch, I'm afraid."));
					if(postalChain().length()>0)
						str.append(L("\n\rThis branch is part of the @x1 postal chain.",postalChain()));
					CMLib.commands().postSay(this,mob,str.toString()+"^T",true,false);
					mob.tell(L("Use 'say \"@x1\" open' to open a box here@x2",name(),((feeForNewBox()<=0.0)?".":(" for "+CMLib.beanCounter().nameCurrencyShort(this,feeForNewBox())+"."))));
					return false;
				}
				return true;
			}
			default:
				break;
			}
		}
		return super.okMessage(myHost,msg);
	}
}