/*
 *  @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.HashMap;
import java.util.Map.Entry;

/**
 * Implementacja dwu-wymiarowej mapy używająca wewnętrznie jednej mapy, 
 * zawierającej klucze złożone. 
 * @param <KX> 
 * @param <KY> 
 * @param <V> 
 * @author ranides
 * @todo (migration) assira
 */
@SuppressWarnings("unchecked")
public class CrossCompositeMap<KX, KY, V> implements CrossMap<KX, KY, V>  {

    private final HashMap<CKey, V> map;

    /**
     * Constructs an empty <tt>CrossCompositeMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public CrossCompositeMap() {
        map = new HashMap<>();
    }

    /**
     * Constructs an empty <tt>CrossCompositeMap</tt> with the specified initial
     * capacity and load factor.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public CrossCompositeMap(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    @Override
    public boolean contains(KX xKey, KY yKey) {
        return map.containsKey( new CKey(xKey, yKey) );
    }

    @Override
    public V put(KX xKey, KY yKey, V value) {
        return map.put(new CKey(xKey, yKey), value);
    }

    @Override
    public V remove(KX xKey, KY yKey) {
        return map.remove(new CKey(xKey, yKey));
    }

    @Override
    public V get(KX xKey, KY yKey) {
        return map.get(new CKey(xKey, yKey));
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }

    @Override
    public void clear() {
        map.clear();
    }

    @Override
    public MultiMap<KX, KY> reduceZ() {
        MultiMap<KX, KY> result = new MultiHashMap<>();
        for(CKey item : map.keySet() ) {
            result.putItem(item.xKey, item.yKey);

        }
        return result;
    }

    @Override
    public MultiMap<KX, V> reduceY() {
        MultiMap<KX, V> result = new MultiHashMap<>();
        for( Entry<CKey, V> item : map.entrySet() ) {
            result.get( item.getKey().xKey ).add( item.getValue() );
        }
        return result;
    }

/* ************************************************************************** */

    private final class CKey {
        private final KX xKey;
        private final KY yKey;

        public CKey(KX xKey, KY yKey) {
            this.xKey = xKey;
            this.yKey = yKey;
        }

        @Override
        public boolean equals(Object object) {
            if (object==null || object.getClass().equals(CKey.class) ) {
                return false;
            }
            final CKey value =(CKey) object;
            return value.xKey.equals(xKey) && value.yKey.equals(yKey);
        }

        @Override
        public int hashCode() {
            // duża liczba pierwsza
            return 1073676287 * xKey.hashCode() + yKey.hashCode();
        }

    }

}
