package com.nitorcreations.willow.metrics;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.inject.Named;

import org.elasticsearch.client.Client;

import com.nitorcreations.willow.messages.AccessLogEntry;

@Named("/access")
public class AccessLogMetric extends FullMessageMultiseriesMetric<AccessLogEntry, Long, Long> {
  long[] limitValues = new long[] { 10L, 100L, 1000L, 10000L };

  private interface ValueGetter {
    long getValue(AccessLogEntry next);
  }
  private static class DurationValueGetter implements ValueGetter {
    @Override
    public long getValue(AccessLogEntry next) {
      return next.getDuration();
    }
  }
  private static class ReturnCodeValueGetter implements ValueGetter {
    @Override
    public long getValue(AccessLogEntry next) {
      return next.getStatus();
    }
  }
  private static final DurationValueGetter DURATION = new DurationValueGetter();
  private static final ReturnCodeValueGetter RETURNCODE = new ReturnCodeValueGetter();
  private ValueGetter getter = DURATION;

  @Override
  public Collection<SeriesData<Long, Long>> calculateMetric(Client client, MetricConfig conf) {
    limitValues = new long[conf.getLimits().length];
    for (int i = 0; i < conf.getLimits().length; i++) {
      limitValues[i] = Long.parseLong(conf.getLimits()[i]);
    }
    if (conf.hasType("statuses")) {
      getter = RETURNCODE;
      if (conf.getLimits().length == 0) {
        limitValues = new long[] { 200L, 300L, 400L, 500L, 600L };
      }
    }
    return super.calculateMetric(client, conf);
  }

  @Override
  protected void addValue(Map<String, SeriesData<Long, Long>> values, List<AccessLogEntry> preceeding, long stepTime, int stepLen, MetricConfig conf) {
    long[] buckets = new long[limitValues.length + 1];
    for (AccessLogEntry next : preceeding) {
      boolean inLimits = false;
      for (int i = 0; i < limitValues.length; i++) {
        if (getter.getValue(next) < limitValues[i]) {
          buckets[i]++;
          inLimits = true;
          break;
        }
      }
      if (!inLimits)
        buckets[limitValues.length]++;
    }
    String lower = "";
    addBucket(values, lower + "-", stepTime, buckets[0]);
    for (int i = 0; i < limitValues.length; i++) {
      lower = Long.toString(limitValues[i]);
    }
    addBucket(values, lower + "-", stepTime, buckets[buckets.length - 1]);
  }

  private void addBucket(Map<String, SeriesData<Long, Long>> values, String label, long time, long value) {
    SeriesData<Long, Long> bucket = values.get(label);
    if (bucket == null) {
      bucket = new SeriesData<>();
      bucket.key = label;
      values.put(label, bucket);
    }
    Point<Long, Long> p = new Point<>();
    p.x = time;
    p.y = value;
    bucket.values.add(p);
  }
}

@@ -1157,24 +1157,65 @@
 fig conf) %7B%0A
+    if (conf.getLimits().length %3E 0) %7B%0A  
     limitVal
@@ -1255,24 +1255,26 @@
 ength%5D;%0A    
+  
 for (int i =
@@ -1309,24 +1309,26 @@
 gth; i++) %7B%0A
+  
       limitV
@@ -1379,21 +1379,30 @@
 %5D);%0A    
+  
 %7D%0A   
+ 
+%7D else
  if (con
@@ -2207,16 +2207,109 @@
 r = %22%22;%0A
+    String upper = %22%22 + limitValues%5B0%5D;%0A    for (int i = 0; i %3C limitValues.length; i++) %7B%0A  
     addB
@@ -2325,32 +2325,40 @@
 ues, lower + %22-%22
+ + upper
 , stepTime, buck
@@ -2365,17 +2365,17 @@
 ets%5B
-0
+i
 %5D);%0A    
 for 
@@ -2370,32 +2370,67 @@
 %5D);%0A    
-for (int i = 0; 
+  lower = Long.toString(limitValues%5Bi%5D);%0A      if (
 i %3C limi
@@ -2435,37 +2435,36 @@
 mitValues.length
-; i++
+ - 1
 ) %7B%0A      lower 
@@ -2461,46 +2461,83 @@
     
+  
-low
+upp
 er = 
-Long.toString(limitValues%5Bi%5D);
+%22%22 + limitValues%5Bi+1%5D;%0A      %7D else %7B%0A        upper = %22%22;%0A      %7D
 %0A   

Fix accesslog metric