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

import java.io.Serializable;
import java.util.Set;
import java.util.function.IntSupplier;
import java.util.function.ToIntFunction;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class LazyLookup<K> extends ALookup<K> {
    
    private final Lookup<K> data;
    
    private ToIntFunction<K> function;
    
    public LazyLookup(Lookup<K> data, IntSupplier supplier) {
        this(data, (ToIntFunction<K> & Serializable) $0 -> supplier.getAsInt());
    }

    public LazyLookup(Lookup<K> data, ToIntFunction<K> function) {
        this.data = data;
        this.function = function;
    }

    @Override
    public Set<LookupEntry<K>> fastEntrySet() {
        return data.fastEntrySet();
    }

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

    @Override
    public boolean containsKey(Object key) {
        return data.containsKey(key);
    }

    @Override
    public boolean containsValue(int value) {
        return data.containsValue(value);
    }
    
    public Integer getIfPresent(Object key) {
        return getIntIfPresent(key);
    }
    
    @SuppressWarnings("element-type-mismatch")
    public int getIntIfPresent(Object key) {
        return data.containsKey(key) ? data.get(key) : defaultReturnValue();
    }

    @Override
    @SuppressWarnings(value = {"element-type-mismatch", "unchecked"})
    public int getInt(Object key) {
        if (!data.containsKey(key)) {
            int ret = function.applyAsInt((K)key);
            data.put((K) key, ret);
            return ret;
        } else {
            return data.getInt(key);
        }
    }

    @Override
    public Integer get(Object key) {
        return getInt(key);
    }

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

    @Override
    @SuppressWarnings(value = {"element-type-mismatch", "unchecked"})
    public int removeInt(Object key) {
        if (!data.containsKey(key)) {
            return function.applyAsInt((K)key);
        } else {
            return data.removeInt(key);
        }
    }
    
    @Override
    public boolean remove(Object key, Object value) {
        return data.remove(key, value);
    }

    @Override
    public int put(K key, int value) {
        if (!data.containsKey(key)) {
            return data.put(key, value);
        } else {
            data.put(key, value);
            return function.applyAsInt(key);
        }
    }

    @Override
    public int defaultReturnValue() {
        return function.applyAsInt(null);
    }

    @Override
    public void defaultReturnValue(int rv) {
        function = (ToIntFunction<K> & Serializable)$0 -> rv;
    }
    
}
