package common.properties;

import java.util.Collection;
import java.util.Map;
import java.util.function.Predicate;

import common.collect.Filter;
import common.collect.ImmutableSet;
import common.collect.Lists;
import common.collect.Maps;
import common.util.Identifyable;
import common.util.Predicates;

public class PropertyEnum<T extends Enum<T> & Identifyable> extends PropertyHelper<T>
{
    private final ImmutableSet<T> allowedValues;
    private final Map<String, T> nameToValue = Maps.<String, T>newHashMap();

    protected PropertyEnum(String name, Class<T> valueClass, Collection<T> allowedValues)
    {
        super(name, valueClass);
        this.allowedValues = ImmutableSet.copyOf(allowedValues);

        for (T t : allowedValues)
        {
            String s = ((Identifyable)t).getName();

            if (this.nameToValue.containsKey(s))
            {
                throw new IllegalArgumentException("Multiple values have the same name \'" + s + "\'");
            }

            this.nameToValue.put(s, t);
        }
    }

    public Collection<T> getAllowedValues()
    {
        return this.allowedValues;
    }

    /**
     * Get the name for the given value.
     */
    public String getName(T value)
    {
        return ((Identifyable)value).getName();
    }

    public static <T extends Enum<T> & Identifyable> PropertyEnum<T> create(String name, Class<T> clazz)
    {
        return create(name, clazz, Predicates.<T>alwaysTrue());
    }

    public static <T extends Enum<T> & Identifyable> PropertyEnum<T> create(String name, Class<T> clazz, Predicate<T> filter)
    {
        return create(name, clazz, Filter.<T>filter(Lists.newArrayList(clazz.getEnumConstants()), filter));
    }

    public static <T extends Enum<T> & Identifyable> PropertyEnum<T> create(String name, Class<T> clazz, T... values)
    {
        return create(name, clazz, Lists.newArrayList(values));
    }

    public static <T extends Enum<T> & Identifyable> PropertyEnum<T> create(String name, Class<T> clazz, Collection<T> values)
    {
        return new PropertyEnum(name, clazz, values);
    }
}
