package jforex;

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

import java.awt.*;
import java.util.Map;
import java.awt.geom.GeneralPath;


public class TVI implements IIndicator , IDrawingIndicator  {
    private IndicatorInfo indicatorInfo;
    private IIndicatorContext theContext;
    private IHistory history;
    private InputParameterInfo[] inputParameterInfos;
    private OptInputParameterInfo[] optInputParameterInfos;
    private OutputParameterInfo[] outputParameterInfos;
    
    private int timePeriodR = 8;
    private int timePeriodS = 8;
    private int smoothPeriod = 5;
    private Boolean useMQ4version = true;
    
    private IBar[][] inputs = new IBar[1][];
    private double[][] outputs = new double[2][];
    private Object[][] output = new Object[1][];
    
    private final GeneralPath generalPath = new GeneralPath();
        
    private IIndicator ema1;
    private IIndicator ema2;
    private IIndicator ema3;
    private IIndicator ema4;
    private IIndicator sma;        
    
    private int TickNess = 3 ;
    private Color upTrendColor = GREEN ;
    private Color downTrendColor = RED ;
                
    public static final Color GREEN = new Color(0x00, 0x80, 0x00);
    public static final Color RED = new Color(0xc0, 0x00, 0x00);
    public static final Color LIGHT_GREEN = new Color(0x80, 0xFF, 0x80);
    public static final Color LIGHT_RED  = new Color(0xFF, 0x80, 0x80);
    public static final Color VERY_DARK_YELLOW = new Color(0x80, 0x80, 0x00);
    public static final Color DARK_RED = new Color(0xc0, 0x00, 0x00);
    public static final Color VERY_DARK_RED = new Color(0x80, 0x00, 0x00);
    public static final Color VERY_DARK_GREEN = new Color(0x00, 0x80, 0x00);
    public static final Color DARK_GREEN = new Color(0x00, 0xC0, 0x00);
                    
    private OutputParameterInfo  outputParameterInfo1 ;
    private OutputParameterInfo  outputParameterInfo2 ;
    private OutputParameterInfo  outputParameterInfo3 ;
    
    public void onStart(IIndicatorContext context) {
        theContext = context ;
        history = theContext.getHistory() ;
        indicatorInfo = new IndicatorInfo("TVI", "tick volume indicator", "Custom",
        		false, false, true, 1, 7, 3);
        
        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();
        }

        int[] availColorsValues = new int[AvailableColors.values().length];
        String[] availColorsNames = new String[AvailableColors.values().length];
        for (int i = 0; i < availColorsValues.length; i++) {
            availColorsValues[i] = i;
            availColorsNames[i] = AvailableColors.values()[i].name();
        }
                        
        inputParameterInfos = new InputParameterInfo[] {new InputParameterInfo("Input data", InputParameterInfo.Type.BAR)};
        optInputParameterInfos = new OptInputParameterInfo[] {
            new OptInputParameterInfo("First period smoothing", OptInputParameterInfo.Type.OTHER,new IntegerRangeDescription(8, 1, 100, 1)),
            new OptInputParameterInfo("Second period smoothing", OptInputParameterInfo.Type.OTHER,new IntegerRangeDescription(8, 1, 100, 1)),
            new OptInputParameterInfo("Final Smoothing", OptInputParameterInfo.Type.OTHER,new IntegerRangeDescription(5, 1, 100, 1)),
            new OptInputParameterInfo("Use MQ4 version", OptInputParameterInfo.Type.OTHER, new BooleanOptInputDescription(true) ),
            new OptInputParameterInfo("Line TickNess", OptInputParameterInfo.Type.OTHER, new IntegerRangeDescription(3, 1, 50, 1)),
            new OptInputParameterInfo("UpTrend Color", OptInputParameterInfo.Type.OTHER, new IntegerListDescription(AvailableColors.GREEN.ordinal(), availColorsValues, availColorsNames)),
            new OptInputParameterInfo("DownTrend Color", OptInputParameterInfo.Type.OTHER, new IntegerListDescription(AvailableColors.RED.ordinal(), availColorsValues, availColorsNames))                        
            };
        
        outputParameterInfo1 = new OutputParameterInfo("Zero line", OutputParameterInfo.Type.DOUBLE,OutputParameterInfo.DrawingStyle.LINE) ;
        outputParameterInfo1.setColor(Color.ORANGE);
        outputParameterInfo2 = new OutputParameterInfo("TVI", OutputParameterInfo.Type.DOUBLE,OutputParameterInfo.DrawingStyle.LINE) ;
        outputParameterInfo2.setColor(Color.WHITE);
        outputParameterInfo3 = new OutputParameterInfo("TVI", OutputParameterInfo.Type.OBJECT,OutputParameterInfo.DrawingStyle.LINE) ;
        outputParameterInfo3.setDrawnByIndicator(true);
        
