package com.carrotsearch.hppc;

import java.util.Iterator;
import java.util.function.IntBinaryOperator;

import com.carrotsearch.hppc.comparators.LongComparator;
import com.carrotsearch.hppc.comparators.LongObjectComparator;
import com.carrotsearch.hppc.cursors.LongCursor;
import com.carrotsearch.hppc.cursors.LongObjectCursor;
import com.carrotsearch.hppc.cursors.ObjectCursor;
import com.carrotsearch.hppc.predicates.LongObjectPredicate;
import com.carrotsearch.hppc.predicates.LongPredicate;
import com.carrotsearch.hppc.predicates.ObjectPredicate;
import com.carrotsearch.hppc.procedures.LongObjectProcedure;
import com.carrotsearch.hppc.procedures.LongProcedure;
import com.carrotsearch.hppc.procedures.ObjectProcedure;
import com.carrotsearch.hppc.sorting.QuickSort;

/**
 * Read-only view with sorted iteration order on a delegate
 * {@link LongObjectHashMap}.
 *
 * <p>
 * In its constructor, this view creates its own iteration order array and sorts
 * it, which is in O(n.log(n)) of the size of the delegate map. Afterward, calls
 * to any method have the same performance as the delegate map.
 *
 * <p>
 * This view is read-only. In addition, the delegate map must not be modified
 * while the view is used, otherwise the iteration is undefined.
 *
 * <p>
 * Since this view provides a fixed iteration order, it must not be used to add
 * entries to another {@link LongObjectHashMap} as this may result in a runtime
 * deadlock. See
 * <a href="https://github.com/carrotsearch/hppc/issues/146">HPPC-103</a> and
 * <a href="https://github.com/carrotsearch/hppc/issues/228">HPPC-186</a> for
 * more information.
 */
@SuppressWarnings("unchecked")
@com.carrotsearch.hppc.Generated(date = "2024-06-04T15:20:17+0200", value = "SortedIterationKTypeVTypeHashMap.java")
public class SortedIterationLongObjectHashMap<VType> implements LongObjectMap<VType> {
	/** An iterator implementation for {@link #iterator}. */
	private final class EntryIterator extends AbstractIterator<LongObjectCursor<VType>> {
		private final LongObjectCursor<VType> cursor = new LongObjectCursor<VType>();
		private int index;

		@Override
		protected LongObjectCursor<VType> fetch() {
			if (index < iterationOrder.length) {
				int slot = iterationOrder[index++];
				cursor.index = slot;
				cursor.key = delegate.keys[slot];
				cursor.value = (VType) delegate.values[slot];
				return cursor;
			}
			return done();
		}
	}

	/** A view of the keys in sorted order. */
	private final class KeysContainer extends AbstractLongCollection implements LongLookupContainer {
		private final SortedIterationLongObjectHashMap<VType> owner = SortedIterationLongObjectHashMap.this;

		@Override
		public void clear() {
			throw readOnlyException();
		}

		@Override
		public boolean contains(long e) {
			return owner.containsKey(e);
		}

		@Override
		public <T extends LongProcedure> T forEach(final T procedure) {
			owner.forEach((LongObjectProcedure<VType>) (k, v) -> procedure.apply(k));
			return procedure;
		}

		@Override
		public <T extends LongPredicate> T forEach(final T predicate) {
			owner.forEach((LongObjectPredicate<VType>) (key, value) -> predicate.apply(key));
			return predicate;
		}

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

		@Override
		public Iterator<LongCursor> iterator() {
			return new KeysIterator();
		}

		@Override
		public void release() {
			throw readOnlyException();
		}

		@Override
		public int removeAll(final long e) {
			throw readOnlyException();
		}

		@Override
		public int removeAll(LongPredicate predicate) {
			throw readOnlyException();
		}

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

	/** A sorted iterator over the set of assigned keys. */
	private final class KeysIterator extends AbstractIterator<LongCursor> {
		private final LongCursor cursor = new LongCursor();
		private int index;

