package de.renew.util;

import java.io.Serial;
import java.util.HashMap;


/**
 * This is a Map where the mapping is bijective. <br>
 *
 * You could use both values as keys and/or as values. <br>
 *
 *
 *
 * @author Benjamin Schleinzer <a href="mailto:0schlein@informatik.uni-hamburg.de">0schlein@informatik.uni-hamburg.de</a>
 *
 * @version 1.0
 */
public class BijectiveMap<K, V> extends HashMap<K, V> {

    /**
     *
     */
    @Serial
    private static final long serialVersionUID = 1173959856549564757L;

    /**
     * Logger for this class
     */
    public static org.apache.log4j.Logger logger =
        org.apache.log4j.Logger.getLogger(BijectiveMap.class);

    /**
     * This set will be checked when searching for a value by key.
     */
    private final HashMap<V, K> _valueKeyMap;

    /**
     * generate a new mapping
     *
     */
    public BijectiveMap() {
        _valueKeyMap = new HashMap<>();
    }

    /**
     *
     * {@link java.util.Map#put(Object, Object)}
     * @param key which refers to value
     * @param value which refers to key
     * @return <code>null</code>
     */
    @Override
    public V put(K key, V value) {
        V returnValue = super.put(key, value);
        if (returnValue != null) {
            _valueKeyMap.remove(returnValue);
        }
        _valueKeyMap.put(value, key);
        return returnValue;
    }

    /**
     *
     * {@link java.util.Map#put(Object, Object)}
     * @param value a value for which to retrieve its associated key
     * @return <code>null</code>
     */
    public K getKey(V value) {
        return _valueKeyMap.get(value);
    }

    /**
     * remove the object and the referred object from the map. <br>
     *
     * if a map set is set, this operation fails if the set operation fails
     * <p>
     * {@link java.util.Map#remove(Object)}
     * @param key the object to remove
     * @return the referred deleted object
     */
    @Override
    public V remove(Object key) {
        V returnValue = super.remove(key);
        if (returnValue != null) {
            _valueKeyMap.remove(returnValue);
        }
        return returnValue;
    }

    /**
     * Remove the key for a given value from the bijective mapping
     * 
     * @param value the value for which to remove its mapped key
     * @return the key associated with the value. Returns null if null was the associated key, or no key was associated with the value at all.
     */
    public K removeKey(Object value) {
        K returnValue = _valueKeyMap.remove(value);
        if (returnValue != null) {
            super.remove(returnValue);
        }
        return returnValue;
    }

    /**
     * removes all elements from this map <br>
     *
     * removes the elements from both list <br>
     *
     * {@link java.util.Map#clear()}
     */
    @Override
    public void clear() {
        super.clear();
        _valueKeyMap.clear();
    }


    /**
     * returns true if there is the specified value in this map <br>
     *
     * Same as containsKey(...)
     * <p>
     * {@link java.util.Map#containsValue(Object)}
     * @param arg0 the value to be tested
     * @return true if this map contains arg0
     */
    @Override
    public boolean containsValue(Object arg0) {
        return _valueKeyMap.containsKey(arg0);
    }
}