package jforex;

import com.dukascopy.api.*;
import com.dukascopy.api.feed.IFeedDescriptor;
import com.dukascopy.api.indicators.*;

import java.awt.*;
import java.awt.Component;
import java.awt.event.*;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ConcurrentModificationException;
import java.util.Currency;
import java.util.TimeZone;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
import java.io.File;
import java.math.BigDecimal;

//@RequiresFullAccess
public class SAMPLEIndicator 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.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[] myOutput = new double[endIndex-startIndex+1];
        
        for (i = startIndex, j=0; i <= endIndex; i++, j++) {

          
            double var1 = (inputs[Instr.USDCHF.ordinal()][j].getClose() + inputs[Instr.USDCHF.ordinal()+7][j].getClose());
            double var2 = (inputs[Instr.USDCAD.ordinal()][j].getClose() + inputs[Instr.USDCAD.ordinal()+7][j].getClose());
            double var3 = (inputs[Instr.USDJPY.ordinal()][j].getClose() + inputs[Instr.USDJPY.ordinal()+7][j].getClose());
            double var4 = (inputs[Instr.EURUSD.ordinal()][j].getClose() + inputs[Instr.EURUSD.ordinal()+7][j].getClose());
            double var5 = (inputs[Instr.GBPUSD.ordinal()][j].getClose() + inputs[Instr.GBPUSD.ordinal()+7][j].getClose());
            double var6 = (inputs[Instr.AUDUSD.ordinal()][j].getClose() + inputs[Instr.AUDUSD.ordinal()+7][j].getClose());
            double var7 = (inputs[Instr.NZDUSD.ordinal()][j].getClose() + inputs[Instr.NZDUSD.ordinal()+7][j].getClose());
            
            myOutput[j] = var1 + var2 + var3 + var4 + var5 + var6 + var7;          
            
        }  
        
        if ( myOutput.length < ma.getLookback()) {
                //not enough data to calculate ma
                return new IndicatorResult(0, 0);
        }
            
        ma.setInputParameter(0, myOutput);
        ma.setOptInputParameter(0, timePeriod);
        ma.setOutputParameter(0, outputs[1]);
        IndicatorResult maResult = ma.calculate(startIndex+timePeriod, myOutput.length-1);
            
        if (maResult.getNumberOfElements() == 0) {
            //sma returned 0 values
            return new IndicatorResult(0, 0);
        }
        
        System.arraycopy(myOutput, timePeriod-1, outputs[0], 0, maResult.getNumberOfElements());
         
        return new IndicatorResult(maResult.getFirstValueIndex(), maResult.getNumberOfElements());

    } // 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 print(Object o)
    {
        console.getOut().println(o.toString());
    }

    //-----------------------------------------------------------------------------
    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++) {
               minValue = Math.min(minValue, v[i]);
               maxValue = Math.max(maxValue, v[i]);
            }
            minMax[0] = minValue;
            minMax[1] = maxValue;
            return minMax;
        }
    }
    

}




 

