/*
 * Copyright 2016 Timothy Brooks
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package net.uncontended.precipice.metrics.experimental;

import net.uncontended.precipice.metrics.AddCounter;
import net.uncontended.precipice.metrics.CountMetrics;
import net.uncontended.precipice.time.Clock;
import net.uncontended.precipice.time.SystemTime;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public class IntervalCountMetrics<T extends Enum<T>> implements CountMetrics<T> {

    private final Class<T> type;
    private final Clock clock;
    private final CountMetrics<T> totalCounter;
    private final AtomicReference<CountMetrics<T>> intervalCounter;
    private volatile CountMetrics<T> previousInterval;

    public IntervalCountMetrics(Class<T> type) {
        this(type, new SystemTime());
    }

    public IntervalCountMetrics(Class<T> type, Clock clock) {
        this.type = type;
        this.clock = clock;
        this.totalCounter = new AddCounter<>(this.type);
        this.intervalCounter = new AtomicReference<CountMetrics<T>>(new AddCounter<>(this.type));
    }

    @Override
    public void add(T metric, long delta) {
        add(metric, delta, clock.nanoTime());

    }

    @Override
    public void add(T metric, long delta, long nanoTime) {
        totalCounter.add(metric, delta, nanoTime);
    }

    @Override
    public long getCount(T metric) {
        return totalCounter.getCount(metric);
    }

    @Override
    public Class<T> getMetricType() {
        return type;
    }

    public synchronized CountMetrics<T> intervalCounts() {
        return intervalCounter.getAndSet(new AddCounter<>(this.type));
    }

    public CountMetrics<T> previousIntervalCounts() {
        return previousInterval;
    }

    public Iterable<CountMetrics<T>> metricCounters(long timePeriod, TimeUnit timeUnit) {
        return metricCounters(timePeriod, timeUnit, clock.nanoTime());
    }

    public Iterable<CountMetrics<T>> metricCounters(long timePeriod, TimeUnit timeUnit, long nanoTime) {
        return null;
    }
}

@@ -856,99 +856,8 @@
 e;%0A%0A
-import java.util.concurrent.TimeUnit;%0Aimport java.util.concurrent.atomic.AtomicReference;%0A%0A
 publ
@@ -935,16 +935,39 @@
 s%3CT%3E %7B%0A%0A
+    private long meta;%0A
     priv
@@ -1040,28 +1040,22 @@
 e final 
-CountMetrics
+Holder
 %3CT%3E tota
@@ -1080,46 +1080,26 @@
 ate 
-final AtomicReference%3CCountMetrics
+volatile Holder
 %3CT%3E
-%3E
  int
@@ -1137,32 +1137,22 @@
 ile 
-CountMetrics%3CT%3E previous
+Holder%3CT%3E last
 Inte
@@ -1396,24 +1396,37 @@
 unter = new 
+Holder%3C%3E(new 
 AddCounter%3C%3E
@@ -1436,16 +1436,25 @@
 is.type)
+, meta++)
 ;%0A      
@@ -1486,39 +1486,15 @@
 new 
-AtomicReference%3CCountMetrics%3CT%3E
+Holder%3C
 %3E(ne
@@ -1510,32 +1510,40 @@
 ter%3C%3E(this.type)
+, meta++
 );%0A    %7D%0A%0A    @O
@@ -1729,22 +1729,33 @@
         
-totalC
+intervalCounter.c
 ounter.a
@@ -1853,45 +1853,312 @@
     
-return totalCounter.getCount(metric);
+for (; ; ) %7B%0A            Holder%3CT%3E localTotal = this.totalCounter;%0A            Holder%3CT%3E localInterval = this.intervalCounter;%0A            if (localTotal.id != localInterval.id) %7B%0A                return localTotal.counter.getCount(metric) + localInterval.counter.getCount(metric);%0A            %7D%0A        %7D
 %0A   
@@ -2332,47 +2332,25 @@
 ount
-er.getAndSet(new AddCounter%3C%3E(this.type
+s(clock.nanoTime(
 ));%0A
@@ -2367,16 +2367,29 @@
  public 
+synchronized 
 CountMet
@@ -2396,25 +2396,17 @@
 rics%3CT%3E 
-previousI
+i
 ntervalC
@@ -2415,345 +2415,702 @@
 nts(
-) %7B%0A        return previousInterval
+long nanoTime) %7B%0A        Holder%3CT%3E old = this.intervalCounter
 ;%0A    
-%7D%0A%0A
     
-public Iterable%3CCountMetrics%3CT%3E%3E metricCounters(long timePeriod, TimeUnit timeUnit) %7B%0A        return metricCounters(timePeriod, timeUnit, clock.nanoTime());%0A    %7D%0A%0A    public Iterable%3CCountMetrics%3CT%3E%3E metricCounters(long timePeriod, TimeUnit timeUnit, long nanoTime) %7B%0A        return null;
+this.intervalCounter = new Holder%3C%3E(new AddCounter%3C%3E(this.type), meta++);%0A        for (T metric : type.getEnumConstants()) %7B%0A            totalCounter.counter.add(metric, old.counter.getCount(metric));%0A        %7D%0A        this.lastInterval = old;%0A        return old.counter;%0A    %7D%0A%0A    public CountMetrics%3CT%3E lastIntervalCounts() %7B%0A        return lastInterval.counter;%0A    %7D%0A%0A    private static class Holder%3CT extends Enum%3CT%3E%3E %7B%0A        private CountMetrics%3CT%3E counter;%0A        private long id;%0A%0A        private Holder(CountMetrics%3CT%3E counter, long id) %7B%0A            this.counter = counter;%0A            this.id = id;%0A        %7D
 %0A   

Implement more interval functionality