/*
 * Copyright (C) 2002-2017 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 KEY_WIDENED
	/** {@inheritDoc}
	 *
	 * <p>In this default implementation, the key gets narrowed down to the
	 * actual key type, throwing an exception if the given key can't be
	 * represented in the restricted domain. This is done for interoperability
	 * with the Java 8 function environment. Its use 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 don't 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);

	/** 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 = GET_VALUE(k);
		return (v != defaultReturnValue() || containsKey(k)) ? VALUE2OBJ(v) : null;
	}

	/** {@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;
	}
}