		@Override
		protected LongCursor fetch() {
			if (index < iterationOrder.length) {
				int slot = iterationOrder[index++];
				cursor.index = slot;
				cursor.value = delegate.keys[slot];
				return cursor;
			}
			return done();
		}
	}

	/** A view of the values in sorted order. */
	private final class ValuesContainer extends AbstractObjectCollection<VType> {
		private final SortedIterationLongObjectHashMap<VType> owner = SortedIterationLongObjectHashMap.this;

		@Override
		public void clear() {
			throw readOnlyException();
		}

		@Override
		public boolean contains(VType value) {
			for (LongObjectCursor<VType> c : owner) {
				if (java.util.Objects.equals(value, c.value)) {
					return true;
				}
			}
			return false;
		}

		@Override
		public <T extends ObjectProcedure<? super VType>> T forEach(T procedure) {
			owner.forEach((LongObjectProcedure<VType>) (k, v) -> procedure.apply(v));
			return procedure;
		}

		@Override
		public <T extends ObjectPredicate<? super VType>> T forEach(T predicate) {
			owner.forEach((LongObjectPredicate<VType>) (k, v) -> predicate.apply(v));
			return predicate;
		}

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

		@Override
		public Iterator<ObjectCursor<VType>> iterator() {
			return new ValuesIterator();
		}

		@Override
		public void release() {
			throw readOnlyException();
		}

		@Override
		public int removeAll(final ObjectPredicate<? super VType> predicate) {
			throw readOnlyException();
		}

		@Override
		public int removeAll(final VType e) {
			throw readOnlyException();
		}

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

	/** A sorted iterator over the set of assigned values. */
	private final class ValuesIterator extends AbstractIterator<ObjectCursor<VType>> {
		private final ObjectCursor<VType> cursor = new ObjectCursor<VType>();
		private int index;

		@Override
		protected ObjectCursor<VType> fetch() {
			if (index < iterationOrder.length) {
				int slot = iterationOrder[index++];
				cursor.index = slot;
				cursor.value = (VType) delegate.values[slot];
				return cursor;
			}
			return done();
		}
	}

	private static RuntimeException readOnlyException() {
		throw new UnsupportedOperationException("Read-only view cannot be modified");
	}

	public final LongObjectHashMap<VType> delegate;

	public final int[] iterationOrder;

	/**
	 * Creates a read-only view with sorted iteration order on the given delegate
	 * map. The ordering is based on the provided comparator on the keys.
	 */
	public SortedIterationLongObjectHashMap(LongObjectHashMap<VType> delegate, LongComparator comparator) {
		this.delegate = delegate;
		this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator);
	}

	/**
	 * Creates a read-only view with sorted iteration order on the given delegate
	 * map. The ordering is based on the provided comparator on keys and values.
	 */
	public SortedIterationLongObjectHashMap(LongObjectHashMap<VType> delegate, LongObjectComparator<VType> comparator) {
		this.delegate = delegate;
		this.iterationOrder = sortIterationOrder(createEntryIndexes(), comparator);
	}

	private boolean checkUnmodified() {
		// Cheap size comparison.
		// We could also check the hashcode, but this is heavy for a frequent check.
		assert delegate.size() == iterationOrder.length
				: "The delegate map changed; this is not supported by this read-only view";
		return true;
	}

	@Override
	public void clear() {
		throw readOnlyException();
	}

	@Override
	public boolean containsKey(long key) {
		return delegate.containsKey(key);
	}

	private int[] createEntryIndexes() {
		final long[] keys = delegate.keys;
		final int size = delegate.size();
		int[] entryIndexes = new int[size];
		int entry = 0;
		if (delegate.hasEmptyKey) {
			entryIndexes[entry++] = delegate.mask + 1;
		}
		for (int keyIndex = 0; entry < size; keyIndex++) {
			if (!((keys[keyIndex]) == 0)) {
				entryIndexes[entry++] = keyIndex;
			}
		}
		return entryIndexes;
	}

