package jforex.account;

import java.text.DecimalFormat;
import java.util.SortedMap;

import com.dukascopy.api.Configurable;
import com.dukascopy.api.IAccount;
import com.dukascopy.api.IBar;
import com.dukascopy.api.IConsole;
import com.dukascopy.api.IContext;
import com.dukascopy.api.IMessage;
import com.dukascopy.api.IStrategy;
import com.dukascopy.api.ITick;
import com.dukascopy.api.Instrument;
import com.dukascopy.api.JFException;
import com.dukascopy.api.Period;
import com.dukascopy.api.RequiresFullAccess;
import com.dukascopy.api.system.ITesterClient;
import com.dukascopy.api.system.TesterFactory;

/**
 * The strategy prints out commissions of current Deposit, Equity and Turnorver limits for currencies
 * on deposits with Dukascopy Bank. For more details see:
 * http://www.dukascopy.com/swiss/english/forex/forex_trading_accounts/commission-policy/
 * Also the strategy prints out overnights, for more details see:
 * http://www.dukascopy.com/swiss/english/forex/forex_trading_accounts/overnight/
 * 
 * Also the strategy calculates the commission for the given order amount
 */
@RequiresFullAccess
public class CommissionsTPCover implements IStrategy {

	@Configurable("Monthly turnover")
	public int monthlyTurnover = 10000000;
	// 22:00 GMT
	@Configurable("Equity at settlment")
	public int equityAtSettlment = 250000;
	@Configurable("Net deposit")
	public int netDeposit = 250000;
	@Configurable("Print commission info")
	public boolean printCommInfo = true;
	
	private ITesterClient client;
	private IConsole console;
	
	private final DecimalFormat df = new DecimalFormat("############");
	private final DecimalFormat dfComm = new DecimalFormat("0.000000");
	
	@Override
	public void onStart(IContext context) throws JFException {
		
		console = context.getConsole();
		
		try {
			client = TesterFactory.getDefaultInstance();
		} catch (Exception e) {
			console.getErr().println(e);
			context.stop();
			return;
		} 
		
		if(printCommInfo){
			printCommissions(client.getCommissions().getDepositLimits(), "Deposit limits:");
			printCommissions(client.getCommissions().getEquityLimits(), "Equity limits:");
			printCommissions(client.getCommissions().getTurnoverLimits(), "Turnover limits:");
		}
		
		print("Commission for 1M: " + dfComm.format(getCommission(1)));
		print("Commission for 5M: " + dfComm.format(getCommission(5)));
	}
	
	private double getCommission(double amount){
		//commissions in units per 1 million order (e.g. in USD for 1M order)
		double depositComm = getCommission(client.getCommissions().getDepositLimits(), netDeposit);
		double equityComm = getCommission(client.getCommissions().getEquityLimits(), equityAtSettlment);
		double turnoverComm = getCommission(client.getCommissions().getTurnoverLimits(), monthlyTurnover);
		//min commission gets applied
		double commInUnits = Math.min(Math.min(depositComm, equityComm),turnoverComm);
		print("depositComm="+depositComm+" equityComm="+equityComm+" turnoverComm="+turnoverComm);
		//Order amounts are expressed in millions
		return (commInUnits / 1000000) * amount;
	}
	
	/**
	 * Get commission by given limit -> commision map. Note the special case for the first
	 * and the last limit
	 * 
	 * @param commMap
	 * @param limit
	 * @return
	 */
	private double getCommission(SortedMap<Double, Double> commMap, double limit){
		
		Double[] limitArray = commMap.keySet().toArray(new Double[0]);
		Double[] commissionArray = commMap.values().toArray(new Double[0]);
		
		//first limit 
		if(limitArray[0] > limit){
			return commissionArray[0];
		}
		
		//second to second to last element
		for(int i=1; i < limitArray.length - 1; i++){
			if((limit >= limitArray[i] && limit < limitArray[i + 1])){
				return commissionArray[i];
			}
		}
		
		//last limit
		return commissionArray[commissionArray.length - 1];
		
	}	
	
	private void printCommissions(SortedMap<Double, Double> limitMap, String comment){
		print(comment.toUpperCase());
		Double firstKey = (Double)limitMap.keySet().toArray()[0];
		print("<" + firstKey + " = " + client.getCommissions().getMaxCommission() );
		for(Double key : limitMap.keySet()){
			print(">=" +df.format(key) + " = " + limitMap.get(key) );
		}
	}	
	
	private void print(Object o){
		console.getOut().println(o);
	}

	@Override
	public void onTick(Instrument instrument, ITick tick) throws JFException {}
	@Override
	public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {}
	@Override
	public void onMessage(IMessage message) throws JFException {}
	@Override
	public void onAccount(IAccount account) throws JFException {}
	@Override
	public void onStop() throws JFException {}

	
}
