package jforex;

import com.dukascopy.api.indicators.*;
import com.dukascopy.api.*;
import java.text.*;
import java.awt.*;
import java.util.*;

// *
// * Indicator showing the minimum/maximum and close spread of a bar
// * V0.1 RoadRunner, 2011.07.31
// * known bug in DukasCopy release 2.13.26/api 2.6.38: input bars ASK and BID are shifted against each other on some timeframes (example: EUR/CHF, 1 min chart) - workaround included to autoshift the bars
// *
public class Spread implements IIndicator {

    private IIndicatorContext context;
    private IConsole console;
    private IndicatorInfo indicatorInfo;
    private InputParameterInfo[] inputParameterInfos;
    private OutputParameterInfo[] outputParameterInfos;
    private OptInputParameterInfo[] optInputParameterInfos;
    private IBar[][] inputs = new IBar[2][];
    private double[][] outputs = new double[3][];
    private SimpleDateFormat fDateTime = new SimpleDateFormat("EEE yyyy.MM.dd HH:mm:ss", Locale.ENGLISH);
    
    public void onStart(IIndicatorContext _context) {
        context = _context;
        console = _context.getConsole();
        fDateTime.setTimeZone(TimeZone.getTimeZone("GMT"));
        InputParameterInfo inputASK = new InputParameterInfo("PriceASK", InputParameterInfo.Type.BAR);
        inputASK.setOfferSide(OfferSide.ASK);
        InputParameterInfo inputBID = new InputParameterInfo("PriceBID", InputParameterInfo.Type.BAR);
        inputBID.setOfferSide(OfferSide.BID);
        
        indicatorInfo = new IndicatorInfo("Spread", "Spread indicator", "LAB", false, false, true, 2, 0, 3);
        inputParameterInfos = new InputParameterInfo[] {
                inputASK, inputBID
        };
        
        outputParameterInfos = new OutputParameterInfo[] {
            new OutputParameterInfo("SpreadMin", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE)
                {{
                     setColor(Color.GREEN);
                     //setColor2(Color.DARKGREEN);
                }},
            new OutputParameterInfo("SpreadMax", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE)
                {{
                     setColor(Color.RED);
                     //setColor2(Color.DARKRED);
                }},
            new OutputParameterInfo("SpreadCurr", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE)
                {{
                     setColor(Color.BLUE);
                     //setColor2(Color.DARKBLUE);
                }}
        };
    }

    public IndicatorResult calculate(int startIndex, int endIndex) {
        //calculating startIndex taking into account lookback value
        if (startIndex - getLookback() < 0) {
            startIndex -= startIndex - getLookback();
        }
        if (startIndex > endIndex) {
            return new IndicatorResult(0, 0, 0);
        }

        int i = 0;
        double dSpread = 0.0;
        double dSpreadLast = 0.0;
        IBar iBarAsk = null;
        IBar iBarBid = null;
        int iBid = 0;
        for(i=startIndex; i <= endIndex; i++){
            iBarAsk = (IBar)(inputs[0][i]);
            iBid = i;
            iBarBid = (IBar)(inputs[1][iBid]);
            
            //workaround DukasCopy bug as in 2.13.26 api 2.6.38: see, if passed ASK/BID bars do have the same time - if not shift...
            while (iBid > startIndex && iBarBid.getTime() > iBarAsk.getTime())
            {
                if (iBid == i)
                    console.getOut().println("ERROR - BID shifted forward: " + String.format("%.1f", dSpread) + "p ASK=" + iBarAsk.toString() + " BID=" + iBarBid.toString());
                    
                iBid--;
                iBarBid = (IBar)(inputs[1][iBid]);
            }
            while (iBid < endIndex && iBarBid.getTime() < iBarAsk.getTime())
            {
                if (iBid == i)
                    console.getOut().println("ERROR - BID shifted backwards: " + String.format("%.1f", dSpread) + "p ASK=" + iBarAsk.toString() + " BID=" + iBarBid.toString());
                    
                iBid++;
                iBarBid = (IBar)(inputs[1][iBid]);
            }
            
            dSpread = Math.min(Math.min(Math.min(iBarAsk.getOpen() - iBarBid.getOpen(), iBarAsk.getHigh() - iBarBid.getHigh()), iBarAsk.getLow() - iBarBid.getLow()), iBarAsk.getClose() - iBarBid.getClose()) / context.getInstrument().getPipValue();
            if (dSpread < 0)        //DK BUG as in 2.13.26 api 2.6.38!!! BID bar seems to be shifted by 1... (but not always ? )
            {
                console.getOut().println("ERROR - negative spread: " + String.format("%.1f", dSpread) + "p ASK=" + iBarAsk.toString() + " BID=" + iBarBid.toString());
            }
            
            outputs[0][i] = dSpread;
                
            dSpread = Math.max(Math.max(Math.max(iBarAsk.getOpen() - iBarBid.getOpen(), iBarAsk.getHigh() - iBarBid.getHigh()), iBarAsk.getLow() - iBarBid.getLow()), iBarAsk.getClose() - iBarBid.getClose()) / context.getInstrument().getPipValue();
            outputs[1][i] = dSpread;

            dSpread = (iBarAsk.getClose() - iBarBid.getClose())  / context.getInstrument().getPipValue();
            outputs[2][i] = dSpread;
        }
        return new IndicatorResult(startIndex, i);
    }


    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 OutputParameterInfo getOutputParameterInfo(int index) {
        if (index <= outputParameterInfos.length) {
            return outputParameterInfos[index];
        }
        return null;
    }

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

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

    public void setOptInputParameter(int index, Object value) {

    }

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

    public int getLookforward() {
        return 0;
    }
}