package jforex.requests;

import java.awt.Color;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.TimeZone;

import com.dukascopy.api.IBar;
import com.dukascopy.api.IConsole;
import com.dukascopy.api.IHistory;
import com.dukascopy.api.IIndicators;
import com.dukascopy.api.Instrument;
import com.dukascopy.api.OfferSide;
import com.dukascopy.api.indicators.BooleanOptInputDescription;
import com.dukascopy.api.indicators.DoubleRangeDescription;
import com.dukascopy.api.indicators.IIndicator;
import com.dukascopy.api.indicators.IIndicatorContext;
import com.dukascopy.api.indicators.IIndicatorsProvider;
import com.dukascopy.api.indicators.IMinMax;
import com.dukascopy.api.indicators.IndicatorInfo;
import com.dukascopy.api.indicators.IndicatorResult;
import com.dukascopy.api.indicators.InputParameterInfo;
import com.dukascopy.api.indicators.IntegerListDescription;
import com.dukascopy.api.indicators.IntegerRangeDescription;
import com.dukascopy.api.indicators.OptInputParameterInfo;
import com.dukascopy.api.indicators.OutputParameterInfo;

//@RequiresFullAccess
public class SAMPLEIndicator_2 implements IIndicator ,IMinMax /*, IDrawingIndicator*/
{
    private IConsole console;
    private IndicatorInfo indicatorInfo;
    private IHistory history;
    private IIndicatorContext context;
    private OfferSide  offerSide;
    private IIndicator ma;

    private InputParameterInfo[] inputParameterInfos;
    private OptInputParameterInfo[] optInputParameterInfos;
    private OutputParameterInfo[] outputParameterInfos;

    private static final String INDI_NAME = "SAMPLE";
    private static final int decimalPlaces = 5;
    private IBar[][] inputs    = new IBar[14][];   
    private double[][] outputs = new double[2][];  
    private int maType;    
    
    // optional input parameters
    private boolean useFixedMinMax = false;
    private double min = 0;
    private double max = 100;
    private double[] minMax = {min, max};
    int timePeriod = 2;
    
    private double minValue = Double.MAX_VALUE;
    private double maxValue = Double.MIN_VALUE;

    enum Instr {
        AUDUSD,
        EURUSD,
        GBPUSD,
        NZDUSD,
        USDCHF,
        USDCAD,
        USDJPY;

        Instr() {}

        Instrument getInstr() {
            return Instrument.valueOf(this.toString());
        }

    }
    
    private IIndicatorsProvider indicatorsProvider;

    public void onStart(IIndicatorContext context) {

        this.context = context;
        this.console = context.getConsole();
        indicatorsProvider = context.getIndicatorsProvider();
        
        ma = indicatorsProvider.getIndicator("MA");

        Instr[] myInstruments = Instr.values();
        
        //  inputs
        inputParameterInfos = new InputParameterInfo[myInstruments.length*2];
        // fill inputs
        for(int k = 0; k < myInstruments.length; k++) {
            inputParameterInfos[k] =  new InputParameterInfo("Price", InputParameterInfo.Type.BAR);
            inputParameterInfos[k].setInstrument(myInstruments[k].getInstr());
            inputParameterInfos[k].setOfferSide(OfferSide.BID);
        }
        
        for(int k = myInstruments.length, l=0; k < inputParameterInfos.length; k++, l++) {
            inputParameterInfos[k] =  new InputParameterInfo("Price", InputParameterInfo.Type.BAR);
            inputParameterInfos[k].setInstrument(myInstruments[l].getInstr());
            inputParameterInfos[k].setOfferSide(OfferSide.ASK);
        }
        
        
        int[] maValues = new int[IIndicators.MaType.values().length];
        String[] maNames = new String[IIndicators.MaType.values().length];
        for (int i = 0; i < maValues.length; i++) {
             maValues[i] = i;
             maNames[i] = IIndicators.MaType.values()[i].name();
        }

        // optional parameters
        optInputParameterInfos = new OptInputParameterInfo[] {
                
                new OptInputParameterInfo("Use fixed levels min/max", OptInputParameterInfo.Type.OTHER, new BooleanOptInputDescription(useFixedMinMax)),
                new OptInputParameterInfo("Limit Max",          OptInputParameterInfo.Type.OTHER,   new DoubleRangeDescription(minMax[1], 0, 100, 0.002, 3)),
                new OptInputParameterInfo("Limit Min",          OptInputParameterInfo.Type.OTHER,   new DoubleRangeDescription(minMax[0], 0, 100, 0.002, 3)),
                new OptInputParameterInfo("MA type",            OptInputParameterInfo.Type.OTHER,   new IntegerListDescription(IIndicators.MaType.SMA.ordinal(), maValues, maNames)),
                new OptInputParameterInfo("MA Time Period",     OptInputParameterInfo.Type.OTHER,   new IntegerRangeDescription(timePeriod, 2, 100, 1))
            };


        outputParameterInfos = new OutputParameterInfo[] {
            new OutputParameterInfo("OUT1", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE),
            new OutputParameterInfo("MA line", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE)
            };
        
        
        // settings color for all outputs
        outputParameterInfos[0].setColor(Color.green);          //Color OUT1
        outputParameterInfos[1].setColor(Color.blue);           //Color SMA line
        
        indicatorInfo = new IndicatorInfo(INDI_NAME, "", "My indicators",
            false, false, false, inputParameterInfos.length, optInputParameterInfos.length, outputParameterInfos.length);
    }