        outputParameterInfos = new OutputParameterInfo[] {
            outputParameterInfo1,
            outputParameterInfo2,
            outputParameterInfo3
        };
            
        ema1 = context.getIndicatorsProvider().getIndicator("EMA");
        ema2 = context.getIndicatorsProvider().getIndicator("EMA");           
        ema3 = context.getIndicatorsProvider().getIndicator("EMA");
        ema4 = context.getIndicatorsProvider().getIndicator("EMA");                    
        sma = context.getIndicatorsProvider().getIndicator("SMA");
    }

    public IndicatorResult calculate(int startIndex, int endIndex) {
        try{

	        //calculating startIndex taking into account lookback value
	        if (startIndex - getLookback() < 0) {
	            startIndex -= startIndex - getLookback();
	        }
            
            double[][] retObj ;
            if( useMQ4version )
            {
                retObj = getTickListMQL4(startIndex - getLookback(), endIndex);
            }else
            {
                retObj = getTickList(startIndex - getLookback(), endIndex);
            }

			double[] upTicks = retObj[0] ;
            double[] downTicks = retObj[1] ;
            
            double[] emaA1 = new double[upTicks.length  - ema1.getLookback() ];
            double[] emaA2 = new double[downTicks.length  - ema1.getLookback() ];
                                
			ema1.setOptInputParameter(0, timePeriodR);
			ema2.setOptInputParameter(0, timePeriodR);            
            ema1.setOutputParameter(0, emaA1);
            ema2.setOutputParameter(0, emaA2);                   
            ema1.setInputParameter(0, upTicks);
            ema2.setInputParameter(0, downTicks);              		    

            IndicatorResult ema1Result = ema1.calculate(startIndex - ema1.getLookback() - smoothPeriod , endIndex);
            IndicatorResult ema2Result = ema2.calculate(startIndex - ema2.getLookback() - smoothPeriod , endIndex);            
            
            double[] emaA3 = new double[ema1Result.getNumberOfElements() - ema3.getLookback()];
            double[] emaA4 = new double[ema2Result.getNumberOfElements() - ema4.getLookback()];                        
            ema3.setOptInputParameter(0, timePeriodS);
            ema4.setOptInputParameter(0, timePeriodS);
            ema3.setOutputParameter(0, emaA3);
            ema4.setOutputParameter(0, emaA4);                   
            ema3.setInputParameter(0, emaA1);
            ema4.setInputParameter(0, emaA2);                        
            
            IndicatorResult ema3Result = ema3.calculate(0 , ema1Result.getNumberOfElements()-1);
            IndicatorResult ema4Result = ema4.calculate(0 , ema2Result.getNumberOfElements()-1);
                                                
            double[] demaOut = new double[ema3Result.getNumberOfElements()];

            int i, k, j ;
	        for (i = 0, k = (ema3Result.getNumberOfElements()); i < k; i++) {            	        
	            double demaUpTicks, demaDnTicks ;
	            demaUpTicks = emaA3[i];
	            demaDnTicks = emaA4[i];
	            // tvi calc
	            double tvi =  100 * ( (demaUpTicks-demaDnTicks)/(demaUpTicks+demaDnTicks) ) ;
	            demaOut[i] = tvi ;	                        	
	        } 
            
            double[] smaA3 = new double[demaOut.length - sma.getLookback()];
                                    
            sma.setOptInputParameter(0, smoothPeriod);
	        sma.setInputParameter(0, demaOut);
            sma.setOutputParameter(0, smaA3);
            IndicatorResult smaResult = sma.calculate(0 , demaOut.length-1);            

	        for (i = 0, k = smaA3.length; i < k; i++) {
	            outputs[0][i] = 0 ;
	            outputs[1][i] = smaA3[i] ;
	            
	            double[] toDraw = new double[1];
	            toDraw[0] = smaA3[i] ;
	            // for drawing
	            output[0][i] = toDraw;
	        }
        
            return new IndicatorResult( startIndex , smaResult.getNumberOfElements() );              
        }
        catch(Exception ex){
            print(" error wwooowww  " + ex.getMessage());    
            return null;
        }
    }

    private double[][] getTickList(int startindex, int endindex) throws Exception
    {
        int i, j;
        
        double prevCandleClose = 0;
        double[] upTicks = new double[endindex - startindex + 1];       
        double[] downTicks = new double[endindex - startindex + 1]; 
        //print("getTickList -- startindex " + startindex + " endindex " + endindex );
        
        for (i = startindex , j = 0; i <= endindex; i++, j++) {
            if(j>0)
            {
                prevCandleClose = inputs[0][i-1].getTime() ;
            }
            upTicks[j] = 0;
            downTicks[j] = 0;         
            
            long startTimeFrom = 0 ;
            long startTimeTo = 0 ;
            
            if(i==endindex)
            {
                startTimeFrom = inputs[0][i].getTime() ;
                startTimeTo = history.getLastTick(theContext.getInstrument()).getTime() ;
            }else
            {
                long startTimeCurrentBar = inputs[0][i].getTime() ;                
                startTimeFrom = history.getPreviousBarStart( theContext.getPeriod() , startTimeCurrentBar ) ;                
                startTimeTo = startTimeCurrentBar ;                                    
            }
            
            /*
            if(i==endIndex)
            {
                startTimeTo = history.getLastTick(theContext.getInstrument()).getTime() ;                  
            }
            */
            
            // avoiding error
            if(startTimeFrom>startTimeTo)
                startTimeFrom = startTimeTo;
                
            java.util.List<ITick> tickList = history.getTicks(theContext.getInstrument() , startTimeFrom , startTimeTo) ;
                            
            if(tickList.size()>1)
            {                   
                for(int z=1;z<tickList.size();z++)
                {
                    ITick currTick = (ITick)tickList.get(z) ;
                    ITick prevTick = (ITick)tickList.get(z-1) ;
                    double currValue = (currTick.getBid()+currTick.getAsk())/2 ;
                    double prevValue = (prevTick.getBid()+prevTick.getAsk())/2 ;
                    if(currValue>prevValue)
                        upTicks[j] = upTicks[j] + 1 ;
                    if(currValue<prevValue)
                        downTicks[j] = downTicks[j] + 1 ;
                        
                }                        
            }else if(tickList.size()>0)
            {
                if(prevCandleClose!=0)
                {
                    ITick firstTick  = (ITick)tickList.get(0);
                    double currValue = (firstTick.getBid()+firstTick.getAsk())/2 ;
                    if(currValue>prevCandleClose)
                        upTicks[j] = upTicks[j] + 1 ;
                    if(currValue<prevCandleClose)
                        downTicks[j] = downTicks[j] + 1 ;
                                            
                }
            }                
        }
        
        double[][] retObj = new double[2][];
        retObj[0] = upTicks ;        
        retObj[1] = downTicks ;
        
        return retObj;        
    }
    

    private double[][] getTickListMQL4(int startindex, int endindex) throws Exception
    {
        int i, j;
        
        double[] upTicks = new double[endindex - startindex + 1];       
        double[] downTicks = new double[endindex - startindex + 1]; 
        
        for (i = startindex , j = 0; i <= endindex; i++, j++) {
            
            upTicks[j] = ( inputs[0][i].getVolume() + ( inputs[0][i].getClose()- inputs[0][i].getOpen() )/ theContext.getInstrument().getPipValue() ) / 2 ;
            downTicks[j] = inputs[0][i].getVolume() - upTicks[j] ;         
        }
        
        double[][] retObj = new double[2][];
        retObj[0] = upTicks ;        
        retObj[1] = downTicks ;
        
        return retObj;        
    }

         
                           
    public IndicatorInfo getIndicatorInfo() {
        return indicatorInfo;
    }

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

    public int getLookback() {
        return ema1.getLookback() + ema3.getLookback() + sma.getLookback();
    }

    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:
                timePeriodR = (Integer) value;
                ema1.setOptInputParameter(0, timePeriodR);            
                ema2.setOptInputParameter(0, timePeriodR);
                break;
            case 1:
                timePeriodS = (Integer) value;
                ema3.setOptInputParameter(0, timePeriodS);
                ema4.setOptInputParameter(0, timePeriodS);
                break;  
            case 2:
                smoothPeriod = (Integer) value;
                sma.setOptInputParameter(0, smoothPeriod);
                break;                             
            case 3:
                useMQ4version = (Boolean) value;                            
                break;
            case 4:
                TickNess = (Integer) value;
                break;                                                       
            case 5:
                int selUpColor = (Integer) value;
                upTrendColor = AvailableColors.values()[selUpColor].getColor();
                break;                 
            case 6:
                int selDownColor = (Integer) value;
                downTrendColor = AvailableColors.values()[selDownColor].getColor();
                break;                  
        }        
    }

    public void setOutputParameter(int index, Object array) {
        if(index<2)
        {        
            outputs[index] = (double[]) array;
        }else{    
            output[0] = (Object[]) array;
        }        
    }
 
    public Point drawOutput(Graphics g, int outputIdx, Object values2, Color color, Stroke stroke,
                           IIndicatorDrawingSupport indicatorDrawingSupport, java.util.List<Shape> shapes,
                           Map<Color, java.util.List<Point>> handles) {
        Object[] values = (Object[]) values2;
        Graphics2D g2 = (Graphics2D) g;
        g2.setStroke(new BasicStroke(TickNess));
               
        if (values2 != null) {
            for (int j = indicatorDrawingSupport.getIndexOfFirstCandleOnScreen(), k =
                    j + indicatorDrawingSupport.getNumberOfCandlesOnScreen() ; j < k; j++) {
                if (j > 0) {
                                                                                    
                    if (values[j] != null) {
                        
                        double[] pointPrev = (double[]) values[j - 1];
                        double[] point = (double[]) values[j];
                                            
                        int barMiddle = (int) indicatorDrawingSupport.getMiddleOfCandle(j);
                        int barMiddlePrev = (int) indicatorDrawingSupport.getMiddleOfCandle(j - 1);
                        
                        if (point[0] > pointPrev[0]) {
                            //g.setColor(upTrendColor);
                            g2.setColor(upTrendColor);
                        } else if (point[0] < pointPrev[0]) {
                            //g.setColor(downTrendColor);
                            g2.setColor(downTrendColor);
                        } else if (point[0] == pointPrev[0])
                        {
                            //g.setColor(VERY_DARK_YELLOW);
                            g2.setColor(VERY_DARK_YELLOW);
                        }
                        
                        int[] xPoints = new int[2];
                        int[] yPoints = new int[2];
                        
                        generalPath.reset();
                        
                        if (pointPrev[0] != 0 && (!Double.isNaN(pointPrev[0]))  && point[0] != 0 && (!Double.isNaN(point[0]) ) ) 
                        {
                            yPoints[0] = (int) indicatorDrawingSupport.getYForValue(pointPrev[0]);
                            yPoints[1] = (int) indicatorDrawingSupport.getYForValue(point[0]);

                            xPoints[0] = barMiddlePrev;
                            xPoints[1] = barMiddle;
                            
                            generalPath.moveTo(xPoints[0], yPoints[0]);    
                            generalPath.lineTo(xPoints[1], yPoints[1]);
                            
                            //g.drawLine(xPoints[0] , yPoints[0] , xPoints[1], yPoints[1]) ;
                            //drawThickLine(g,xPoints[0] , yPoints[0] , xPoints[1], yPoints[1], TickNess);                            
                            g2.draw(generalPath);
                        }
                        
                        int fontSize = 9;
                        g2.setFont(new Font(g2.getFont().getName(), g2.getFont().getStyle(), fontSize));
                        g2.setColor(Color.BLACK);
                        //g2.setStroke(new BasicStroke(1));
                        String text = "TVI by chriz [Indiana Pips]" ;
                        g2.drawString(text, 10  ,  indicatorDrawingSupport.getChartHeight() - 10);
                                                
                    }
                }
            }           
                                                
        }
        
        return null;
    }
       
    private void print(String sss)
    {
        theContext.getConsole().getOut().println(sss) ;
    }       
 
     private static enum AvailableColors {
        GREEN(Color.GREEN), 
        RED (Color.RED), 
        LIGHT_GREEN(new Color(0x80, 0xFF, 0x80)), 
        LIGHT_RED(new Color(0xFF, 0x80, 0x80)), 
        VERY_DARK_YELLOW(new Color(0x80, 0x80, 0x00)),
        DARK_RED ( new Color(0xc0, 0x00, 0x00)),
        VERY_DARK_RED (new Color(0x80, 0x00, 0x00)),
        VERY_DARK_GREEN (new Color(0x00, 0x80, 0x00)),
        DARK_GREEN (new Color(0x00, 0xC0, 0x00)),
        BLACK ( Color.BLACK ),
        BLUE ( Color.BLUE  ),
        CYAN ( Color.CYAN ),
        DARK_GRAY  ( Color.DARK_GRAY ),
        GRAY ( Color.GRAY ),
        LIGHT_GRAY ( Color.LIGHT_GRAY ),
        MAGENTA ( Color.MAGENTA ),
        ORANGE ( Color.ORANGE ),
        PINK ( Color.PINK),
        WHITE ( Color.WHITE ),
        YELLOW ( Color.YELLOW );
        
        Color theColor ;
        
        private AvailableColors(Color c) {
            theColor = c;
        }
        
        private Color getColor() {
            return theColor;
        }
     }

    private void printArray (double [] arr) {
        if (arr != null) {
            if (arr.length > 300) {
                return;
            }
            print("***** Start Array Output *****");
            for (int i=0; i<arr.length; i++) {
                print("arr[" + i + "] = " + arr[i]);
            }
            print("***** End Array Output *******");
        }
    }
           
}