package li.strolch.metrics;

import static li.strolch.model.StrolchModelConstants.STATE_VALUES;

import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;

import li.strolch.model.Resource;
import li.strolch.model.timedstate.BooleanTimedState;
import li.strolch.model.timedstate.IntegerListTimedState;
import li.strolch.model.timedstate.IntegerTimedState;
import li.strolch.model.timevalue.ITimeValue;
import li.strolch.model.timevalue.ITimeVariable;
import li.strolch.model.timevalue.impl.BooleanValue;
import li.strolch.model.timevalue.impl.IntegerListValue;
import li.strolch.model.timevalue.impl.IntegerValue;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.policy.StrolchPolicy;

public abstract class MetricPolicy extends StrolchPolicy {

	public MetricPolicy(StrolchTransaction tx) {
		super(tx);
	}

	public void incr(Resource metric, int value, ZonedDateTime time) {
		long timeStamp = getTimeStamp(time);
		ITimeVariable<IntegerValue> timeEvolution = getIntegerMetric(metric).getTimeEvolution();
		ITimeValue<IntegerValue> currentValue = timeEvolution.getValueAt(timeStamp);
		if (currentValue != null && currentValue.getTime() == timeStamp) {
			timeEvolution.setValueAt(timeStamp, currentValue.getValue().add(value));
		} else {
			timeEvolution.setValueAt(timeStamp, getValue(value));
		}
		timeEvolution.compact();
		tx().update(metric);
	}

	public void add(Resource metric, int value, ZonedDateTime time) {
		long timeStamp = getTimeStamp(time);
		ITimeVariable<IntegerListValue> timeEvolution = getIntegerListMetric(metric).getTimeEvolution();
		ITimeValue<IntegerListValue> currentValue = timeEvolution.getValueAt(timeStamp);
		if (currentValue != null && currentValue.getTime() == timeStamp) {
			timeEvolution.setValueAt(timeStamp, currentValue.getValue().add(List.of(value)));
		} else {
			timeEvolution.setValueAt(timeStamp, getValueList(value));
		}
		timeEvolution.compact();
		tx().update(metric);
	}

	public void set(Resource metric, boolean value, ZonedDateTime time) {
		long timeStamp = getTimeStamp(time);
		ITimeVariable<BooleanValue> timeEvolution = getBooleanMetric(metric).getTimeEvolution();
		ITimeValue<BooleanValue> currentValue = timeEvolution.getValueAt(timeStamp);
		if (currentValue != null && currentValue.getTime() == timeStamp) {
			timeEvolution.setValueAt(timeStamp, currentValue.getValue().add((value)));
		} else {
			timeEvolution.setValueAt(timeStamp, getValue(value));
		} timeEvolution.compact();
		tx().update(metric);
	}

	protected IntegerTimedState getIntegerMetric(Resource metric) {
		IntegerTimedState metricT = metric.getTimedState(STATE_VALUES, false);
		if (metricT == null) {
			metricT = new IntegerTimedState(STATE_VALUES, "Values");
			metric.addTimedState(metricT);
		}

		return metricT;
	}

	protected BooleanTimedState getBooleanMetric(Resource metric) {
		BooleanTimedState metricT = metric.getTimedState(STATE_VALUES, false);
		if (metricT == null) {
			metricT = new BooleanTimedState(STATE_VALUES, "Values");
			metric.addTimedState(metricT);
		}

		return metricT;
	}

	protected IntegerListTimedState getIntegerListMetric(Resource metric) {
		IntegerListTimedState metricT = metric.getTimedState(STATE_VALUES, false);
		if (metricT == null) {
			metricT = new IntegerListTimedState(STATE_VALUES, "Values");
			metric.addTimedState(metricT);
		}

		return metricT;
	}

	protected BooleanValue getValue(boolean value) {
		return new BooleanValue(value);
	}

	protected IntegerValue getValue(int value) {
		return new IntegerValue(value);
	}

	protected IntegerListValue getValueList(int value) {
		return new IntegerListValue(value);
	}

	protected long getTimeStamp(ZonedDateTime time) {
		return time.truncatedTo(ChronoUnit.HOURS).toInstant().toEpochMilli();
	}

	protected int getDurationSeconds(Duration duration) {
		return (int) duration.abs().toSeconds();
	}
}

@@ -947,32 +947,175 @@
 ateTime time) %7B%0A
+%09%09incr(metric, value, time, ChronoUnit.HOURS);%0A%09%7D%0A%0A%09public void incr(Resource metric, int value, ZonedDateTime time, ChronoUnit granularity) %7B%0A
 %09%09long timeStamp
@@ -1126,32 +1126,45 @@
 etTimeStamp(time
+, granularity
 );%0A%09%09ITimeVariab
@@ -1541,35 +1541,8 @@
 %09%09%7D%0A
-%09%09timeEvolution.compact();%0A
 %09%09tx
@@ -1623,32 +1623,173 @@
 ateTime time) %7B%0A
+%09%09add(metric, value, time, ChronoUnit.HOURS);%0A%09%7D%0A%0A%09public void add(Resource metric, int value, ZonedDateTime time, ChronoUnit granularity) %7B%0A
 %09%09long timeStamp
@@ -1800,32 +1800,45 @@
 etTimeStamp(time
+, granularity
 );%0A%09%09ITimeVariab
@@ -2240,35 +2240,8 @@
 %09%09%7D%0A
-%09%09timeEvolution.compact();%0A
 %09%09tx
@@ -2340,133 +2340,200 @@
 %7B%0A%09%09
-long timeStamp = getTimeStamp(time);%0A%09%09ITimeVariable%3CB
+set(metric, value, time, ChronoUnit.HOURS);%0A%09%7D%0A%0A%09public void set(Resource metric, b
 oolean
-V
+ v
 alue
-%3E timeEvolution = getBooleanMetric(metric).getTimeEvolution(
+, ZonedDateTime time, ChronoUnit granularity) %7B%0A%09%09long timeStamp = getTimeStamp(time, granularity
 );%0A%09
@@ -2540,18 +2540,21 @@
 %09ITimeVa
+riab
 l
-u
 e%3CBoolea
@@ -2565,219 +2565,69 @@
 ue%3E 
-currentValue = timeEvolution.getValueAt(timeStamp);%0A%09%09if (currentValue != null && currentValue.getTime() == timeStamp) %7B%0A%09%09%09timeEvolution.setValueAt(timeStamp, currentValue.getValue().add((value)));%0A%09%09%7D else %7B%0A%09
+timeEvolution = getBooleanMetric(metric).getTimeEvolution();%0A
 %09%09ti
@@ -2682,37 +2682,8 @@
 ));%0A
-%09%09%7D timeEvolution.compact();%0A
 %09%09tx
@@ -3884,24 +3884,48 @@
 ateTime time
+, ChronoUnit granularity
 ) %7B%0A%09%09return
@@ -3942,32 +3942,27 @@
 catedTo(
-ChronoUnit.HOURS
+granularity
 ).toInst

Allow overriding time granularity in MetricPolicy