package net.lecousin.framework.math;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import net.lecousin.framework.plugins.ExtensionPoint;
import net.lecousin.framework.plugins.Plugin;

/** Represent an integer unit. */
public interface IntegerUnit {

	/** Annotation to specify in which unit a field is expressed. */
	@Retention(RetentionPolicy.RUNTIME)
	@Target({ElementType.FIELD})
	public @interface Unit {
		/** Integer unit. */
		public Class<? extends IntegerUnit> value();
	}
	
	/** Error when converting from a unit to another. */
	public static class UnitConversionException extends Exception {
		
		private static final long serialVersionUID = -4651595394407140514L;

		/** Constructor. */
		public UnitConversionException(Class<? extends IntegerUnit> from, Class<? extends IntegerUnit> to, long value) {
			super("Cannot convert value " + value + " from " + from.getSimpleName() + " to " + to.getSimpleName());
		}
		
	}
	
	/** Convert a value. */
	public static interface Converter extends Plugin {
		/** Return true if the conversion is supported. */
		boolean supportConversion(Class<? extends IntegerUnit> from, Class<? extends IntegerUnit> to);
		
		/** Convert a value. */
		long convert(long value, Class<? extends IntegerUnit> from, Class<? extends IntegerUnit> to) throws UnitConversionException;
	}
	
	/** Extension point from converters. */
	public static class ConverterRegistry implements ExtensionPoint<Converter> {

		/** Constructor. */
		@SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
		public ConverterRegistry() {
			instance = this;
			converters.add(new TimeUnit.Converter());
		}

		private static ConverterRegistry instance;
		private ArrayList<Converter> converters = new ArrayList<>();
		
		@Override
		public Class<Converter> getPluginClass() { return Converter.class; }

		@Override
		public void addPlugin(Converter plugin) { converters.add(plugin); }

		@Override
		public void allPluginsLoaded() {
			// nothing to do
		}
		
		/** Conversion. */
		public static long convert(long value, Class<? extends IntegerUnit> from, Class<? extends IntegerUnit> to)
		throws UnitConversionException {
			for (Converter c : instance.converters)
				if (c.supportConversion(from, to))
					return c.convert(value, from, to);
			throw new UnitConversionException(from, to, value);
		}
		
	}
	
}

@@ -226,16 +226,66 @@
 ayList;%0D
+%0Aimport java.util.HashMap;%0D%0Aimport java.util.Map;%0D
 %0A%0D%0Aimpor
@@ -337,24 +337,289 @@
 arnings;%0D%0A%0D%0A
+import net.lecousin.framework.math.TimeUnit.Day;%0D%0Aimport net.lecousin.framework.math.TimeUnit.Hour;%0D%0Aimport net.lecousin.framework.math.TimeUnit.Millisecond;%0D%0Aimport net.lecousin.framework.math.TimeUnit.Minute;%0D%0Aimport net.lecousin.framework.math.TimeUnit.Second;%0D%0A
 import net.l
@@ -2928,11 +2928,1082 @@
 %0A%09%7D%0D%0A%09%0D%0A
+%09public static class ParserRegistry %7B%0D%0A%09%09%0D%0A%09%09private static final Map%3CString, Class%3C? extends IntegerUnit%3E%3E units = new HashMap%3C%3E();%0D%0A%09%09%0D%0A%09%09public static void register(String text, Class%3C? extends IntegerUnit%3E unit) %7B%0D%0A%09%09%09units.put(text, unit);%0D%0A%09%09%7D%0D%0A%09%09%0D%0A%09%09public static Class%3C? extends IntegerUnit%3E get(String text) %7B%0D%0A%09%09%09return units.get(text);%0D%0A%09%09%7D%0D%0A%09%09%0D%0A%09%09static %7B%0D%0A%09%09%09ParserRegistry.register(%22ms%22, Millisecond.class);%0D%0A%09%09%09ParserRegistry.register(%22millisecond%22, Millisecond.class);%0D%0A%09%09%09ParserRegistry.register(%22milliseconds%22, Millisecond.class);%0D%0A%09%09%09ParserRegistry.register(%22millis%22, Millisecond.class);%0D%0A%09%09%09ParserRegistry.register(%22second%22, Second.class);%0D%0A%09%09%09ParserRegistry.register(%22seconds%22, Second.class);%0D%0A%09%09%09ParserRegistry.register(%22minute%22, Minute.class);%0D%0A%09%09%09ParserRegistry.register(%22minutes%22, Minute.class);%0D%0A%09%09%09ParserRegistry.register(%22h%22, Hour.class);%0D%0A%09%09%09ParserRegistry.register(%22hour%22, Hour.class);%0D%0A%09%09%09ParserRegistry.register(%22hours%22, Hour.class);%0D%0A%09%09%09ParserRegistry.register(%22day%22, Day.class);%0D%0A%09%09%09ParserRegistry.register(%22days%22, Day.class);%0D%0A%09%09%7D%0D%0A%09%7D%0D%0A
 %7D%0D%0A

add IntegerUnit.Parser to get a unit from a text