/*
 * Copyright (C) 2002-2022 Sebastiano Vigna
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package PACKAGE;

import it.unimi.dsi.fastutil.Function;


/** A type-specific {@link Function}; provides some additional methods that use polymorphism to avoid (un)boxing.
 *
 * <p>Type-specific versions of {@code get()}, {@code put()} and
 * {@code remove()} cannot rely on {@code null} to denote absence of
 * a key. Rather, they return a {@linkplain #defaultReturnValue() default
 * return value}, which is set to 0/false at creation, but can be changed using
 * the {@code defaultReturnValue()} method.
 *
 * <p>For uniformity reasons, even functions returning objects implement the default
 * return value (of course, in this case the default return value is
 * initialized to {@code null}).
 *
 * <p>The default implementation of optional operations just throw an {@link
 * UnsupportedOperationException}, except for the type-specific {@code
 * containsKey()}, which return true. Generic versions of accessors delegate to
 * the corresponding type-specific counterparts following the interface rules.
 *
 * <p><strong>Warning:</strong> to fall in line as much as possible with the
 * {@linkplain java.util.Map standard map interface}, it is required that
 * standard versions of {@code get()}, {@code put()} and
 * {@code remove()} for maps with primitive-type keys or values <em>return
 * {@code null} to denote missing keys </em> rather than wrap the default
 * return value in an object. In case both keys and values are reference
 * types, the default return value must be returned instead, thus violating
 * the {@linkplain java.util.Map standard map interface} when the default
 * return value is not {@code null}.
 *
 * @see Function
 */

@FunctionalInterface
#ifdef JDK_PRIMITIVE_FUNCTION
public interface FUNCTION KEY_VALUE_GENERIC extends Function<KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS>, JDK_PRIMITIVE_FUNCTION KEY_GENERIC VALUE_GENERIC {
#else
public interface FUNCTION KEY_VALUE_GENERIC extends Function<KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS> {
#endif

#ifdef JDK_PRIMITIVE_FUNCTION
#if KEYS_BYTE_CHAR_SHORT_FLOAT
	/** {@inheritDoc}
	 *
	 * @implSpec This default implementation delegates to the type-specific
	 * {@code get()} method after narrowing down the key to the
	 * actual key type, throwing an exception if the given key cannot be
	 * represented in the restricted domain. This is done for interoperability
	 * with the Java 8 function environment. The use of this method is discouraged, as
	 * unexpected errors can occur. Instead, the corresponding classes should be
	 * used (e.g., {@link it.unimi.dsi.fastutil.ints.Int2IntFunction} instead of
	 * {@link it.unimi.dsi.fastutil.shorts.Short2IntFunction}).
	 *
	 * @throws IllegalArgumentException If the given operand is not an element of the key domain.
	 * @since 8.0.0
	 * @deprecated Please use primitive types which do not have to be widened as keys.
	 */
	@Deprecated
#else
	/** {@inheritDoc}
	 * @since 8.0.0
	 */
#endif
	@Override
	default VALUE_GENERIC_TYPE_WIDENED JDK_PRIMITIVE_FUNCTION_APPLY(KEY_GENERIC_TYPE_WIDENED operand) { return GET_VALUE(KEY_NARROWING(operand)); }
#endif

	/** Adds a pair to the map (optional operation).
	 *
	 * @param key the key.
	 * @param value the value.
	 * @return the old value, or the {@linkplain #defaultReturnValue() default return value} if no value was present for the given key.
	 * @see Function#put(Object,Object)
	 */

	default VALUE_GENERIC_TYPE put(final KEY_GENERIC_TYPE key, final VALUE_GENERIC_TYPE value) {
		throw new UnsupportedOperationException();
	}

	/** Returns the value to which the given key is mapped.
	 *
	 * @param key the key.
	 * @return the corresponding value, or the {@linkplain #defaultReturnValue() default return value} if no value was present for the given key.
	 * @see Function#get(Object)
	 */

	VALUE_GENERIC_TYPE GET_VALUE(KEY_TYPE key);

	/** Returns the value associated by this function to the specified key, or give the specified
	 *  value if not present.
	 *
	 * @param key the key.
	 * @param defaultValue the value to return if not present.
	 * @return the corresponding value, or {@code defaultValue} if no value was present for the given key.
	 * @see Function#getOrDefault(Object, Object)
	 * @since 8.5.0
	 */
	default VALUE_GENERIC_TYPE getOrDefault(final KEY_TYPE key, final VALUE_GENERIC_TYPE defaultValue) {
		final VALUE_GENERIC_TYPE v;
		return ((v = GET_VALUE(key)) != defaultReturnValue() || containsKey(key)) ? v : defaultValue;
	}

