package net.ranides.assira.collection.maps;

import net.ranides.assira.collection.CollectionUtils;
import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.reflection.IClass;
import net.ranides.assira.reflection.IClasses;
import net.ranides.assira.reflection.IHints;
import net.ranides.assira.reflection.impl.FElements;

import java.util.Collection;
import java.util.Map;
import java.util.NoSuchElementException;

public class TypeMap<V> {

    private final MultiMap<IClass<?>, V> content = new OpenMultiMap<>();

    public V match(IClass<?> type) {
        if(content.containsKey(type)) {
            Collection<V> found = content.getAll(type);
            if(found.size() > 1) {
                throw new NoSuchElementException("There is ambiguous match for type: " + type);
            }
            return CollectionUtils.first(found).get();
        }
        throw new NoSuchElementException("There is no match for type: " + type);
    }

    public Collection<V> matchAll(IClass<?> type) {
        return content.getAll(type);
    }

    public boolean matches(IClass<?>  key) {
        return content.containsKey(key);
    }

    public void add(IClass<?> type, V value) {
        content.put(type, value);
        type.parents().forEach(a -> content.put(a, value));
        type.interfaces().forEach(a -> content.put(a, value));
    }

    public void addAll(Map<? extends IClass<?>, ? extends V> values) {
        values.forEach((t,v)-> add(t, v));
    }

    public IClasses types() {
        return FElements.newClasses(IHints.none(), CQuery.from(content.uniqueKeySet()));
    }

}
