package jforex;

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

public class BetterVolume implements IIndicator {
    private IndicatorInfo indicatorInfo;
    private InputParameterInfo[] inputParameterInfos;
    private OptInputParameterInfo[] optInputParameterInfos;
    private OutputParameterInfo[] outputParameterInfos;
    private double[][][] inputs = new double[1][][];
    private int lookBack = 20;
    private int MAPeriod = 100;
    private double[][] outputs = new double[7][];
    
    private IIndicatorContext context ;
    
    public void onStart(IIndicatorContext context) {
        this.context = context ;
        indicatorInfo = new IndicatorInfo("BetterVolume", "Better Volume", "BVI",
        		false, false, true, 1, 2, 7);
        inputParameterInfos = new InputParameterInfo[] {new InputParameterInfo("Input data", InputParameterInfo.Type.PRICE)};
        optInputParameterInfos = new OptInputParameterInfo[] {
            new OptInputParameterInfo("LookBack", OptInputParameterInfo.Type.OTHER,new IntegerRangeDescription(20, 2, 100, 1)),
            new OptInputParameterInfo("MA Period", OptInputParameterInfo.Type.OTHER,new IntegerRangeDescription(100, 2, 1000, 1)),
            };
        outputParameterInfos = new OutputParameterInfo[] {
            new OutputParameterInfo("Climax High", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.HISTOGRAM)
                {{
                     setColor(Color.RED);
                }},
            new OutputParameterInfo("Neutral", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.HISTOGRAM)
                {{
                     setColor(Color.BLUE);
                }},
            new OutputParameterInfo("Low", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.HISTOGRAM)
                {{
                     setColor(Color.YELLOW);
                }},                
            new OutputParameterInfo("High Churn", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.HISTOGRAM)
                {{
                     setColor(Color.GREEN);
                }},                                
            new OutputParameterInfo("Climax Low", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.HISTOGRAM)
                {{
                     setColor(Color.BLACK);
                }},                      
            new OutputParameterInfo("Climax Churn", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.HISTOGRAM)
                {{
                     setColor(Color.MAGENTA);
                }},
            new OutputParameterInfo("Moving Average", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE)
                {{
                     setColor(Color.PINK);
                }}                
        };
        
                
    }

    public IndicatorResult calculate(int startIndex, int endIndex) {
        
        double VolLowest,Range,Value2,Value3,HiValue2,HiValue3,LoValue3,tempv2,tempv3,tempv;        
        
        //calculating startIndex taking into account lookback value
        if (startIndex - getLookback() < 0) {
            startIndex -= startIndex - getLookback();
        }
        int i, j;

        for (i = startIndex, j = 0; i <= endIndex; i++, j++) {
        	
            outputs[0][j] = 0; 
            outputs[1][j] = inputs[0][4][i]; 
            outputs[2][j] = 0; 
            outputs[3][j] = 0; 
            outputs[4][j] = 0; 
            outputs[5][j] = 0;
            Value2=0;
            Value3=0;
            HiValue2=0;
            HiValue3=0;
            LoValue3=99999999;
            tempv2=0;
            tempv3=0;
            tempv=0;
                                                
            // get the lowest value for lookbak bars
            VolLowest = inputs[0][4][i - lookBack];             
        	for (int k = lookBack; k >= 0; k--) {
				if(inputs[0][4][i - k] < VolLowest){  
				    VolLowest = inputs[0][4][i - k];  
				}                
        	}
            
			if (inputs[0][4][i] == VolLowest)
			{
			   outputs[2][j] = inputs[0][4][i];
			   outputs[1][j] = 0;
			}
            
			Range = (inputs[0][2][i]-inputs[0][3][i]);
			Value2 = inputs[0][4][i] * Range;
			
			if (Range != 0)
			    Value3 = inputs[0][4][i]/Range;


            // begin moving average computation                                                
            for (int k = MAPeriod; k >= 0; k--) {
                tempv += inputs[0][4][i - k] ;
            }
            outputs[6][j] = tempv/MAPeriod ;
            // end 
            
            for (int k = lookBack; k >= 0; k--) {
				tempv2 = inputs[0][4][i - k]*(inputs[0][2][i - k]-inputs[0][3][i - k]); 
				if ( tempv2 >= HiValue2 )
				    HiValue2 = tempv2;
				    
				if ( inputs[0][4][i - k]*(inputs[0][2][i - k]-inputs[0][3][i - k]) != 0 )
                {           
					tempv3 = inputs[0][4][i - k] / (inputs[0][2][i - k]-inputs[0][3][i - k]);
					if ( tempv3 > HiValue3 ) 
					    HiValue3 = tempv3; 
					if ( tempv3 < LoValue3 )
					    LoValue3 = tempv3;
                } 
            }            

            /*
            HiValue2 = round(HiValue2, 2) ;
            HiValue3 = round(HiValue3, 2) ;
            Value3 = round(Value3, 2) ;
            Value2 = round(Value2, 2) ;
            */
                        
			if ( Value2 == HiValue2  && inputs[0][1][i] > (inputs[0][2][i]+inputs[0][3][i]) / 2 )
			{
				outputs[0][j] = inputs[0][4][i];
				outputs[1][j]=0;
				outputs[2][j]=0;
			}   
			
			if ( Value3 == HiValue3 )
			{
				outputs[3][j] = inputs[0][4][i];                
				outputs[1][j] = 0;
				outputs[2][j] = 0;
				outputs[0][j] = 0;
			}

			if ( Value2 == HiValue2 && Value3 == HiValue3 )
			{
				outputs[5][j] = inputs[0][4][i];
				outputs[1][j]=0;
				outputs[0][j]=0;
				outputs[3][j]=0;
				outputs[2][j]=0;
			} 
			if ( Value2 == HiValue2  && inputs[0][1][i] <= (inputs[0][2][i]+inputs[0][3][i]) / 2 )
			{
				outputs[4][j] = inputs[0][4][i];
				outputs[5][j]=0;
				outputs[1][j]=0;
				outputs[0][j]=0;
				outputs[3][j]=0;
				outputs[2][j]=0;
			} 
                                                                        
        }
        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() {
        if ( lookBack>MAPeriod)
            return lookBack;
        else
            return MAPeriod;
    }

    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] = (double[][]) array;
    }

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

    public void setOutputParameter(int index, Object array) {
        outputs[index] = (double[]) array;
    }
    
    // positive value only.
    public static double round(double value, int decimalPlace)
    {
      double power_of_ten = 1;
      // floating point arithmetic can be very tricky.
      // that's why I introduce a "fudge factor"
      double fudge_factor = 0.05;
      while (decimalPlace-- > 0) {
         power_of_ten *= 10.0d;
         fudge_factor /= 10.0d;
      }
      return Math.round((value + fudge_factor)* power_of_ten)  / power_of_ten;
    }

    
}