	/** Removes the mapping with the given key (optional operation).
	 * @param key the key.
	 * @return the old value, or the {@linkplain #defaultReturnValue() default return value} if no value was present for the given key.
	 * @see Function#remove(Object)
	 */
	default VALUE_GENERIC_TYPE REMOVE_VALUE(final KEY_TYPE key) {
		throw new UnsupportedOperationException();
	}

#if KEYS_PRIMITIVE || VALUES_PRIMITIVE

	/** {@inheritDoc}
	 * @deprecated Please use the corresponding type-specific method instead. */
	@Deprecated
	@Override
	default VALUE_GENERIC_CLASS put(final KEY_GENERIC_CLASS key, final VALUE_GENERIC_CLASS value) {
		final KEY_GENERIC_TYPE k = KEY_CLASS2TYPE(key);
		final boolean containsKey = containsKey(k);
		final VALUE_GENERIC_TYPE v = put(k, VALUE_CLASS2TYPE(value));
		return containsKey ? VALUE2OBJ(v) : null;
	}

	/** {@inheritDoc}
	 * @deprecated Please use the corresponding type-specific method instead. */
	@Deprecated
	@Override
	default VALUE_GENERIC_CLASS get(final Object key) {
#if KEYS_PRIMITIVE
		if (key == null) return null;
#endif
		final KEY_TYPE k = KEY_OBJ2TYPE(key);
		final VALUE_GENERIC_TYPE v;
		return ((v = GET_VALUE(k)) != defaultReturnValue() || containsKey(k)) ? VALUE2OBJ(v) : null;
	}

	/** {@inheritDoc}
	 * @deprecated Please use the corresponding type-specific method instead. */
	@Deprecated
	@Override
	default VALUE_GENERIC_CLASS getOrDefault(final Object key, VALUE_GENERIC_CLASS defaultValue) {
#if KEYS_PRIMITIVE
		if (key == null) return defaultValue;
#endif
		final KEY_TYPE k = KEY_OBJ2TYPE(key);
		final VALUE_GENERIC_TYPE v = GET_VALUE(k);
		return (v != defaultReturnValue() || containsKey(k)) ? VALUE2OBJ(v) : defaultValue;
	}

	/** {@inheritDoc}
	 * @deprecated Please use the corresponding type-specific method instead. */
	@Deprecated
	@Override
	default VALUE_GENERIC_CLASS remove(final Object key) {
#if KEYS_PRIMITIVE
		if (key == null) return null;
#endif
		final KEY_TYPE k = KEY_OBJ2TYPE(key);
		return containsKey(k) ? VALUE2OBJ(REMOVE_VALUE(k)) : null;
	}

#if KEYS_PRIMITIVE

	/** Returns true if this function contains a mapping for the specified key.
	 *
	 * <p>Note that for some kind of functions (e.g., hashes) this method will
	 * always return true. In particular, this default implementation always
	 * returns true.
	 *
	 * @param key the key.
	 * @return true if this function associates a value to {@code key}.
	 * @see Function#containsKey(Object)
	 */
	default boolean containsKey(KEY_TYPE key) {
		return true;
	}

	/** {@inheritDoc}
	 * @deprecated Please use the corresponding type-specific method instead. */

	@Deprecated
	@Override
	default boolean containsKey(final Object key) {
		return key == null ? false : containsKey(KEY_OBJ2TYPE(key));
	}

#endif

#endif

	/** Sets the default return value (optional operation).
	 *
	 * This value must be returned by type-specific versions of
	 * {@code get()}, {@code put()} and {@code remove()} to
	 * denote that the map does not contain the specified key. It must be
	 * 0/{@code false}/{@code null} by default.
	 *
	 * @param rv the new default return value.
	 * @see #defaultReturnValue()
	 */

	default void defaultReturnValue(VALUE_GENERIC_TYPE rv) {
		throw new UnsupportedOperationException();
	}


	/** Gets the default return value.
	 *
	 * <p>This default implementation just return the default null value
	 * of the type ({@code null} for objects, 0 for scalars, false for Booleans).
	 *
	 * @return the current default return value.
	 */

