package dev.kkorolyov.pancake.platform.entity;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Objects;

/**
 * A distinct combination of registered component types.
 */
public class Signature implements Comparable<Signature> {
	private static final HashMap<Class<? extends Component>, Long> indexMap = new HashMap<>();
	private static long indexCounter;

	private final Collection<Class<? extends Component>> types = new HashSet<>();
	private long signature;

	/**
	 * Constructs a new signature from a set of component types.
	 * @param types types defining signature
	 */
	@SafeVarargs
	public Signature(Class<? extends Component>... types) {
		this(Arrays.asList(types));
	}
	/**
	 * Constructs a new signature from a set of component types.
	 * @param types types defining signature
	 */
	public Signature(Iterable<Class<? extends Component>> types) {
		if (types != null) {
			for (Class<? extends Component> type : types) {
				add(type);
			}
		}
	}

	private static long maskOf(Class<? extends Component> type) {
		return indexMap.computeIfAbsent(type, k -> 1L << indexCounter++);
	}

	/**
	 * Checks if a subset of this signature matches {@code other}.
	 * @param other signature to check against
	 * @return {@code true} if this signature contains all component types specified by {@code other}
	 */
	public final boolean masks(Signature other) {
		return (signature & other.signature) == other.signature;
	}

	/**
	 * Adds a component type to this signature.
	 * @param type added component type
	 */
	public void add(Class<? extends Component> type) {
		types.add(type);
		signature |= maskOf(type);
	}
	/**
	 * Removes a component type from this signature.
	 * @param type removed component type
	 */
	public void remove(Class<? extends Component> type) {
		types.remove(type);
		signature &= ~maskOf(type);
	}

	/** @return number of types composing this signature */
	public int size() {
		return types.size();
	}

	/** @return types composing this signature */
	public Collection<Class<? extends Component>> getTypes() {
		return types;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;

		Signature other = (Signature) o;
		return signature == other.signature;
	}
	@Override
	public int hashCode() {
		return Objects.hash(signature);
	}

	@Override
	public int compareTo(Signature o) {
		return Long.compare(signature, o.signature);
	}
}

package dev.kkorolyov.pancake.platform.entity;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Objects;
import java.util.stream.StreamSupport;

import static java.util.stream.Collectors.toUnmodifiableSet;

/**
 * A distinct combination of registered component types.
 */
public final class Signature implements Comparable<Signature> {
	private static final HashMap<Class<? extends Component>, Long> INDEX_MAP = new HashMap<>();
	private static long indexCounter;

	private final Collection<Class<? extends Component>> types;
	private final long signature;

	/**
	 * Constructs a new signature from a set of component types.
	 * @param types types defining signature
	 */
	@SafeVarargs
	public Signature(Class<? extends Component>... types) {
		this(Arrays.asList(types));
	}
	/**
	 * Constructs a new signature from a set of component types.
	 * @param types types defining signature
	 */
	public Signature(Iterable<Class<? extends Component>> types) {
		this.types = StreamSupport.stream(types.spliterator(), false)
				.collect(toUnmodifiableSet());
		signature = this.types.stream()
				.map(Signature::maskOf)
				.reduce(0L, (sig, i) -> sig | i);
	}
	private static long maskOf(Class<? extends Component> type) {
		return INDEX_MAP.computeIfAbsent(type, k -> 1L << indexCounter++);
	}

	/**
	 * Checks if a subset of this signature matches {@code other}.
	 * @param other signature to check against
	 * @return {@code true} if this signature contains all component types specified by {@code other}
	 */
	public boolean masks(Signature other) {
		return (signature & other.signature) == other.signature;
	}

	/** @return number of types composing this signature */
	public int size() {
		return types.size();
	}

	/** @return types composing this signature */
	public Collection<Class<? extends Component>> getTypes() {
		return types;
	}

	@Override
	public int compareTo(Signature o) {
		return Long.compare(signature, o.signature);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) return true;
		if (obj == null || getClass() != obj.getClass()) return false;
		Signature o = (Signature) obj;
		return signature == o.signature;
	}
	@Override
	public int hashCode() {
		return Objects.hash(signature);
	}

	@Override
	public String toString() {
		return "Signature{" +
				"types=" + types +
				", signature=" + signature +
				'}';
	}
}

Simplify Signature