/*
 * Copyright 2016 MovingBlocks
 *
 * 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 org.terasology.rendering.nui.layers.ingame.metrics;


import com.google.api.client.repackaged.com.google.common.base.Preconditions;
import org.terasology.entitySystem.systems.BaseComponentSystem;
import org.terasology.entitySystem.systems.RegisterSystem;
import org.terasology.registry.Share;

import java.util.ArrayList;


/**
 * Manages an ordered set of MetricsMode instances.
 *
 *
 * A number of default metrics modes is instantiated in the initialize() method and becomes immediately available by
 * iterating through the set repeatedly invoking the toggle() method. The getCurrentMode() method can also be used to
 * obtain the mode currently pointed at. Further modes can be added to the set via the register method, while registered
 * modes can be removed via the unregister method. A convenience method unregisterAll() removes all registered modes at
 * once. MetricsModes are currently used by the DebugOverlay instance, displaying runtime statistics on screen.
 * unregister and unregisterAll functions.
 */
@RegisterSystem
@Share(DebugMetricsSystem.class)
public class DebugMetricsSystem extends BaseComponentSystem {

    private final MetricsMode defaultMode = new NullMetricsMode();
    private CircularToggleSet<MetricsMode> modes;
    private MetricsMode currentMode;

    @Override
    public void initialise() {
        modes = new CircularToggleSet<>();

        register(defaultMode);
        register(new RunningMeansMode());
        register(new SpikesMode());
        register(new AllocationsMode());
        register(new RunningThreadsMode());
        register(new WorldRendererMode());
        register(new RenderingExecTimeMeansMode("Rendering - Execution Time: Running Means - Sorted Alphabetically"));
        currentMode = defaultMode;
    }


    /**
     * Adds a MetricsMode instance to the set. Use the toggle() and getCurrentMode() methods to iterate over the set and
     * obtain the MetricsMode instances.
     * @param mode a MetricsMode instance
     */
    public boolean register(MetricsMode mode) {
        Preconditions.checkNotNull(mode, "Illegal argument passed, mode is null.");
        return modes.add(mode);
    }

    /**
     * Returns current mode, initializes a default {@link NullMetricsMode}, if currentMode is null
     * @return current mode
     */
    public MetricsMode getCurrentMode() {
        return currentMode;
    }

    /**
     * Iterates through the set of registered MetricsMode and returns the first instance whose method isAvailable()
     * returns true. Notice that this could be the MetricsMode that was current at the time this method was invoked.
     * @returns a MetricsMode instance
     */
    public MetricsMode toggle() {
        do {
            currentMode = modes.toggle();
        } while (!currentMode.isAvailable());

        return currentMode;
    }


    /**
     * Removes from the set the MetricsMode instance provided as input.
     *
     * If the MetricsMode instance is the mode currently pointed at by the iterator, toggles the iterator forward.
     *
     * @return True if the MetricsMode instance was in the set. False otherwise.
     */
    public boolean unregister(MetricsMode mode) {
        Preconditions.checkNotNull(mode, "Illegal argument passed, mode is null.");
        if (mode == defaultMode) {
            throw new IllegalArgumentException("Removing defaultMode is not allowed.");
        }

        if (mode == currentMode) {
            toggle();
        }

        return modes.remove(mode);
    }

    /**
     * Removes all registered metrics modes except an instance of NullMetricsMode, which is guaranteed to be always available.
     */
    public void unregisterAll() {
        modes.clear();
        register(defaultMode);
        currentMode = defaultMode;
    }

    /**
     * Returns number of registered metrics modes
     */
    public int getNumberOfModes() {
        return modes.size();
    }


    private class CircularToggleSet<T> {
        ArrayList<T> container = new ArrayList<>();
        int cursor;

        public int size() {
            return container.size();
        }

        public void clear() {
            cursor = 0;
            container.clear();
        }

        public boolean remove(T mode) {
            return container.remove(mode);
        }

        public T toggle() {
            cursor = (cursor + 1) % size();
            return container.get(cursor);
        }

        public boolean add(T mode) {
            if (!container.contains(mode)) {
                return container.add(mode);
            }
            return false;
        }
    }
}

@@ -1810,32 +1810,24 @@
 private 
-CircularToggleSe
+ArrayLis
 t%3CMetric
@@ -1876,16 +1876,40 @@
 entMode;
+%0A    private int cursor;
 %0A%0A    @O
@@ -1967,32 +1967,24 @@
 s = new 
-CircularToggleSe
+ArrayLis
 t%3C%3E();%0A%0A
@@ -2738,39 +2738,134 @@
 %22);%0A
-        return modes.add(mode);
+%0A        if (!modes.contains(mode)) %7B%0A            return modes.add(mode);%0A        %7D else %7B%0A            return false;%0A        %7D
 %0A   
@@ -2990,24 +2990,28 @@
 @return 
+the 
 current 
 mode%0A   
@@ -3002,19 +3002,35 @@
 current 
-mod
+MetricsMode instanc
 e%0A     *
@@ -3443,16 +3443,66 @@
    do %7B%0A
+            cursor = (cursor + 1) %25 modes.size();%0A
         
@@ -3525,23 +3525,26 @@
 = modes.
-toggle(
+get(cursor
 );%0A     
@@ -3822,16 +3822,116 @@
 forward.
+ Unregistering%0A     * defaultMode is not allowed, therefore throws %7B@link IllegalArgumentException%7D.
 %0A     *%0A
@@ -4589,32 +4589,52 @@
  modes.clear();%0A
+        cursor = 0;%0A
         register
@@ -4705,23 +4705,27 @@
 %0A     * 
-R
+@r
 eturn
-s
+ the
  number 
@@ -4743,782 +4743,107 @@
 ed m
-etrics modes%0A     */%0A    public int getNumberOfModes() %7B%0A        return modes.size();%0A    %7D%0A%0A%0A    private class CircularToggleSet%3CT%3E %7B%0A        ArrayList%3CT%3E container = new ArrayList%3C%3E();%0A        int cursor;%0A%0A        public int size() %7B%0A            return container.size();%0A        %7D%0A%0A        public void clear() %7B%0A            cursor = 0;%0A            container.clear();%0A        %7D%0A%0A        public boolean remove(T mode) %7B%0A            return container.remove(mode);%0A        %7D%0A%0A        public T toggle() %7B%0A            cursor = (cursor + 1) %25 size();%0A            return container.get(cursor);%0A        %7D%0A%0A        public boolean add(T mode) %7B%0A            if (!container.contains(mode)) %7B%0A                return container.add(mode);%0A            %7D%0A            return false;%0A        %7D
+odes including defaultMode%0A     */%0A    public int getNumberOfModes() %7B%0A        return modes.size();
 %0A   

Edit DebugMetricsSystem, remove CircularToggleSet