package jforex.indicators;

import java.util.ArrayList;
import java.util.List;

import com.dukascopy.api.IBar;
import com.dukascopy.api.IConsole;
import com.dukascopy.api.IHistory;
import com.dukascopy.api.ITick;
import com.dukascopy.api.JFException;
import com.dukascopy.api.indicators.IIndicator;
import com.dukascopy.api.indicators.IIndicatorContext;
import com.dukascopy.api.indicators.IndicatorInfo;
import com.dukascopy.api.indicators.IndicatorResult;
import com.dukascopy.api.indicators.InputParameterInfo;
import com.dukascopy.api.indicators.IntegerRangeDescription;
import com.dukascopy.api.indicators.OptInputParameterInfo;
import com.dukascopy.api.indicators.OutputParameterInfo;

/**
 * The indicator calculates the number of ticks per bar.
 * 
 * The barCount sets the number of last bars considered for calculation - 
 * for earlier bars the value will be 0. This is due to performance,
 * because the calculation gets more time consuming with bigger periods
 * (since the bigger the period the more ticks it contains). 
 * 
 * At the current implementation, it is not advised to use the indicator 
 * with periods larger than ONE_DAY, because there are not applied techniques
 * for optimally working with large quantities of historical ticks 
 * (e.g. reading ticks in chunks).
 *
 */
public class TicksPerBar implements IIndicator{
    private IndicatorInfo indicatorInfo;
    private InputParameterInfo[] inputParameterInfos;
    private OptInputParameterInfo[] optInputParameterInfos;
    private OutputParameterInfo[] outputParameterInfos;
    private IBar[][] inputBars = new IBar[1][];
    private double[][] outputs = new double[1][];
    
    private IConsole console;
    private IHistory history;
    private IIndicatorContext context;
    private int barCount = 20; 

    public void onStart(IIndicatorContext context) {
        
        console = context.getConsole();
        this.context = context;
        this.history = context.getHistory();
        
        indicatorInfo = new IndicatorInfo("TicksPerBar", "Ticks per bar", "Custom indicators", false, false, false, 1, 1, 1);
        indicatorInfo.setRecalculateOnNewCandleOnly(true);
        
        inputParameterInfos = new InputParameterInfo[] {
            new InputParameterInfo("Input data", InputParameterInfo.Type.BAR)
        };
        optInputParameterInfos = new OptInputParameterInfo[] {new OptInputParameterInfo("Bar count", OptInputParameterInfo.Type.OTHER,
                new IntegerRangeDescription(20, 1, 1000, 1))};
        
        outputParameterInfos = new OutputParameterInfo[] {new OutputParameterInfo("out", OutputParameterInfo.Type.DOUBLE,
                OutputParameterInfo.DrawingStyle.HISTOGRAM)};
        
    }   


    public IndicatorResult calculate(int startIndex, int endIndex) {

        if (startIndex > endIndex) {
            return new IndicatorResult(0, 0);
        }

        int i, j;
        long lastTickTime, from, to;
        List<ITick> ticks = new ArrayList<ITick>();
        for (i = startIndex, j = 0; i <= endIndex; i++, j++) {
            //calculate only for latest barCount bars
            if(i < endIndex - barCount){
                continue;
            }
            try {
                lastTickTime = history.getLastTick(context.getFeedDescriptor().getInstrument()).getTime();
                from = inputBars[0][i].getTime();
                to = Math.min(inputBars[0][i].getTime() + context.getFeedDescriptor().getPeriod().getInterval(), lastTickTime);
                //calculation for the current bar and the last tick was in the previous bar
                if(from > to){
                    outputs[0][j] = 0;
                } else {
                    ticks = history.getTicks(context.getFeedDescriptor().getInstrument(), from, to);
                    outputs[0][j] = ticks.size();
                }
            } catch (JFException e) {
                console.getErr().println(e);
            }

        }
        return new IndicatorResult(startIndex, j);
    }

    public IndicatorInfo getIndicatorInfo() {
        return indicatorInfo;
    }

    public InputParameterInfo getInputParameterInfo(int index) {
        if (index <= inputParameterInfos.length) {
            return inputParameterInfos[index];
        }
        return null;
    }

    public int getLookback() {
        return 0;
    }

    public int getLookforward() {
        return 0;
    }

    public OptInputParameterInfo getOptInputParameterInfo(int index) {
        if (index <= optInputParameterInfos.length) {
            return optInputParameterInfos[index];
        }
        return null;
    }

    public OutputParameterInfo getOutputParameterInfo(int index) {
        if (index <= outputParameterInfos.length) {
            return outputParameterInfos[index];
        }
        return null;
    }

    public void setInputParameter(int index, Object array) {
        inputBars[index] = (IBar[]) array;
    }

    public void setOptInputParameter(int index, Object value) {
        if(index == 0){
            barCount = (Integer) value;
        }
    }

    public void setOutputParameter(int index, Object array) {
        outputs[index] = (double[]) array;
    }
    
}