	@Override
	public <T extends LongObjectProcedure<? super VType>> T forEach(T procedure) {
		assert checkUnmodified();
		final int[] iterationOrder = this.iterationOrder;
		final long[] keys = delegate.keys;
		final VType[] values = (VType[]) delegate.values;
		for (int i = 0, size = size(); i < size; i++) {
			int slot = iterationOrder[i];
			procedure.apply(keys[slot], values[slot]);
		}
		return procedure;
	}

	@Override
	public <T extends LongObjectPredicate<? super VType>> T forEach(T predicate) {
		assert checkUnmodified();
		final int[] iterationOrder = this.iterationOrder;
		final long[] keys = delegate.keys;
		final VType[] values = (VType[]) delegate.values;
		for (int i = 0, size = size(); i < size; i++) {
			int slot = iterationOrder[i];
			if (!predicate.apply(keys[slot], values[slot])) {
				break;
			}
		}
		return predicate;
	}

	@Override
	public VType get(long key) {
		return delegate.get(key);
	}

	@Override
	public VType getOrDefault(long key, VType defaultValue) {
		return delegate.getOrDefault(key, defaultValue);
	}

	@Override
	public boolean indexExists(int index) {
		return delegate.indexExists(index);
	}

	@Override
	public VType indexGet(int index) {
		return delegate.indexGet(index);
	}

	@Override
	public void indexInsert(int index, long key, VType value) {
		throw readOnlyException();
	}

	@Override
	public int indexOf(long key) {
		return delegate.indexOf(key);
	}

	@Override
	public VType indexRemove(int index) {
		throw readOnlyException();
	}

	@Override
	public VType indexReplace(int index, VType newValue) {
		throw readOnlyException();
	}

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

	@Override
	public Iterator<LongObjectCursor<VType>> iterator() {
		assert checkUnmodified();
		return new EntryIterator();
	}

	@Override
	public LongCollection keys() {
		assert checkUnmodified();
		return new KeysContainer();
	}

	@Override
	public VType put(long key, VType value) {
		throw readOnlyException();
	}

	@Override
	public int putAll(Iterable<? extends LongObjectCursor<? extends VType>> iterable) {
		throw readOnlyException();
	}

	@Override
	public int putAll(LongObjectAssociativeContainer<? extends VType> container) {
		throw readOnlyException();
	}

	@Override
	public void release() {
		throw readOnlyException();
	}

	@Override
	public VType remove(long key) {
		throw readOnlyException();
	}

	@Override
	public int removeAll(LongContainer container) {
		throw readOnlyException();
	}

	@Override
	public int removeAll(LongObjectPredicate<? super VType> predicate) {
		throw readOnlyException();
	}

	@Override
	public int removeAll(LongPredicate predicate) {
		throw readOnlyException();
	}

	@Override
	public int size() {
		assert checkUnmodified();
		return delegate.size();
	}

	/**
	 * Sort the iteration order array based on the provided comparator on the keys.
	 */
	protected int[] sortIterationOrder(int[] entryIndexes, LongComparator comparator) {
		QuickSort.sort(entryIndexes, (i, j) -> {
			long[] keys = delegate.keys;
			return comparator.compare(keys[entryIndexes[i]], keys[entryIndexes[j]]);
		});
		return entryIndexes;
	}

	/**
	 * Sort the iteration order array based on the provided comparator on keys and
	 * values.
	 */
	protected int[] sortIterationOrder(int[] entryIndexes, LongObjectComparator<VType> comparator) {
		QuickSort.sort(entryIndexes, new IntBinaryOperator() {
			final long[] keys = delegate.keys;
			final VType[] values = (VType[]) delegate.values;

			@Override
			public int applyAsInt(int i, int j) {
				int index1 = entryIndexes[i];
				int index2 = entryIndexes[j];
				return comparator.compare(keys[index1], values[index1], keys[index2], values[index2]);
			}
		});
		return entryIndexes;
	}

	@Override
	public ObjectContainer<VType> values() {
		assert checkUnmodified();
		return new ValuesContainer();
	}

	@Override
	public String visualizeKeyDistribution(int characters) {
		return delegate.visualizeKeyDistribution(characters);
	}
}
