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

import java.util.*;

/*
   Copyright 2004-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.
*/
@SuppressWarnings({"unchecked","rawtypes"})
public class Auction extends Channel implements Tickable
{
	public Auction(){}

	private final String[]	access	= I(new String[] { "AUCTION" });

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

	@Override
	public String name()
	{
		return "Auction";
	}

	protected final static String MESSAGE_NOAUCTION()
	{
		return CMLib.lang().fullSessionTranslation("There is not currently a live auction.  Use AUCTION UP syntax to add one, or visit an auctioneer for a long auction.");
	}
	
	public String liveAuctionStatus()
	{
		if(getLiveData().getAuctionedItem()!=null)
		{
			String bidWords=CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),getLiveData().getBid());
			if(bidWords.length()==0)
				bidWords="0";
			return "Up for live auction: "+getLiveData().getAuctionedItem().name()+".  The current bid is "+bidWords+".";
		}
		return "";
	}
	
	protected AuctionData   liveAuctionData=null;
	
	protected AuctionData getLiveData()
	{
		if(liveAuctionData == null)
		{
			liveAuctionData = (AuctionData)CMClass.getCommon("DefaultAuction");
		}
		return liveAuctionData;
	}

	protected static final int STATE_START=0;
	protected static final int STATE_RUNOUT=1;
	protected static final int STATE_ONCE=2;
	protected static final int STATE_TWICE=3;
	protected static final int STATE_THREE=4;
	protected static final int STATE_CLOSED=5;

	@Override public int getTickStatus(){ return Tickable.STATUS_NOT;}

	public void setLiveAuctionState(int code)
	{
		getLiveData().setAuctionState(code);
		getLiveData().setAuctionTickDown(15000/CMProps.getTickMillis());
	}

	@Override
	public boolean tick(Tickable ticking, int tickID)
	{
		if(tickID==Tickable.TICKID_LIVEAUCTION)
		{
			getLiveData().setAuctionTickDown(getLiveData().getAuctionTickDown()-1);
			if(getLiveData().getAuctionTickDown()<=0)
			{
				final MOB auctioneerM=getLiveData().getAuctioningMob();
				final MOB winnerM=getLiveData().getHighBidderMob();
				if((getLiveData().getAuctionState()==STATE_START)&&((System.currentTimeMillis()-getLiveData().getStartTime())<(5*15000)))
				{
					if(((System.currentTimeMillis()-getLiveData().getStartTime())>(3*15000))
					&&((winnerM==null)||(winnerM==auctioneerM)))
						setLiveAuctionState(STATE_RUNOUT);
					else
						setLiveAuctionState(STATE_START);
					return true;
				}
				setLiveAuctionState(getLiveData().getAuctionState()+1);
				final Vector<String> V=new Vector<String>();
				V.add("AUCTION");
				V.add("CHANNEL");
				switch(getLiveData().getAuctionState())
				{
				case STATE_RUNOUT:
					V.add("The live auction for "+getLiveData().getAuctionedItem().name()+" is almost done. The current bid is "+CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),getLiveData().getBid())+".");
					break;
				case STATE_ONCE:
					V.add(CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),getLiveData().getBid())+" for "+getLiveData().getAuctionedItem().name()+" going ONCE!");
					break;
				case STATE_TWICE:
					V.add(CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),getLiveData().getBid())+" for "+getLiveData().getAuctionedItem().name()+" going TWICE!");
					break;
				case STATE_THREE:
					V.add(getLiveData().getAuctionedItem().name()+" going for "+CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),getLiveData().getBid())+"! Last chance!");
					break;
				case STATE_CLOSED:
					{
						if((winnerM!=null)&&(winnerM!=getLiveData().getAuctioningMob()))
						{
							V.add(getLiveData().getAuctionedItem().name()+" SOLD to "+winnerM.name()+" for "+CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),getLiveData().getBid())+".");
							auctioneerM.doCommand(V,MUDCmdProcessor.METAFLAG_FORCED);
							if(getLiveData().getAuctionedItem() != null)
							{
								getLiveData().getAuctionedItem().unWear();
								final AuctionPolicy aRates=(AuctionPolicy)CMClass.getCommon("DefaultAuctionPolicy");
								winnerM.location().moveItemTo(getLiveData().getAuctionedItem(),ItemPossessor.Expire.Player_Drop);
								final double houseCut=Math.floor(getLiveData().getBid()*aRates.liveFinalCutPct());
								final double finalAmount=getLiveData().getBid()-houseCut;
								CMLib.coffeeShops().returnMoney(winnerM,getLiveData().getCurrency(),getLiveData().getHighBid()-getLiveData().getBid());
								CMLib.coffeeShops().returnMoney(auctioneerM,getLiveData().getCurrency(),finalAmount);
								auctioneerM.tell(L("@x1 has been transferred to you as payment from @x2, after the house took a cut of @x3.  The goods have also been transferred in exchange.",CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),finalAmount),winnerM.name(auctioneerM),CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),houseCut)));
								if(CMLib.commands().postGet(winnerM,null,getLiveData().getAuctionedItem(),false)
								||(winnerM.isMine(getLiveData().getAuctionedItem())))
								{
									winnerM.tell(L("@x1 has been transferred to @x2.  You should have received the auctioned goods.  This auction is complete.",CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),getLiveData().getBid()),auctioneerM.name(winnerM)));
									if(getLiveData().getAuctionedItem() instanceof LandTitle)
									{
										final CMMsg msg=CMClass.getMsg(auctioneerM,winnerM,getLiveData().getAuctionedItem(),CMMsg.MASK_ALWAYS|CMMsg.TYP_GIVE,null);
										getLiveData().getAuctionedItem().executeMsg(winnerM,msg);
									}
								}
								else
								{
									auctioneerM.moveItemTo(getLiveData().getAuctionedItem());
									auctioneerM.tell(L("Your transaction could not be completed because @x1 was unable to collect the item.  Please contact @x2 about receipt of @x3 for @x4.",winnerM.name(auctioneerM),winnerM.name(auctioneerM),getLiveData().getAuctionedItem().name(winnerM),CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),getLiveData().getBid())));
									winnerM.tell(L("Your transaction could not be completed because you were unable to collect the item.  Please contact @x1 about receipt of @x2 for @x3.",auctioneerM.name(winnerM),getLiveData().getAuctionedItem().name(winnerM),CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),getLiveData().getBid())));
								}
							}
						}
						else
						if(!auctioneerM.isMine(getLiveData().getAuctionedItem()))
							auctioneerM.moveItemTo(getLiveData().getAuctionedItem());
						getLiveData().setAuctioningMob(null);
						getLiveData().setAuctionedItem(null);
						getLiveData().setHighBidderMob(null);
						getLiveData().setHighBid(0.0);
						getLiveData().setBid(0.0);
						getLiveData().setAuctionState(0);
						CMLib.threads().deleteTick(this,Tickable.TICKID_LIVEAUCTION);
					}
					return false;
				}
				auctioneerM.doCommand(V,MUDCmdProcessor.METAFLAG_FORCED);
			}
		}
		return true;
	}


	public boolean doLiveAuction(MOB mob, List<String> commands, Environmental target)
	{
		final Vector<String> V=new Vector<String>();
		V.add("AUCTION");
		V.add("CHANNEL");
		if(target!=null)
		{
			if(!(target instanceof Item))
				return false;
			getLiveData().setAuctioningMob(mob);
			getLiveData().setAuctionedItem((Item)target);
			final String sb=CMParms.combine(commands,0);
			getLiveData().setCurrency(CMLib.english().numPossibleGoldCurrency(mob,sb));
			if(getLiveData().getCurrency().length()==0)
				getLiveData().setCurrency(CMLib.beanCounter().getCurrency(mob));
			final double denomination=CMLib.english().numPossibleGoldDenomination(null,getLiveData().getCurrency(),sb);
			final long num=CMLib.english().numPossibleGold(null,sb);
			getLiveData().setBid(CMath.mul(denomination,num));
			getLiveData().setHighBid(getLiveData().getBid()-1);
			getLiveData().setStartTime(System.currentTimeMillis());
			setLiveAuctionState(STATE_START);
			CMLib.threads().startTickDown(this,Tickable.TICKID_LIVEAUCTION,1);
			final String bidWords=CMLib.beanCounter().nameCurrencyShort(getLiveData().getCurrency(),getLiveData().getBid());
			if(target instanceof Item)
				mob.delItem((Item)target);
			V.add("New live auction: "+getLiveData().getAuctionedItem().name()+".  The opening bid is "+bidWords+".");
			if(getLiveData().getAuctioningMob()!=null)
				getLiveData().getAuctioningMob().doCommand(V,MUDCmdProcessor.METAFLAG_FORCED);
		}
		else
		{
			if(getLiveData().getAuctionState()>0)
				setLiveAuctionState(STATE_RUNOUT);
			String sb="";
			if(commands!=null)
				sb=CMParms.combine(commands,0);
			final MOB M=getLiveData().getHighBidderMob();
			final Object[] bidObjs=CMLib.english().parseMoneyStringSDL(mob,sb,getLiveData().getCurrency());
			final String currency=(String)bidObjs[0];
			final double amt=CMath.mul(((Double)bidObjs[1]).doubleValue(),((Long)bidObjs[2]).doubleValue());
			final String[] resp=CMLib.coffeeShops().bid(mob,amt,currency,getLiveData(),getLiveData().getAuctionedItem(),V);
			if(resp!=null)
			{
				if(resp[0]!=null)
					mob.tell(resp[0]);
				if((resp[1]!=null)&&(M!=null))
					M.tell(resp[1]);
			}
			if((V.size()>2)
			&&(getLiveData().getAuctioningMob()!=null))
				getLiveData().getAuctioningMob().doCommand(V,MUDCmdProcessor.METAFLAG_FORCED);
		}
		return true;
	}

	public void auctionNotify(MOB M, String resp, String regardingItem)
	throws java.io.IOException
	{
		if(CMLib.flags().isInTheGame(M,true))
			M.tell(resp);
		else
		if(M.playerStats()!=null)
		{
			CMLib.smtp().emailIfPossible(CMProps.getVar(CMProps.Str.SMTPSERVERNAME),
											"auction@"+CMProps.getVar(CMProps.Str.MUDDOMAIN).toLowerCase(),
											"noreply@"+CMProps.getVar(CMProps.Str.MUDDOMAIN).toLowerCase(),
											M.playerStats().getEmail(),
											"Auction Update for item: "+regardingItem,
											resp);
		}
	}


	@Override
	public boolean execute(MOB mob, List<String> commands, int metaFlags)
		throws java.io.IOException
	{
		//mob.tell(L("Auctions are currently closed for maintenance.  When it re-opens, this command will continue to remain available for live auctions, and new auctioneer mobs will be placed in the major cities for doing multi-day auctions, so keep your eyes open for that coming soon!"));
		//if((mob!=null)||(commands!=null)) return false;
		final PlayerStats pstats=mob.playerStats();
		if(pstats==null)
			return false;
		final int channelInt=CMLib.channels().getChannelIndex("AUCTION");
		final int channelNum=CMLib.channels().getChannelCodeNumber("AUCTION");

		if(CMath.isSet(pstats.getChannelMask(),channelInt))
		{
			pstats.setChannelMask(pstats.getChannelMask()&(pstats.getChannelMask()-channelNum));
			mob.tell(L("The AUCTION channel has been turned on.  Use `NOAUCTION` to turn it off again."));
		}

		String cmd=null;
		commands.remove(0);
		if(commands.size()<1)
			cmd="";
		else
			cmd=commands.get(0).toUpperCase();

		if(cmd.equals("LIST"))
		{
			commands.remove(0);
			final StringBuffer buf=new StringBuffer("");
			if((getLiveData().getAuctionedItem()!=null)&&(getLiveData().getAuctioningMob()!=null))
			{
				buf.append(L("\n\r^HCurrent *live* auction: ^N\n\r"));
				buf.append(liveAuctionStatus()+"\n\r");
			}
			else
				buf.append(MESSAGE_NOAUCTION());
			mob.tell(buf.toString());
			return true;
		}
		else
		if(cmd.equals("UP"))
		{
			commands.remove(0);
			if((getLiveData().getAuctionedItem()!=null)&&(getLiveData().getAuctioningMob()!=null))
			{
				mob.tell(L("A live auction is already underway.  Do AUCTION LIST to see it."));
				return false;
			}
			final Vector<String> V=new Vector<String>();
			if((commands.size()>=2)
			&&((CMLib.english().numPossibleGold(mob,commands.get(commands.size()-1))>0)||(commands.get(commands.size()-1).equals("0"))))
			{
				V.add(commands.get(commands.size()-1));
				commands.remove(commands.size()-1);
			}
			else
				V.add("0");

			final String s=CMParms.combine(commands,0);
			final Environmental E=mob.findItem(null,s);
			if((E==null)||(E instanceof MOB))
			{
				mob.tell(L("@x1 is not an item you can auction.",s));
				return false;
			}
			if((E instanceof Container)&&(((Container)E).hasContent()))
			{
				mob.tell(L("@x1 will have to be emptied first.",E.name()));
				return false;
			}
			if(!(((Item)E).amWearingAt(Wearable.IN_INVENTORY)))
			{
				mob.tell(L("@x1 will have to be removed first.",E.name()));
				return false;
			}
			final AuctionPolicy aRates=(AuctionPolicy)CMClass.getCommon("DefaultAuctionPolicy");
			final double deposit=aRates.liveListingPrice();
			final String depositAmt=CMLib.beanCounter().nameCurrencyLong(mob, deposit);

			if(deposit>0.0)
			{
				if((mob.isMonster())
				||(!mob.session().confirm(L("Auctioning @x1 will cost a listing fee of @x2, proceed (Y/n)?",E.name(),depositAmt),"Y")))
					return false;
			}
			else
			if((mob.isMonster())
			||(!mob.session().confirm(L("Auction @x1 live, with a starting bid of @x2 (Y/n)?",E.name(),(V.get(0))),"Y")))
				return false;
			if(CMLib.beanCounter().getTotalAbsoluteValue(mob,CMLib.beanCounter().getCurrency(mob))<deposit)
			{
				mob.tell(L("You don't have enough @x1 to cover the listing fee!",CMLib.beanCounter().getDenominationName(CMLib.beanCounter().getCurrency(mob))));
				return false;
			}
			CMLib.beanCounter().subtractMoney(mob, CMLib.beanCounter().getCurrency(mob), deposit);
			doLiveAuction(mob,V,E);
			if(getLiveData().getAuctionedItem()!=null)
				getLiveData().setAuctioningMob(mob);
			return true;
		}
		else
		if(cmd.equals("BID"))
		{
			commands.remove(0);
			if((getLiveData().getAuctionedItem()==null)||(getLiveData().getAuctioningMob()==null))
			{
				mob.tell(MESSAGE_NOAUCTION());
				return false;
			}
			if(commands.size()<1)
			{
				mob.tell(L("Bid how much?"));
				return false;
			}
			final String amount=CMParms.combine(commands,0);
			doLiveAuction(mob,new XVector(amount),null);
			return true;
		}
		else
		if(cmd.equals("CLOSE"))
		{
			commands.remove(0);
			if((getLiveData().getAuctionedItem()==null)||(getLiveData().getAuctioningMob()==null))
			{
				mob.tell(MESSAGE_NOAUCTION());
				return false;
			}
			if((getLiveData().getAuctionedItem()==null)||(getLiveData().getAuctioningMob()!=mob))
			{
				mob.tell(L("You are not currently running a live auction."));
				return false;
			}
			final Vector<String> V=new Vector<String>();
			V.add("AUCTION");
			V.add("The auction has been closed.");
			CMLib.threads().deleteTick(this,Tickable.TICKID_LIVEAUCTION);
			getLiveData().getAuctioningMob().moveItemTo(getLiveData().getAuctionedItem());
			if((getLiveData().getHighBid()>0.0)&&(getLiveData().getHighBidderMob()!=null))
				CMLib.coffeeShops().returnMoney(getLiveData().getHighBidderMob(),getLiveData().getCurrency(),getLiveData().getHighBid());
			getLiveData().setAuctioningMob(null);
			getLiveData().setAuctionedItem(null);
			super.execute(mob,V,metaFlags);
			return true;
		}
		else
		if(cmd.equals("INFO"))
		{
			commands.remove(0);
			if((getLiveData().getAuctionedItem()==null)||(getLiveData().getAuctioningMob()==null))
			{
				mob.tell(MESSAGE_NOAUCTION());
				return false;
			}
			Environmental E=null;
			E=getLiveData().getAuctionedItem();
			mob.tell(L("Item: @x1",E.name()));
			CMLib.commands().handleBeingLookedAt(CMClass.getMsg(mob,CMMsg.MASK_ALWAYS|CMMsg.MSG_EXAMINE,null));
			mob.tell(CMLib.coffeeShops().getViewDescription(mob,E));
			return true;
		}
		else
		if(cmd.equals("CHANNEL"))
		{
			commands.remove(0);
			if(commands.size()==0)
			{
				mob.tell(L("Channel what?"));
				return false;
			}
			if((getLiveData().getAuctionedItem()==null)||(getLiveData().getAuctioningMob()==null))
			{
				mob.tell(L("Channeling is only allowed during live auctions."));
				return false;
			}
			commands.add(0,"AUCTION");
			super.execute(mob,commands,metaFlags);
			return true;
		}
		commands.add(0,"AUCTION");
		super.execute(mob,commands,metaFlags);
		return false;
	}


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


}