package net.anotheria.moskito.core.counter;

import net.anotheria.moskito.core.predefined.Constants;
import net.anotheria.moskito.core.producers.AbstractStats;
import net.anotheria.moskito.core.stats.Interval;
import net.anotheria.moskito.core.stats.StatValue;
import net.anotheria.moskito.core.stats.TimeUnit;
import net.anotheria.moskito.core.stats.impl.StatValueFactory;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Generic counter implementation.
 *
 * @author lrosenberg
 * @since 16.11.12 23:11
 */
public abstract class GenericCounterStats extends AbstractStats{

	/**
	 * Map with statvalues for faster access.
	 */
	private HashMap<String, StatValue> values;

	/**
	 * Available value names.
	 */
	private final List<String> valueNames;

	/**
	 * Main constructor, counter names as list
	 * @param name name of the stats object
	 * @param intervals intervals
	 * @param counters list of counter names
	 */
	protected GenericCounterStats(String name, Interval[] intervals, List<String> counters) {
		// Super call
		super(name);

		// Make sure that there are some counters and that they aren't empty
		if (counters != null && !counters.isEmpty()) {
			// Create new map to store counterName - statValue pairs
			this.values = new HashMap<>(counters.size());

			// For each counter
			for (String counter : counters) {
				// Create new stat value
				StatValue statValue = StatValueFactory.createStatValue(0L, counter, intervals);

				// Store it in the map
				this.values.put(counter, statValue);

				// Register it in the parent class
				super.addStatValues(statValue);
			}

			// Copy the counter names as an unmodifiable list
			this.valueNames = Collections.unmodifiableList(counters);
		}

		// If counters are null or empty - throw an exception
		else {
			throw new IllegalArgumentException("Illegal counters=" + counters);
		}
	}

	/**
	 * Main constructor, counter names as array
	 * @param name name of the stats object
	 * @param intervals intervals
	 * @param counters array of counter names
	 */
	protected GenericCounterStats(String name, Interval[] intervals, String ... counters) {
		this(name, intervals, Arrays.asList(counters));
	}

	/**
	 * Shortened constructor, counter names as list
	 * We assume that we use default intervals obtained by {@link Constants#getDefaultIntervals()}
	 * @param name name of the stats object
	 * @param counters list of counter names
	 */
	protected GenericCounterStats(String name, List<String> counters) {
		this(name, Constants.getDefaultIntervals(), counters);
	}

	/**
	 * Shortened constructor, counter names as array
	 * We assume that we use default intervals obtained by {@link Constants#getDefaultIntervals()}
	 * @param name name of the stats object
	 * @param counters array of counter names
	 */
	protected GenericCounterStats(String name, String ... counters){
		this(name, Constants.getDefaultIntervals(), counters);
	}

	/**
	 * Increment counter by 1
	 * @param counterName counter
	 */
	public void inc(String counterName){
		values.get(counterName).increase();
	}

	/**
	 * Decrement counter by 1
	 * @param counterName counter
	 */
	public void dec(String counterName) {
		values.get(counterName).decrease();
	}

	/**
	 * Increase counter by a given value
	 * @param counterName counter
	 * @param value increment value
	 */
	public void incBy(String counterName, long value){
		values.get(counterName).increaseByLong(value);
	}

	/**
	 * Decrease counter by a given value
	 * @param counterName counter
	 * @param value decrement value
	 */
	public void decBy(String counterName, long value) {
		values.get(counterName).decreaseByLong(value);
	}

	public long get(String counterName, String intervalName){
		return values.get(counterName).getValueAsLong(intervalName);
	}

	public void set(String counterName, int value) {
		values.get(counterName).setValueAsInt(value);
	}

	@Override
	public String toStatsString(String aIntervalName, TimeUnit unit) {
		// Create new string builder
		StringBuilder s = new StringBuilder();

		// Append stat name
		s.append(getName()).append(' ');

		// For each counter name
		for (String name : valueNames) {
			// Extract stat value and then long value for this counter
			StatValue statValue = values.get(name);
			long longValue = statValue.getValueAsLong(aIntervalName);

			// Append them to the string builder
			s.append(name).append(": ").append(longValue);
		}

		// Build the resulting string
		return s.toString();
	}

	@Override
	public String toString(){
		return getName()+ ' ' +values.values();
	}

	public abstract String describeForWebUI();

	@Override
	public String getValueByNameAsString(String valueName, String intervalName, TimeUnit timeUnit) {
		// If counter name is null or empty
		if (valueName == null || valueName.isEmpty())
			throw new AssertionError("Value name can not be empty");
		valueName = valueName.toLowerCase();

		// Get the value of this counter and convert it to string
		return String.valueOf(get(valueName, intervalName));
	}

	@Override
	public List<String> getAvailableValueNames() {
		return valueNames;
	}

	/**
	 * This method is no longer supported, please use {@link GenericCounterStats#getAvailableValueNames()}
	 * @return a set of possible names
	 */
	@Deprecated
	public Set<String> getPossibleNames() {
		return new LinkedHashSet<>(this.getAvailableValueNames());
	}
}