package org.opennms.newts.api.query;


import java.util.List;

import org.opennms.newts.api.Duration;
import org.opennms.newts.api.Gauge;
import org.opennms.newts.api.ValueType;

import com.google.common.base.Function;

import static com.google.common.base.Preconditions.checkNotNull;


public class Datasource {
    
    public static interface AggregationFunction extends Function<List<ValueType<Double>>, ValueType<Double>> {
        /**
         * An Aggregation function is a function that takes the list of PDPs should be aggregated in a single bucket
         * of resolution.  For example, if the step size if 5m and the resolution is 1h then the aggregation function
         * will be called with a list of 12 values.  These function should ignore all NaN values as if they were not
         * included in the list at all.
         */
        public ValueType<Double> apply(List<ValueType<Double>> input);
    };
    
    public static enum StandardAggregationFunctions implements AggregationFunction {
        
        // These function assume that the xff calculation is done elsewhere and that the values that 
        // are returned is non sensible if there are more NaN then defined by the xff.
        AVERAGE {

            @Override
            public ValueType<Double> apply(List<ValueType<Double>> input) {
                int count = 0;
                ValueType<Double> sum = new Gauge(0);
                for(ValueType<Double> item : input) {
                    if (!item.isNan()) {
                        sum = sum.plus(item);
                        count++;
                    }
                }
                
                return sum.divideBy(count);
            }
            
        },
        MAX {

            @Override
            public ValueType<Double> apply(List<ValueType<Double>> input) {
                ValueType<Double> max = new Gauge(Double.MIN_VALUE);
                for(ValueType<Double> item : input) {
                    if (!item.isNan()) {
                        double diff = item.minus(max).doubleValue();
                        max = diff > 0 ? item : max;
                    }
                }
                return max;
            }
            
        },
        MIN {

            @Override
            public ValueType<Double> apply(List<ValueType<Double>> input) {
                ValueType<Double> min = new Gauge(Double.MAX_VALUE);
                for(ValueType<Double> item : input) {
                    if (!item.isNan()) {
                        double diff = item.minus(min).doubleValue();
                        min = diff < 0 ? item : min;
                    }
                }
                return min;
            }
            
        }
        
        
    }

    private final String m_label;
    private final String m_source;
    private final Duration m_heartbeat;
    private final double m_xff;
    private final AggregationFunction m_aggregationFunction;

    public Datasource(String label, String sourceName, Duration heartbeat, double xff, AggregationFunction aggregationFunction) {
        checkNotNull(label, "label argument");
        checkNotNull(sourceName, "source name argument");
        checkNotNull(heartbeat, "heartbeat argument");

        m_label = label;
        m_source = sourceName;
        m_heartbeat = heartbeat;
        m_xff = xff;
        m_aggregationFunction = aggregationFunction;
    }

    public String getLabel() {
        return m_label;
    }

    public String getSource() {
        return m_source;
    }

    public Duration getHeartbeat() {
        return m_heartbeat;
    }
    
    public double getXff() {
        return m_xff;
    }
    
    public AggregationFunction getAggregationFuction() {
        return m_aggregationFunction;
    }

}

@@ -43,175 +43,124 @@
 ort 
-java.util.List;%0A%0Aimport org.opennms.newts.api.Duration;%0Aimport org.opennms.newts.api.Gauge;%0Aimport org.opennms.newts.api.ValueType;%0A%0Aimport com.google.common.base.Func
+static com.google.common.base.Preconditions.checkNotNull;%0A%0Aimport java.util.List;%0A%0Aimport org.opennms.newts.api.Dura
 tion
@@ -161,39 +161,32 @@
 ration;%0A%0Aimport 
-static 
 com.google.commo
@@ -196,34 +196,16 @@
 ase.
-Preconditions.checkNotNull
+Function
 ;%0A%0A%0A
@@ -309,46 +309,24 @@
 ist%3C
-ValueType%3C
 Double%3E
-%3E
 , 
-ValueType%3C
 Double%3E
-%3E
  %7B%0A 
@@ -755,33 +755,22 @@
  public 
-ValueType%3C
 Double
-%3E
  apply(L
@@ -773,34 +773,23 @@
 ly(List%3C
-ValueType%3C
 Double%3E
-%3E
  input);
@@ -1144,33 +1144,22 @@
  public 
-ValueType%3C
 Double
-%3E
  apply(L
@@ -1162,34 +1162,23 @@
 ly(List%3C
-ValueType%3C
 Double%3E
-%3E
  input) 
@@ -1230,44 +1230,25 @@
     
-ValueType%3C
 Double
-%3E
  sum = 
-new Gauge(0)
+0.0d
 ;%0A  
@@ -1265,33 +1265,22 @@
     for(
-ValueType%3C
 Double
-%3E
  item : 
@@ -1309,35 +1309,41 @@
        if (!
-item.isNan(
+Double.isNaN(item
 )) %7B%0A       
@@ -1367,24 +1367,15 @@
 sum 
++
 = 
-sum.plus(
 item
-)
 ;%0A  
@@ -1492,24 +1492,16 @@
  sum
-.divideBy(
+ / 
 count
-)
 ;%0A  
@@ -1592,33 +1592,22 @@
  public 
-ValueType%3C
 Double
-%3E
  apply(L
@@ -1610,34 +1610,23 @@
 ly(List%3C
-ValueType%3C
 Double%3E
-%3E
  input) 
@@ -1647,42 +1647,21 @@
     
-ValueType%3C
 Double
-%3E
  max = 
-new Gauge(
 Doub
@@ -1668,25 +1668,24 @@
 le.MIN_VALUE
-)
 ;%0A          
@@ -1694,33 +1694,22 @@
     for(
-ValueType%3C
 Double
-%3E
  item : 
@@ -1738,35 +1738,41 @@
        if (!
-item.isNan(
+Double.isNaN(item
 )) %7B%0A       
@@ -1810,33 +1810,14 @@
 item
-.minus(max).doubleValue()
+ - max
 ;%0A  
@@ -2029,33 +2029,22 @@
  public 
-ValueType%3C
 Double
-%3E
  apply(L
@@ -2051,26 +2051,15 @@
 ist%3C
-ValueType%3C
 Double%3E
-%3E
  inp
@@ -2084,42 +2084,21 @@
     
-ValueType%3C
 Double
-%3E
  min = 
-new Gauge(
 Doub
@@ -2109,17 +2109,16 @@
 AX_VALUE
-)
 ;%0A      
@@ -2135,25 +2135,14 @@
 for(
-ValueType%3C
 Double
-%3E
  ite
@@ -2183,19 +2183,25 @@
 f (!
-item.isNan(
+Double.isNaN(item
 )) %7B
@@ -2247,33 +2247,14 @@
 item
-.minus(min).doubleValue()
+ - min
 ;%0A  

update aggregate functions to use Double instead of ValueType