	default VALUE_GENERIC_TYPE defaultReturnValue() {
		return VALUE_NULL;
	}

#if KEYS_PRIMITIVE && (KEY_INDEX == VALUE_INDEX)
	static KEY_VALUE_GENERIC FUNCTION identity() {
		return k -> k;
	}
#endif

#define FUNCTION_NAME(K, V) K ## 2 ## V ## Function

#if KEYS_REFERENCE && VALUES_REFERENCE
#define K_T_G <K, T>
#define T_K_G <? super T, ? extends K>
#define V_T_G <? super V, ? extends T>
#define T_V_G <T, V>
#else

#if KEYS_PRIMITIVE
#define K_T_G <T>
#define T_K_G <? super T>
#else
#define K_T_G <K, T>
#define T_K_G <? super T, ? extends K>
#endif

#if VALUES_PRIMITIVE
#define V_T_G <? extends T>
#define T_V_G <T>
#else
#define V_T_G <? super V, ? extends T>
#define T_V_G <T, V>
#endif

#endif

#if KEYS_PRIMITIVE

	/** {@inheritDoc}
	 * @deprecated Please use the corresponding type-specific method instead.
	 */
	@Deprecated
	@Override
	default <T> java.util.function.Function<T, VALUE_GENERIC_CLASS> compose(java.util.function.Function<? super T, ? extends KEY_GENERIC_CLASS> before) {
		return Function.super.compose(before);
	}

#define GET_BEFORE GET_KEY
#else
#define GET_BEFORE get
#endif

#if VALUES_PRIMITIVE

	/** {@inheritDoc}
	 * @deprecated Please use the corresponding type-specific method instead.
	 */
	@Deprecated
	@Override
	default <T> java.util.function.Function<KEY_GENERIC_CLASS, T> andThen(java.util.function.Function<? super VALUE_GENERIC_CLASS, ? extends T> after) {
		return Function.super.andThen(after);
	}

#define GET_AFTER(T_C) get
#else
#define GET_AFTER(T_C) get ## T_C
#endif


#define COMPOSITION(K_C, V_C, T_C, T_P, G_A, G_B, T_G, K_T_G, V_T_G, T_V_G, T_K_G) \
	default T_G PACKAGE.FUNCTION_NAME(K_C, T_C) K_T_G andThen ## T_C(VALUE_PACKAGE.FUNCTION_NAME(V_C, T_C) V_T_G after) { \
		return k -> after.G_A(GET_VALUE(k)); \
	} \
	default T_G it.unimi.dsi.fastutil.T_P.FUNCTION_NAME(T_C, V_C) T_V_G compose ## T_C(it.unimi.dsi.fastutil.T_P.FUNCTION_NAME(T_C, K_C) T_K_G before) { \
		return k -> GET_VALUE(before.G_B(k)); \
	}

COMPOSITION(KEY_TYPE_CAP, VALUE_TYPE_CAP, Byte, bytes, GET_AFTER(Byte), get, , KEY_GENERIC, VALUE_GENERIC, VALUE_GENERIC, KEY_GENERIC)
COMPOSITION(KEY_TYPE_CAP, VALUE_TYPE_CAP, Short, shorts, GET_AFTER(Short), get, , KEY_GENERIC, VALUE_GENERIC, VALUE_GENERIC, KEY_GENERIC)
COMPOSITION(KEY_TYPE_CAP, VALUE_TYPE_CAP, Int, ints, GET_AFTER(Int), get, , KEY_GENERIC, VALUE_GENERIC, VALUE_GENERIC, KEY_GENERIC)
COMPOSITION(KEY_TYPE_CAP, VALUE_TYPE_CAP, Long, longs, GET_AFTER(Long), get, , KEY_GENERIC, VALUE_GENERIC, VALUE_GENERIC, KEY_GENERIC)
COMPOSITION(KEY_TYPE_CAP, VALUE_TYPE_CAP, Char, chars, GET_AFTER(Char), get, , KEY_GENERIC, VALUE_GENERIC, VALUE_GENERIC, KEY_GENERIC)
COMPOSITION(KEY_TYPE_CAP, VALUE_TYPE_CAP, Float, floats, GET_AFTER(Float), get, , KEY_GENERIC, VALUE_GENERIC, VALUE_GENERIC, KEY_GENERIC)
COMPOSITION(KEY_TYPE_CAP, VALUE_TYPE_CAP, Double, doubles, GET_AFTER(Double), get, , KEY_GENERIC, VALUE_GENERIC, VALUE_GENERIC, KEY_GENERIC)
COMPOSITION(KEY_TYPE_CAP, VALUE_TYPE_CAP, Object, objects, get, GET_BEFORE, <T>, K_T_G, V_T_G, T_V_G, T_K_G)
COMPOSITION(KEY_TYPE_CAP, VALUE_TYPE_CAP, Reference, objects, get, GET_BEFORE, <T>, K_T_G, V_T_G, T_V_G, T_K_G)

}
