package com.planet_ink.coffee_mud.Behaviors;
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 2002-2022 Bo Zimmerman

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
public class MoneyChanger extends StdBehavior
{
	@Override
	public String ID()
	{
		return "MoneyChanger";
	}

	protected Map<String, Double>	rates						= new Hashtable<String, Double>();
	protected double				spaceMaxCut					= 0.0;
	protected long					spaceMaxDistance			= SpaceObject.Distance.GalaxyRadius.dm;
	protected double				cut							= 0.05;
	private boolean					complainedAboutSpaceError	= false;

	@Override
	public String accountForYourself()
	{
		return "money changing for a fee";
	}

	@Override
	public void startBehavior(final PhysicalAgent forMe)
	{
		if(forMe==null)
			return;
		if(!(forMe instanceof MOB))
			return;
		// i suppose this is for accepting heavy loads of coin
		((MOB)forMe).baseCharStats().setStat(CharStats.STAT_STRENGTH,100);
		super.startBehavior(forMe);
	}

	protected final Map<String,Double> getRatesFor(final Environmental affecting, String currency)
	{
		if(spaceMaxCut<=0.0)
			return rates;
		currency=currency.toUpperCase();
		if(rates.containsKey(currency))
			return rates;
		String myCurrency=CMLib.beanCounter().getCurrency(affecting);
		if(CMLib.beanCounter().isCurrencyMatch(myCurrency,currency))
		{
			rates.put(currency, Double.valueOf(cut));
			return rates;
		}
		final SpaceObject homeO=CMLib.map().getSpaceObject(affecting, false);
		if(homeO!=null)
		{
			myCurrency=CMLib.beanCounter().getCurrency(homeO);
			if(CMLib.beanCounter().isCurrencyMatch(myCurrency,currency))
			{
				rates.put(currency, Double.valueOf(cut));
				return rates;
			}
		}
		else
		{
			// no space object, wtf? this SHOULD fail
			if(!complainedAboutSpaceError)
			{
				complainedAboutSpaceError=true;
				Log.errOut("MoneyChanger",affecting.Name()+" is not on a planet, so space rates cannot apply!");
			}
			return rates;
		}
		for(final Enumeration<Area> a=CMLib.map().spaceAreas();a.hasMoreElements();)
		{
			final Area A=a.nextElement();
			if((A!=null)&&(A!=homeO))
			{
				myCurrency=CMLib.beanCounter().getCurrency(A);
				if(CMLib.beanCounter().isCurrencyMatch(myCurrency,currency))
				{
					final SpaceObject oA=(SpaceObject)A;
					final long distance=CMLib.map().getDistanceFrom(homeO, oA);
					if((distance<0)||(distance>spaceMaxDistance))
					{
						rates.put(currency, Double.valueOf(spaceMaxCut));
					}
					else
					{
						final double pct=CMath.div(distance, spaceMaxDistance);
						double amt=spaceMaxCut*pct;
						if(amt < cut)
							amt=cut;
						rates.put(currency, Double.valueOf(cut));
					}
					return rates;
				}
			}
		}

		return rates;
	}

	protected boolean doIExchangeThisCurrency(final Environmental affecting, final String currency)
	{
		final MoneyLibrary.MoneyDefinition def=CMLib.beanCounter().getCurrencySet(currency);
		if((def != null)&&(!def.canTrade()))
			return false;
		final Map<String,Double> rates=getRatesFor(affecting, currency);
		return ((rates.size()==0)||(rates.containsKey(currency.toUpperCase())));
	}

	protected double getMyCut(final Environmental affecting, final String currency)
	{
		final Map<String,Double> rates=getRatesFor(affecting,currency);
		if((rates.size()>0)&&(rates.containsKey(currency.toUpperCase())))
			return rates.get(currency.toUpperCase()).doubleValue();
		return cut;
	}

	@Override
	public void setParms(String newParm)
	{
		super.setParms(newParm);
		rates.clear();
		cut=0.05;
		spaceMaxCut=0.0;
		spaceMaxDistance=SpaceObject.Distance.GalaxyRadius.dm;
		newParm=newParm.toUpperCase();
		int x=newParm.indexOf('=');
		while(x>0)
		{
			int lastSp=newParm.lastIndexOf(' ',x);
			if(lastSp<0)
				lastSp=0;
			if((lastSp>=0)&&(lastSp<x-1)&&(Character.isLetter(newParm.charAt(x-1))))
			{
				String parm=newParm.substring(lastSp,x).trim().toUpperCase();
				while((x<newParm.length())&&(newParm.charAt(x)!='='))
					x++;
				if(x<newParm.length())
				{
					while((x<newParm.length())
					&&(!Character.isDigit(newParm.charAt(x)))
					&&(newParm.charAt(x)!='.'))
						x++;
					if(x<newParm.length())
					{
						newParm=newParm.substring(x);
						x=0;
						while((x<newParm.length())
						&&((Character.isDigit(newParm.charAt(x)))||(newParm.charAt(x)=='.')))
							x++;
						double val=CMath.s_double(newParm.substring(0,x));
						if(newParm.substring(0,x).indexOf('.')<0)
							val= CMath.s_long(newParm.substring(0,x));
						if(x<newParm.length())
							newParm=newParm.substring(x+1);
						else
							newParm="";
						if(parm.equalsIgnoreCase("default"))
							parm="";
						if(parm.equalsIgnoreCase("spacemaxcut"))
							spaceMaxCut=val/100.0;
						else
						if(parm.equalsIgnoreCase("spacemaxdistance"))
							spaceMaxDistance=Math.round(CMath.mul(SpaceObject.Distance.GalaxyRadius.dm, val/100.0));
						else
						if(parm.equalsIgnoreCase("cut"))
							cut=val/100.0;
						else
							rates.put(parm,Double.valueOf(val/100.0));
					}
				}
			}
			x=newParm.indexOf('=');
		}
	}

