package jforex.strategies.indicators;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

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

public class AddToChartIchiMacdAdx2 implements IStrategy {
    private IIndicators indicators;
    private IChart chart;
    private IHistory history;
    private IConsole console;
    private IOhlcChartObject ohlc = null;
    
    @Configurable("")
    public Filter filter = Filter.NO_FILTER;
    @Configurable("")
    public Instrument instrument = Instrument.EURUSD;
    @Configurable("")
    public Period period = Period.TEN_MINS;
    @Configurable("")
    public OfferSide side = OfferSide.BID;
    @Configurable("")
    public int candleCount = 10;

    public void onStart(IContext context) throws JFException {
        this.indicators = context.getIndicators();
        this.chart = context.getChart(Instrument.EURUSD);
        this.history = context.getHistory();
        this.console = context.getConsole();
        
        chart = context.getChart(instrument);
        IBar prevBar = history.getBar(instrument, period, side, 1);

        @SuppressWarnings("serial")
        Map<IIndicator, Object[]> indOptInputMap = new HashMap<IIndicator, Object[]>(){{
            put(indicators.getIndicator("ICHIMOKU"), new Object[]{9, 26, 52});
            put(indicators.getIndicator("MACD"), new Object[]{12, 26, 9});
            put(indicators.getIndicator("ADX"), new Object[]{14});
        }};
        
        for(Map.Entry<IIndicator, Object[]> entry : indOptInputMap.entrySet()){
            IIndicator indicator = entry.getKey();
            Object[] optInputs = entry.getValue();
            
            //0. add to chart
            addToChart(chart, indicator, optInputs);
            
            //1. input related params - for all indicators use close prices and BID side, where applicable
            AppliedPrice [] appliedPrices = new AppliedPrice[indicator.getIndicatorInfo().getNumberOfInputs()];
            Arrays.fill(appliedPrices, AppliedPrice.CLOSE);
            OfferSide [] offerSides = new OfferSide[indicator.getIndicatorInfo().getNumberOfInputs()];
            Arrays.fill(offerSides, OfferSide.BID);
            
            //2. calculation
            Object[] indResult = indicators.calculateIndicator(instrument, period, offerSides, indicator.getIndicatorInfo().getName(), 
                    appliedPrices, optInputs, filter, candleCount, prevBar.getTime(), 0);
            
            //3. process outputs
            print("indicator outputs: ");
            for(int i = 0; i < indicator.getIndicatorInfo().getNumberOfOutputs(); i++){
                OutputParameterInfo outputInfo = indicator.getOutputParameterInfo(i);
                String outputName = outputInfo.getName();
                String shiftInfo = outputInfo.getShift() == 0 ? "" : " (output shifted by " + outputInfo.getShift()+")";
                //note that the last element is the latest value, the 0th - the oldest one
                if(outputInfo.getType() == OutputParameterInfo.Type.DOUBLE){                    
                    print("%s%s=%s", outputName, shiftInfo, arrayToString((double[])indResult[i]));  
                } else if(outputInfo.getType() == OutputParameterInfo.Type.INT){
                    print("%s%s=%s", outputName, shiftInfo, Arrays.asList((int[])indResult[i]));  
                } else {
                    print("%s type is Object - %s, which means that it is custom drawing output or it needs customized processing.", outputName, indResult[i].getClass());
                }
            }
        }

    }
    
    private void addToChart(IChart chart, IIndicator indicator, Object[] optInputs){
        if(chart == null){
            print("chart not opened!");    
            return;
        } 
        if (chart.getSelectedOfferSide() != this.side) {
            print("chart offer side is not " + this.side);
            return;
        }  
        if (chart.getSelectedPeriod() != this.period) {
            print("chart period is not " + this.period);
            return;
        }         

        chart.addIndicator(indicator, optInputs);
        
        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);
    }
    
    private void print(String format, Object...args){
        print(String.format(format, args));
    }

    private void print(Object message) {
        console.getOut().println(message);
    }
    
    public 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;
    }

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