/**
 * Copyright (C) 2010-14 pvmanager developers. See COPYRIGHT.TXT
 * All rights reserved. Use is subject to license terms. See LICENSE.TXT
 */
package org.epics.pvmanager.formula;

import static org.epics.vtype.ValueFactory.*;

import java.util.Arrays;
import java.util.List;
import org.epics.util.array.ArrayDouble;
import org.epics.util.array.ArrayInt;
import org.epics.util.array.IteratorNumber;
import org.epics.util.array.ListNumber;
import org.epics.util.array.ListNumbers;
import org.epics.util.stats.Range;
import org.epics.util.stats.Ranges;
import org.epics.util.stats.Statistics;
import org.epics.util.stats.StatisticsUtil;

import org.epics.vtype.VNumber;
import org.epics.vtype.VNumberArray;

/**
 * @author shroffk
 * 
 */
class HistogramOfFormulaFunction implements FormulaFunction {

    @Override
    public boolean isPure() {
	return true;
    }

    @Override
    public boolean isVarArgs() {
	return false;
    }

    @Override
    public String getName() {
	return "histogramOf";
    }

    @Override
    public String getDescription() {
	return "Returns a histograms of the elements in the array.";
    }

    @Override
    public List<Class<?>> getArgumentTypes() {
	return Arrays.<Class<?>> asList(VNumberArray.class);
    }

    @Override
    public List<String> getArgumentNames() {
	return Arrays.asList("Array", "index");
    }

    @Override
    public Class<?> getReturnType() {
	return VNumber.class;
    }

    @Override
    public Object calculate(List<Object> args) {
	VNumberArray numberArray = (VNumberArray) args.get(0);
        if (numberArray == null) {
            return null;
        }
        Statistics stats = StatisticsUtil.statisticsOf(numberArray.getData());
        int nBins = 100;

        IteratorNumber newValues = numberArray.getData().iterator();
        double minValueRange = stats.getMinimum().doubleValue();
        double maxValueRange = stats.getMaximum().doubleValue();
        
        ListNumber xBoundaries = ListNumbers.linearListFromRange(minValueRange, maxValueRange, nBins + 1);
        Range xRange = stats;
        String unit = numberArray.getUnits();
        int[] binData = new int[nBins];
        while (newValues.hasNext()) {
            double value = newValues.nextDouble();
            // Check value in range
            if (Ranges.contains(xRange, value)) {

                int bin = (int) Math.floor(Ranges.normalize(xRange, value) * nBins);
                if (bin == nBins) {
                    bin--;
                }

                binData[bin]++;
            }
        }
        
	return newVNumberArray(new ArrayInt(binData), new ArrayInt(nBins), Arrays.asList(newDisplay(xBoundaries, unit)),
		numberArray, numberArray, displayNone());
    }

}

@@ -630,16 +630,58 @@
 icsUtil;
+%0Aimport org.epics.util.text.NumberFormats;
 %0A%0Aimport
@@ -812,19 +812,24 @@
 ion 
-implement
+extend
 s 
+Stateful
 Form
@@ -847,73 +847,8 @@
  %7B%0A%0A
-    @Override%0A    public boolean isPure() %7B%0A%09return true;%0A    %7D%0A%0A
     
@@ -1411,24 +1411,181 @@
 class;%0A    %7D
+%0A    %0A    private VNumberArray previousValue;%0A    private VNumberArray previousResult;%0A    private double previousMaxCount;%0A    private Range previousXRange;
 %0A%0A    @Overr
@@ -1775,103 +1775,506 @@
     
-Statistics stats = StatisticsUtil.statisticsOf(numberArray.getData());%0A        int nBins = 100;
+%0A        // If no change, return previous%0A        if (previousValue == numberArray) %7B%0A            return previousResult;%0A        %7D%0A        %0A        Statistics stats = StatisticsUtil.statisticsOf(numberArray.getData());%0A        int nBins = 100;%0A        Range aggregatedRange = Ranges.aggregateRange(stats, previousXRange);%0A        Range xRange;%0A        if (Ranges.overlap(stats, aggregatedRange) %3E= 0.75) %7B%0A            xRange = aggregatedRange;%0A        %7D else %7B%0A            xRange = stats;%0A        %7D
 %0A%0A  
@@ -2367,29 +2367,30 @@
 alueRange = 
-stats
+xRange
 .getMinimum(
@@ -2437,21 +2437,22 @@
 Range = 
-stats
+xRange
 .getMaxi
@@ -2592,38 +2592,8 @@
 1);%0A
-        Range xRange = stats;%0A
     
@@ -2674,16 +2674,45 @@
 nBins%5D;%0A
+        double maxCount = 0;%0A
         
@@ -3090,26 +3090,124 @@
         
-%7D%0A
+    if (binData%5Bbin%5D %3E maxCount) %7B%0A                    maxCount = binData%5Bbin%5D;%0A                %7D%0A  
         
 %7D%0A      
@@ -3190,32 +3190,34 @@
     %7D%0A          
+  
 %7D%0A        %0A%09retu
@@ -3210,24 +3210,298 @@
         
-%0A%09return
+%7D%0A        %0A        if (previousMaxCount %3E maxCount && previousMaxCount %3C maxCount * 2.0) %7B%0A            maxCount = previousMaxCount;%0A        %7D%0A        %0A        previousMaxCount = maxCount;%0A        previousXRange = xRange;%0A        previousValue = numberArray;%0A        previousResult =
  newVNum
@@ -3631,22 +3631,162 @@
 ay, 
-d
+newD
 isplay
-None())
+(0.0, 0.0, 0.0, %22count%22, NumberFormats.format(0), maxCount, maxCount, maxCount, Double.NaN, Double.NaN));%0A        %0A        return previousResult
 ;%0A  

update to current SNAPSHOT