package jforex.strategies.rangebars;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;

import com.dukascopy.api.*;
import com.dukascopy.api.IIndicators.MaType;
import com.dukascopy.api.bar.IRangeBar;
import com.dukascopy.api.indicators.IIndicator;
import com.dukascopy.api.listener.IRangeBarFeedListener;

/**
 * The strategy shows how to emulate range bar history.
 * Also it shows how to use range bar close prices with MA indicator.
 *
 */

@RequiresFullAccess
public class RangeBarsHistory3 implements IStrategy {

	private IConsole console;
	private IHistory history;
	private IIndicators indicators;
	private SimpleDateFormat sdf;

	@Configurable("Instrument")
	public Instrument instrument = Instrument.EURUSD;
	@Configurable("Offer Side")
	public OfferSide offerSide = OfferSide.BID;
	@Configurable("Price range (pips)")
	public int priceRangePips = 1;
	@Configurable("MA TimePeriod")
	public int maTimePeriod = 3;
	@Configurable("MA Shift")
	public int maShift = 0;
	@Configurable("Log values")
	public boolean logValues = true;
	@Configurable("(config) History Chunk Size")
	public Period historyChunkSize = Period.ONE_MIN;

	public static DecimalFormat df = new DecimalFormat("0.00000");
	private PriceRange priceRange;
	
	private BlockingQueue<IRangeBar> rangeBars;
	
	@Override
	public void onStart(IContext context) throws JFException {

		this.history = context.getHistory();
		this.console = context.getConsole();
		this.indicators = context.getIndicators();
		
		log("start");
		
		
		priceRange = PriceRange.valueOf(priceRangePips);

		sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
		

		
		rangeBars = new ArrayBlockingQueue<IRangeBar>(maShift + maTimePeriod);
		
		
		context.subscribeToRangeBarFeed(instrument, offerSide, priceRange, 
				new IRangeBarFeedListener() {
					public void onBar(Instrument instrument, OfferSide offerSide, PriceRange priceRange, IRangeBar bar) {
						log("Received Range Bar: " + instrument + ", "  + offerSide + ", " + priceRange + ", " + bar);

						if(rangeBars.remainingCapacity()==0){
							//our queue is full
							//lets take out the oldest range bar
							print("Our Queue is full, Removing item: " + rangeBars.poll());
						}
						
						
						//store the new range bar
						rangeBars.offer(bar);
						
						if(rangeBars.remainingCapacity()>0){
							print("we do not have enough items for calculating indicator values");
							return;
						}
				
						//get close prices
						double[] closePrices = new double[rangeBars.size()];
						
						Iterator<IRangeBar> barsIterator = rangeBars.iterator();
						int i = 0;
						while (barsIterator.hasNext()) {
							IRangeBar rangeBar = barsIterator.next();
							//log(rangeBar);
							closePrices[i] = rangeBar.getClose();
							i++;
						}
						
						double ma = getMa(maTimePeriod, maShift, closePrices);
						print("MA value with period " + maTimePeriod+ " and shift " + maShift +": "+ df.format(ma));
						
					}
				});
		
	}
	
	/**
	 * Calculates MA over an array, see more:
	 * http://www.dukascopy.com/wiki/index.php?title=Strategy_API:_Indicators#Calculate_indicator_on_array
	 * 
	 * @param timePeriod
	 * @param shift
	 * @param priceArr
	 * @return
	 */
	private double getMa(int timePeriod, int shift, double [] priceArr ) {
		IIndicator maIndicator = indicators.getIndicator("MA");
		
		double result = 0;
		
		 
		//set optional inputs
		maIndicator.setOptInputParameter(0, maTimePeriod);
		maIndicator.setOptInputParameter(1, MaType.SMA.ordinal());
		 
		//set inputs
		maIndicator.setInputParameter(0, priceArr);
		 
		//set outputs
		double [] resultArr = new double [priceArr.length];
		maIndicator.setOutputParameter(0, resultArr);
		 
		//calculate
		maIndicator.calculate(0, priceArr.length - 1);
		print("ma result array: " + arrayToString(resultArr));
		
		int index = resultArr.length - shift - 1 - maIndicator.getLookback();
		print("Retreiving MA from resultArr[" + index + "]");
		result  = resultArr[index];
		
		return result;
	}

	
	private void print(Object o){
		console.getOut().println(o);
	}
	
	private void log(Object o){
		if(logValues){
			print(o);
		}
	}
	
	public static String arrayToString(double[] arr) {
		String str = "";
		for (int r = 0; r < arr.length; r++) {
			str += "[" + r + "] " + df.format(arr[r]) + "; ";
		}
		return str;
	}

	@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 {}

}
    