package jforex.requests;

import java.util.*;
import java.text.SimpleDateFormat;

import com.dukascopy.api.*;
import com.dukascopy.api.drawings.IOhlcChartObject;
import com.dukascopy.api.feed.*;
import com.dukascopy.api.IIndicators.AppliedPrice;
import com.dukascopy.api.indicators.IIndicator;

/**
 * The strategy calculates MACD and EMA values over the last 10 range bars
 * and prints the result to the strategy console.
 * 
 * Optionally the strategy plots the indicators on chart, if the
 * chart configuration matches the feed data. The strategy also adds
 * OHLC informer with indicator showing option
 *
 */
@RequiresFullAccess
public class MacdEmaOnFeed2 implements IStrategy {
    private IConsole console;
    private IHistory history;
    private IIndicators indicators;
    private SimpleDateFormat sdf;
    
    @Configurable("Instrument")
    public Instrument instrument = Instrument.EURUSD;
    @Configurable("side")
    public OfferSide side = OfferSide.BID;
    @Configurable("priceRangePips")
    public int priceRangePips = 5;
    @Configurable("Range Bar Count for indicator calculation")
    public int rangeBarCount = 10;
    @Configurable("Add to chart?")
    public boolean addToChart = true;    
    @Configurable("emaTimePeriod")
    public int emaTimePeriod = 12;
    @Configurable("macdSignalPeriod")
    public int macdSignalPeriod = 9;
    @Configurable("macdSlowPeriod")
    public int macdSlowPeriod = 26;
    @Configurable("macdFastPeriod")
    public int macdFastPeriod = 12;
    
    private PriceRange priceRange;
    
    private static int EMA_LINE = 0;
    
    private static int MACD = 0;
    private static int MACD_SIGNAL = 1;
    private static int MACD_HIST = 2;
    
   
    public void onStart(IContext context) throws JFException {
        this.console = context.getConsole();
        this.history = context.getHistory();
        this.indicators = context.getIndicators();
        sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT")); 

        priceRange = PriceRange.valueOf(priceRangePips);
        
        //just subscribe for feed to be read in the cache
        //context.subscribeToRangeBarFeed(Instrument.EURUSD, OfferSide.BID, PriceRange.valueOf(5), new IRangeBarFeedListener() {
            //public void onBar(Instrument instrument, OfferSide offerSide, PriceRange priceRange, IRangeBar bar) {}

        
        //add indicators to chart and setup OHLC informer to show indicator values
        if(this.addToChart){
            addToChart(context.getChart(instrument));
        }   
        
        /*final FeedDescriptor feedDescriptor = new FeedDescriptor();
        feedDescriptor.setInstrument(instrument);
        feedDescriptor.setDataType(DataType.PRICE_RANGE_AGGREGATION);
        feedDescriptor.setOfferSide(side);
        feedDescriptor.setPriceRange(priceRange);*/
        
        context.subscribeToRangeBarFeed(Instrument.EURUSD, OfferSide.BID, PriceRange.valueOf(5), new IRangeBarFeedListener() {
            public void onBar(Instrument instrument, OfferSide offerSide, PriceRange priceRange, IRangeBar bar) {
                try {
                    final FeedDescriptor feedDescriptor = new FeedDescriptor();
                    feedDescriptor.setInstrument(instrument);
                    feedDescriptor.setDataType(DataType.PRICE_RANGE_AGGREGATION);
                    feedDescriptor.setOfferSide(side);
                    feedDescriptor.setPriceRange(priceRange);        
                    
                    IRangeBar lastRangeBar = history.getRangeBar(instrument, side, priceRange, 0);
                    print("Last range bar start time: " + sdf.format(lastRangeBar.getTime()) + "; " + lastRangeBar.getOpen());
                    
                    //calculate and print EMA
                    Object[] emaFeed = indicators.calculateIndicator(feedDescriptor, new OfferSide[] { OfferSide.BID }, "EMA",
                            new AppliedPrice[] { AppliedPrice.CLOSE }, new Object[] { emaTimePeriod },  rangeBarCount, lastRangeBar.getTime(), 0);
                    double[] ema = (double[]) emaFeed[EMA_LINE]; //ema has just 1 output
                    print("ema for last %s range bars: %s",rangeBarCount, arrayToString(ema));
                    
                    //calculate and print MACD        
                    Object[] macdFeed = indicators.calculateIndicator(feedDescriptor, new OfferSide[] { OfferSide.BID }, "MACD",
                            new AppliedPrice[] { AppliedPrice.CLOSE }, new Object[] {  macdFastPeriod, macdSlowPeriod, macdSignalPeriod },  
                            rangeBarCount, lastRangeBar.getTime(), 0);
                    double[][] macd = {(double[]) macdFeed[MACD], (double[]) macdFeed[MACD_SIGNAL], (double[]) macdFeed[MACD_HIST] };        
            
                    IIndicator macdIndicator = indicators.getIndicator("MACD");
                    for(int i = 0; i < macdIndicator.getIndicatorInfo().getNumberOfOutputs(); i++){
                        String outputName = macdIndicator.getOutputParameterInfo(i).getName();
                        print("%s for last %s range bars: %s",outputName, rangeBarCount, arrayToString(macd[i]));
                    }
                } catch (JFException ex) {
                        ex.printStackTrace(console.getErr());
                }
            }        
        });    
    }
    
    private void addToChart(IChart chart){
        if(chart == null){
            print("chart not opened!");    
            return;
        } 
        if (chart.getSelectedOfferSide() != this.side) {
            print("chart offer side is not " + this.side);
            return;
        }  
        if (chart.getPriceRange() != this.priceRange) {
            print("chart price range is not " + this.priceRange);
            return;
        } 
        
        chart.addIndicator(indicators.getIndicator("EMA"), new Object[] { emaTimePeriod });
        chart.addIndicator(indicators.getIndicator("MACD"), new Object[] { macdFastPeriod, macdSlowPeriod, macdSignalPeriod });

        IOhlcChartObject ohlc = null;
        for (IChartObject obj : chart.getAll()) {
            if (obj instanceof IOhlcChartObject) {
                ohlc = (IOhlcChartObject) obj;
            }
        }
        if (ohlc == null) {
            ohlc = chart.getChartObjectFactory().createOhlcInformer();
            chart.addToMainChart(ohlc);
        }
        ohlc.setShowIndicatorInfo(true);
    }

    public void onAccount(IAccount account) throws JFException {
    }

    public void onMessage(IMessage message) throws JFException {
    }

    public void onStop() throws JFException {
    }

    public void onTick(Instrument instrument, ITick tick) throws JFException {
    }
    
    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
    }

    private void print(String format, Object...args){
        print(String.format(format,args));
    }
    
    private void print(Object o){
         console.getOut().println(o);
    }

    private static String arrayToString(double[] arr) {
        String str = "";
        for (int r = 0; r < arr.length; r++) {
            str += String.format("[%s] %.5f; ", r, arr[r]);
        }
        return str;
    }
    
}