package jforex.strategies;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import com.dukascopy.api.*;
import com.dukascopy.api.IEngine.OrderCommand;
import com.dukascopy.api.bar.IRangeBar;
import com.dukascopy.api.listener.IRangeBarFeedListener;

/**
 * The strategy makes a BUY order on completion of a GREEN candle
 * and SELL - on RED one.
 * 
 * The user can select if the strategy works with:
 * a) candle stick bars (time period),
 * b) range bars (price range).
 *
 */

@RequiresFullAccess
public class TradeOnDirectionRangeBars implements IStrategy {

	@Configurable("Use range bars")
	public boolean useRangeBars = true;
	@Configurable("Instrument")
	public Instrument instrument = Instrument.EURUSD;
	@Configurable("Period (used only with candles)")
	public Period period = Period.TEN_SECS;	
	@Configurable("Price range (used only with range bars)")
	public int priceRangePips = 1;	
	@Configurable("Amount")
	public double amount = 0.001;
	@Configurable("Stop loss")
	public int stopLossPips = 3;
	@Configurable("Take profit")
	public int takeProfitPips = 5;

	
	private IEngine engine;
	private IConsole console;
	private IHistory history;
	
	private int orderCount; 
	private IRangeBar prevRangeBar;
	
	private Queue<OrderCommand> orderCmds = new ConcurrentLinkedQueue<OrderCommand>();
	
	@Override
	public void onStart(IContext context) throws JFException {
		this.engine = context.getEngine();
		this.console = context.getConsole();
		this.history = context.getHistory();		

		console.getOut().println("start");
		
		context.subscribeToRangeBarFeed(Instrument.EURUSD, OfferSide.BID, PriceRange.valueOf(priceRangePips), 
				new IRangeBarFeedListener() {
					public void onBar(Instrument instrument, OfferSide offerSide, PriceRange priceRange, IRangeBar bar) {
						
						if(!useRangeBars){
							return;
						}

						if(prevRangeBar == null){
							prevRangeBar = bar;
							return;
						}

						//add order command to queue - we can submit order only from strategy thread 
						try {
							OrderCommand cmd = getCommand(bar, prevRangeBar);
							orderCmds.offer(cmd);
						} catch (JFException e) {
							e.printStackTrace(console.getErr());
						}						
						
						prevRangeBar = bar;
					}
				});
	}

	@Override
	public void onTick(Instrument instrument, ITick tick) throws JFException {
		if(!useRangeBars){
			return;
		}
		
		//execute all order commands made in range bar feed
		while(!orderCmds.isEmpty()){
			submitOrder(orderCmds.poll());
		}
	}

	@Override
	public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
		
		if(useRangeBars){
			return;
		}
		
		if( this.instrument != instrument || this.period != period){
			return;		
		}
		//take previous bar from historical data
		IBar prevCandleStickBar = history.getBar(instrument, period, OfferSide.BID, 2);
		OrderCommand cmd = getCommand(bidBar, prevCandleStickBar);
		submitOrder(cmd);
	}
	
	private OrderCommand getCommand(IBar curBar, IBar prevBar) throws JFException{
		
		return curBar.getClose() > prevBar.getClose() 
			? OrderCommand.BUY
			: OrderCommand.SELL;
	}
	
	private IOrder submitOrder(OrderCommand orderCmd) throws JFException {

		double stopLossPrice, takeProfitPrice;

		// Calculating stop loss and take profit prices
		if (orderCmd == OrderCommand.BUY) {
			stopLossPrice = history.getLastTick(this.instrument).getBid() - getPipPrice(this.stopLossPips);
			takeProfitPrice = history.getLastTick(this.instrument).getBid() + getPipPrice(this.takeProfitPips);
		} else {
			stopLossPrice = history.getLastTick(this.instrument).getAsk() + getPipPrice(this.stopLossPips);
			takeProfitPrice = history.getLastTick(this.instrument).getAsk() - getPipPrice(this.takeProfitPips);
		}

		// Submitting an order for the specified instrument at the current market price
		return engine.submitOrder("order" + ++orderCount, this.instrument, orderCmd, this.amount, 0, 5, stopLossPrice, takeProfitPrice);
	}
	
	private double getPipPrice(int pips) {
		return pips * this.instrument.getPipValue();
	}

	@Override
	public void onMessage(IMessage message) throws JFException {
		//if the message is related to order print its content
		if (message.getOrder() != null){
			console.getOut().println(message.getOrder().getLabel() + " " + message.getType() + " " + message.getContent());
		}
	}

	@Override
	public void onAccount(IAccount account) throws JFException {

	}

	@Override
	public void onStop() throws JFException {
		//close all orders on strategy stop
		for(IOrder o : engine.getOrders()){
			o.close();
		}
	}

}
