/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.collection.map;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Map;

/**
 * Implementacja dwu-wymiarowej mapy zoptymalizowana dla typów {@code Enum}.
 * @param <KX> 
 * @param <KY> 
 * @param <V> 
 * @author ranides
 * @todo (migration) assira
 */
@SuppressWarnings("PMD.ShortVar")
public final class CrossEnumMap<KX extends Enum<KX>, KY extends Enum<KY>, V> implements CrossMap<KX, KY, V> {
    
    private final EnumMap<KX, EnumMap<KY, V>> data;

    /**
     * Tworzy pustą mapę, która jako współrzędnych używa podanych typów {@code enum}.
     * @param xclass
     * @param yclass
     */
    public CrossEnumMap(Class<KX> xclass, Class<KY> yclass) {
        this.data = new EnumMap<>(xclass);
        for(KX x : xclass.getEnumConstants()) {
            this.data.put(x, new EnumMap<KY, V>(yclass));
        }
    }

    @Override
    public boolean contains(KX x, KY y) {
        return data.get(x).containsKey(y);
    }

    @Override
    public V put(KX x, KY y, V value) {
        V prev = data.get(x).get(y);
        data.get(x).put(y, value);
        return prev;
    }

    @Override
    public V remove(KX x, KY y) {
        V prev = data.get(x).get(y);
        data.get(x).remove(y);
        return prev;
    }

    @Override
    public V get(KX x, KY y) {
        return data.get(x).get(y);
    }

    @Override
    public int size() {
        int size = 0;
        for(EnumMap<KY,V> map : data.values()) { size += map.size(); }
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size() == 0;
    }

    @Override
    public void clear() {
        for(EnumMap<KY,V> map : data.values()) { map.clear(); }
    }

    @Override
    public MultiMap<KX, KY> reduceZ() {
        MultiMap<KX, KY> result = new MultiHashMap<>();
        for(Map.Entry<KX, ? extends Map<KY,V>> entry : data.entrySet() ) {
            result.put(entry.getKey(), new ArrayList<>(entry.getValue().keySet()) );
        }
        return result;
    }

    @Override
    public MultiMap<KX, V> reduceY() {
        MultiMap<KX, V> result = new MultiHashMap<>();
        for( Map.Entry<KX, ? extends Map<KY,V>> item : data.entrySet() ) {
            result.put( item.getKey(), new ArrayList<>(item.getValue().values()) );
        }
        return result;
    }
    
}