    public IndicatorResult calculate(int startIndex, int endIndex)
    {
        
        if (startIndex > endIndex) {
            return new IndicatorResult(0, 0);
        }
        
        //calculating startIndex taking into account lookback value
        if (startIndex - getLookback() < 0) {
            startIndex = getLookback();
        }
        
        int i,j;
        minValue = Double.MAX_VALUE;
        maxValue = Double.MIN_VALUE;
        double[] maInput = new double[inputs[0].length];
                
        //changed name from myOutput, to maInput, also reduced code repetition
        for (j=0; j < inputs[0].length; j++) {
            maInput[j] = 0;
            for(int inputIdx=0; inputIdx< Instr.values().length; inputIdx++){
                 maInput[j]+= (inputs[inputIdx][j].getClose() + inputs[inputIdx+7][j].getClose());
            }            
        }    
            
        ma.setInputParameter(0, maInput);
        ma.setOptInputParameter(0, timePeriod);
        ma.setOutputParameter(0, outputs[1]);
        IndicatorResult maResult = ma.calculate(startIndex,endIndex);
        
        //example logging - remove after testing
        console.getInfo().println(String.format(
                "output 1 length = %s, contents = %s,\n" +
                "maInput length = %s, maInput = %s,\n" +
                "startIndex=%s, endIndex=%s, getLookback()=%s", 
                outputs[1].length, Arrays.toString(outputs[1]),
                maInput.length, Arrays.toString(maInput),
                startIndex, endIndex, getLookback()));

         
        return new IndicatorResult(startIndex, outputs[0].length);

    } // end of calculate()
    

    //--------------------------------------------------------------------------
    public IndicatorInfo getIndicatorInfo() {
        return indicatorInfo;
    }

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

    public int getLookback() {
        // ma.setOptInputParameter(0, timePeriod);
        return timePeriod;
    }

    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) {
        inputs[index] = (IBar[]) array;
    }

    public void setOptInputParameter(int index, Object value) {
       switch (index){
            case 0: // using fixed levels min max
                useFixedMinMax = (Boolean)value;
                break;            
            case 1:  //maxLimit
                max = (Double)value;
                minMax[1] = max;
                break;
            case 2:  //minLimit
                min = (Double)value;
                minMax[0] = min;
                break;
            case 3:
                maType = (Integer) value;
                ma = indicatorsProvider.getIndicator(IIndicators.MaType.values()[maType].name());                                        
                break;     
            case 4:  //sma period
                timePeriod = (Integer)value;
                break;        
         }
    }

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



    //-----------------------------------------------------------------------------
    public void printErr(Object o)
    {
        console.getErr().println(o.toString());
    }

    public String toStr(long time) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") {
            {
                setTimeZone(TimeZone.getTimeZone("GMT"));
            }
        };
        return sdf.format(time);
    }
 
 @Override
    //-----------------------------------------------------------------------------
    public double[] getMinMax(int outputIdx, Object values, int firstVisibleValueIndex, int lastVisibleValueIndex) {
        
        minValue = Double.MAX_VALUE;
        maxValue = Double.MIN_VALUE;

        if (useFixedMinMax) {
            minMax[0] = min;
            minMax[1] = max;
            return minMax;
        } else {
            double[] v = (double[])values;
            
            for (int i=0; i< v.length; i++) {
                if(v[i] > 0.00001){
                    minValue = Math.min(minValue, v[i]);
                }
                if(v[i] > 0.00001){
                    maxValue = Math.max(maxValue, v[i]);
                }
            }
            minMax[0] = minValue;
            minMax[1] = maxValue;
            return minMax;
        }
    }
    

}




 

