package fault.metrics;

import fault.utils.SystemTime;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceArray;

/**
 * Created by timbrooks on 6/1/15.
 */
public class MultiWriterActionMetrics {

    private final AtomicReferenceArray<Second> metrics;
    private final SystemTime systemTime;
    private final AtomicLong advanceSlotTimeInMillis;
    private final int totalSlots;
    private AtomicInteger slotNumber = new AtomicInteger(0);

    public MultiWriterActionMetrics(int secondsToTrack) {
        this(secondsToTrack, new SystemTime());
    }

    public MultiWriterActionMetrics(int secondsToTrack, SystemTime systemTime) {
        this.totalSlots = secondsToTrack;
        this.metrics = new AtomicReferenceArray<>(secondsToTrack);
        this.systemTime = systemTime;
        this.advanceSlotTimeInMillis = new AtomicLong(systemTime.currentTimeMillis() + 1000L);
    }

    public void incrementMetric(Metric metric) {
        int currentSlotNumber = slotNumber.get();
        int slotsToAdvance = slotsToAdvance();


    }

    public int getFailuresForTimePeriod(Metric metric, long milliseconds) {
        return 0;
    }

    // Unsure if this is correct
    private int advanceToCurrentSlot(int currentSlotNumber, int slotsToAdvance) {
        if (slotsToAdvance != 0) {
            int newSlot = slotsToAdvance + currentSlotNumber;
            int adjustedSlot = newSlot % totalSlots;
            if (slotNumber.compareAndSet(currentSlotNumber, adjustedSlot)) {
                for (int i = currentSlotNumber + 1; i <= newSlot; ++i) {
                    if (i < totalSlots) {
                        this.metrics.set(i, null);
                    } else {
                        this.metrics.set(i - totalSlots, null);
                    }
                }
                this.advanceSlotTimeInMillis.set(advanceSlotTimeInMillis.get() + (1000 * slotsToAdvance));
                return adjustedSlot;
            } else {
                return -1;
            }
        }
        return currentSlotNumber;
    }

    private int slotsToAdvance() {
        long currentTimestamp = systemTime.currentTimeMillis();
        if (currentTimestamp < advanceSlotTimeInMillis.get()) {
            return 0;
        }

        long advanceSlotTimeInMillis = this.advanceSlotTimeInMillis.get();
        return 1 + (int) ((currentTimestamp - advanceSlotTimeInMillis) / 1000);
    }

}

@@ -1043,74 +1043,169 @@
 int 
-currentSlotNumber = slotNumber.get();%0A        int slotsToAdvance =
+newSlot = -1;%0A        while (newSlot == -1) %7B%0A            int currentSlotNumber = slotNumber.get();%0A            newSlot = advanceToCurrentSlot(currentSlotNumber,
  slo
@@ -1217,19 +1217,82 @@
 dvance()
+)
 ;%0A
-%0A
+        %7D%0A        metrics.get(newSlot).incrementMetric(metric);
 %0A    %7D%0A%0A
@@ -1363,24 +1363,26 @@
 iseconds) %7B%0A
+%0A%0A
         retu
@@ -1540,16 +1540,71 @@
 nce 
-!
+=
 = 0) %7B%0A
+            return currentSlotNumber;%0A        %7D else %7B%0A
     
@@ -1787,24 +1787,246 @@
 tedSlot)) %7B%0A
+                // This presents a lot of races with changing the slotNumber. Mabye time should be the slotnumber?%0A                this.advanceSlotTimeInMillis.set(advanceSlotTimeInMillis.get() + (1000 * slotsToAdvance));%0A
             
@@ -2316,115 +2316,8 @@
   %7D%0A
-                this.advanceSlotTimeInMillis.set(advanceSlotTimeInMillis.get() + (1000 * slotsToAdvance));%0A
     
@@ -2403,32 +2403,32 @@
 ;%0A            %7D%0A
+
         %7D%0A      
@@ -2425,42 +2425,8 @@
   %7D%0A
-        return currentSlotNumber;%0A
     

Work on multiwriter metrics