package p.studio.utilities.structures;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

public interface ReadOnlyCollection<T> extends Iterable<T> {
    static <T> ReadOnlyCollection<T> empty() {
        return wrap(List.of());
    }

    static <T> ReadOnlyCollection<T> wrap(final Collection<T> collection) {
        if (collection == null || collection.isEmpty()) {
            return wrapInternal(List.of());
        }
        return wrapInternal(List.copyOf(collection));
    }

    @SafeVarargs
    static <T> ReadOnlyCollection<T> with(final T... values) {
        if (values == null || values.length == 0) {
            return ReadOnlyCollection.empty();
        }
        return ReadOnlyCollection.wrap(Stream.of(values).toList());
    }

    static <T> boolean isEmpty(final ReadOnlyCollection<T> c) {
        return c == null || c.isEmpty();
    }

    static <T> boolean isNotEmpty(final ReadOnlyCollection<T> c) {
        return !isEmpty(c);
    }

    private static <T> ReadOnlyCollection<T> wrapInternal(final Collection<T> collection) {
        return new ReadOnlyCollection<>() {
            @Override
            public Collection<T> asCollection() {
                return collection;
            }

            @Override
            public boolean equals(final Object o) {
                if (o == this) return true;
                if (o instanceof ReadOnlyCollection<?> other) {
                    return asCollection().equals(other.asCollection());
                }
                return false;
            }

            @Override
            public int hashCode() {
                return collection.hashCode();
            }
        };
    }

    Collection<T> asCollection();

    default int size() {
        return asCollection().size();
    }

    default boolean isEmpty() {
        return asCollection().isEmpty();
    }

    default boolean contains(final Object o) {
        return asCollection().contains(o);
    }

    default boolean containsAll(final ReadOnlyCollection<T> c) {
        if (ReadOnlyCollection.isEmpty(c)) {
            return true;
        }
        return c.stream().allMatch(this::contains);
    }

    default boolean containsAny(final ReadOnlyCollection<T> c) {
        if (ReadOnlyCollection.isEmpty(c)) {
            return false;
        }
        return c.stream().anyMatch(this::contains);
    }

    @Override
    default Iterator<T> iterator() {
        return asCollection().iterator();
    }

    @Override
    default Spliterator<T> spliterator() {
        return asCollection().spliterator();
    }

    default Stream<T> stream() {
        return asCollection().stream();
    }

    default <R> Stream<R> map(final Function<T, R> mapper) {
        return stream().map(mapper);
    }

    default T[] toArray(final Supplier<T[]> factory) {
        return asCollection().toArray(factory.get());
    }

    default MutableList<T> toMutableList() {
        return MutableList.wrap(asCollection());
    }
}
