import com.dukascopy.api.indicators.*;
import com.dukascopy.api.IIndicators;

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

/**
 * ALMA by Arnaud Legoux / Dimitris Kouzis-Loukas / Anthony Cascino 
 * www.arnaudlegoux.com                      
 * Coded by: chriz aka Indiana Pips
 * Date: April 06, 2011
 * email: puntasabbioni/AT/gmail.com 
 */

public class ALMA implements IIndicator {
    private IndicatorInfo indicatorInfo;
    private InputParameterInfo[] inputParameterInfos;
    private OptInputParameterInfo[] optInputParameterInfos;
    private OutputParameterInfo[] outputParameterInfos;
    private double[][] inputs = new double[1][];

    private double[][] outputs = new double[1][];
    private int iWindowSize = 9;
    private double iM_Sigma = 6.0;
    private double iP_Sample = 0.85;

    private double[] aALMA;
            
    public void onStart(IIndicatorContext context) {
        indicatorInfo = new IndicatorInfo("ALMA", "Arnaud Legoux Moving Average", "Overlap Studies",
        		true, false, true, 1, 3, 1);
        
        inputParameterInfos = new InputParameterInfo[] {
            new InputParameterInfo("Input data", InputParameterInfo.Type.DOUBLE){{                
                setAppliedPrice(IIndicators.AppliedPrice.CLOSE);
            }}
        };
        
        optInputParameterInfos = new OptInputParameterInfo[] {
            new OptInputParameterInfo("ALMA period. Must be an ODD number.", OptInputParameterInfo.Type.OTHER,new IntegerRangeDescription(9, 1, 50, 1)),
            new OptInputParameterInfo("Precision / Smoothing", OptInputParameterInfo.Type.OTHER,new DoubleRangeDescription(6, 1, 50, 0.1, 1)),
            new OptInputParameterInfo("Sample point. Where in terms of the window should we take the current value (0-1)", OptInputParameterInfo.Type.OTHER,new DoubleRangeDescription(0.85, 0, 1, 0.01, 2))
            };
        outputParameterInfos = new OutputParameterInfo[] { 
                new OutputParameterInfo("ALMA", OutputParameterInfo.Type.DOUBLE, OutputParameterInfo.DrawingStyle.LINE) {{
	                setColor(Color.BLUE);
	            }}                
            };           
    }

	private void ResetWindow() 
	{	   	   
		aALMA = new double[iWindowSize];
		double m = (int)Math.floor(iP_Sample * (double)(iWindowSize - 1));
		double s = iWindowSize;
		s /= iM_Sigma;
		
		for (int i = 0; i < iWindowSize; i++) 
		{
		    aALMA[i] = Math.exp(-((((double)i)-m)*(((double)i)-m))/(2*s*s));
		}
	}    
        
    public IndicatorResult calculate(int startIndex, int endIndex) {
        //calculating startIndex taking into account lookback value
        if (startIndex - getLookback() < 0) {
            startIndex -= startIndex - getLookback();
        }
                
        ResetWindow();
        
        int i, j;            
        for (i = startIndex, j = 0; i <= endIndex; i++, j++) {

		    double agr  = 0;
		    double norm = 0;

        	for (int k = iWindowSize, z=0 ; k > 0; k--,z++) {
        		agr += aALMA[z] * inputs[0][i - k];
                norm += aALMA[z];
        	}
            
            if (norm != 0) agr /= norm;
            
            outputs[0][j] = agr;
        }

        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() {
        return iWindowSize;
    }

    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) {

        switch (index) {
            case 0:
                iWindowSize = (Integer) value;
                break;
            case 1:
                iM_Sigma = (Double) value;
                break;                                              
            case 2:
                iP_Sample = (Double) value;
                break;                                                       
            default:
                throw new ArrayIndexOutOfBoundsException(index);  
        }      
    }

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