	@Override
	public boolean okMessage(final Environmental host, final CMMsg msg)
	{
		if(!super.okMessage(host,msg))
			return false;
		final MOB source=msg.source();
		if(!canFreelyBehaveNormal(host))
			return true;
		final MOB observer=(MOB)host;
		if((source!=observer)
		&&(msg.amITarget(observer))
		&&(msg.targetMinor()==CMMsg.TYP_GIVE)
		&&(!CMSecurity.isAllowed(source,source.location(),CMSecurity.SecFlag.CMDROOMS))
		&&(msg.tool()!=null))
		{
			if(!(msg.tool() instanceof Coins))
			{
				CMLib.commands().postSay(observer,source,L("I'm sorry, I can only accept money."),true,false);
				return false;
			}
			else
			if(!doIExchangeThisCurrency(host,((Coins)msg.tool()).getCurrency()))
			{
				CMLib.commands().postSay(observer,source,L("I'm sorry, I don't accept that kind of currency."),true,false);
				return false;
			}
			final MoneyLibrary.MoneyDefinition targetCurrencyDef=CMLib.beanCounter().getCurrencySet(CMLib.beanCounter().getCurrency(observer));
			if((targetCurrencyDef != null)&&(!targetCurrencyDef.canTrade()))
			{
				CMLib.commands().postSay(observer,source,L("I'm sorry, I can't convert into @x1.",CMStrings.capitalizeAndLower(targetCurrencyDef.ID())),true,false);
				return false;
			}
			double value=((Coins)msg.tool()).getTotalValue();
			final String currency=((Coins)msg.tool()).getCurrency().toUpperCase();
			final double takeCut=getMyCut(host,currency);
			double amountToTake=CMLib.beanCounter().abbreviatedRePrice(observer,value*takeCut);
			if((amountToTake>0.0)&&(amountToTake<CMLib.beanCounter().getLowestDenomination(CMLib.beanCounter().getCurrency(observer))))
				amountToTake=CMLib.beanCounter().getLowestDenomination(CMLib.beanCounter().getCurrency(observer));
			value-=amountToTake;
			observer.recoverPhyStats();
			final Coins C=CMLib.beanCounter().makeBestCurrency(observer,value);
			if((value<=0)||(C==null))
			{
				CMLib.commands().postSay(observer,source,L("I'm sorry, I can not change such a small amount."),true,false);
				return false;
			}
		}
		return true;
	}

	@Override
	public void executeMsg(final Environmental affecting, final CMMsg msg)
	{
		super.executeMsg(affecting,msg);
		final MOB source=msg.source();
		if(!canFreelyBehaveNormal(affecting))
			return;
		final MOB observer=(MOB)affecting;

		if((source!=observer)
		&&(msg.amITarget(observer))
		&&(msg.targetMinor()==CMMsg.TYP_GIVE)
		&&(msg.tool() instanceof Coins))
		{
			if((CMLib.flags().canBeSeenBy(source,observer))
			&&(CMLib.flags().canBeSeenBy(observer,source)))
			{
				double value=((Coins)msg.tool()).getTotalValue();
				final String currency=((Coins)msg.tool()).getCurrency().toUpperCase();
				final double takeCut=getMyCut(affecting,currency);
				double amountToTake=CMLib.beanCounter().abbreviatedRePrice(observer,value*takeCut);
				if((amountToTake>0.0)&&(amountToTake<CMLib.beanCounter().getLowestDenomination(CMLib.beanCounter().getCurrency(observer))))
					amountToTake=CMLib.beanCounter().getLowestDenomination(CMLib.beanCounter().getCurrency(observer));
				value-=amountToTake;
				observer.recoverPhyStats();
				final Coins C=CMLib.beanCounter().makeBestCurrency(observer,value);
				if((value>0.0)&&(C!=null))
				{
					// this message will actually end up triggering the hand-over.
					final CMMsg newMsg=CMClass.getMsg(observer,source,C,CMMsg.MSG_SPEAK,L("^T<S-NAME> say(s) 'Thank you for your business' to <T-NAMESELF>.^?"));
					C.setOwner(observer);
					final long num=C.getNumberOfCoins();
					final String curr=C.getCurrency();
					final double denom=C.getDenomination();
					C.destroy();
					C.setNumberOfCoins(num);
					C.setCurrency(curr);
					C.setDenomination(denom);
					msg.addTrailerMsg(newMsg);
				}
				else
					CMLib.commands().postSay(observer,source,L("Gee, thanks. :)"),true,false);
				((Coins)msg.tool()).destroy();
			}
			else
			if(!CMLib.flags().canBeSeenBy(source,observer))
				CMLib.commands().postSay(observer,null,L("Wha?  Where did this come from?  Cool!"),true,false);
		}
		else
		if((msg.source()==observer)
		&&(msg.target() instanceof MOB)
		&&(msg.targetMinor()==CMMsg.TYP_SPEAK)
		&&(msg.tool() instanceof Coins)
		&&(((Coins)msg.tool()).amDestroyed())
		&&(!msg.source().isMine(msg.tool()))
		&&(!((MOB)msg.target()).isMine(msg.tool())))
			CMLib.beanCounter().giveSomeoneMoney(msg.source(),(MOB)msg.target(),((Coins)msg.tool()).getTotalValue());
	}
}
