package p.studio.utilities.structures;

import java.util.*;
import java.util.stream.Stream;

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

    static <T> ReadOnlyList<T> wrap(final Collection<T> c) {
        if (c == null || c.isEmpty()) {
            return ReadOnlyList.empty();
        }
        return wrapInternal(List.copyOf(c));
    }

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

    private static <T> ReadOnlyList<T> wrapInternal(final List<T> list) {
        return new ReadOnlyList<>() {
            @Override
            public List<T> asList() {
                return list;
            }

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

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

    List<T> asList();

    @Override
    default Collection<T> asCollection() {
        return asList();
    }

    default T get(final int index) {
        return asList().get(index);
    }

    default int indexOf(final Object o) {
        return asList().indexOf(o);
    }

    default int lastIndexOf(final Object o) {
        return asList().lastIndexOf(o);
    }

    default ListIterator<T> listIterator() {
        return asList().listIterator();
    }

    default ListIterator<T> listIterator(final int index) {
        return asList().listIterator(index);
    }

    default ReadOnlyList<T> immutableSubList(final int fromIndex, final int toIndex) {
        return ReadOnlyList.wrap(asList().subList(fromIndex, toIndex));
    }

    default T getFirst() {
        return asList().getFirst();
    }

    default T getLast() {
        return asList().getLast();
    }

    default ReadOnlyList<T> immutableSort(final Comparator<? super T> c) {
        if (c == null) {
            return ReadOnlyList.wrap(asList().stream().sorted().toList());
        }
        return ReadOnlyList.wrap(asList().stream().sorted(c).toList());
    }

    default ReadOnlyList<T> invert() {
        final var inverted = new ArrayList<>(asList());
        java.util.Collections.reverse(inverted);
        return ReadOnlyList.wrap(inverted);
    }

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

    @Override
    default MutableList<T> toMutableList() {
        return MutableList.wrap(asList());
    }

    default boolean elementsEqual(final ReadOnlyList<?> other) {
        if (other == null || other.size() != size()) {
            return false;
        }
        for (int i = 0; i < size(); i++) {
            if (!Objects.equals(get(i), other.get(i))) {
                return false;
            }
        }
        return true;
    }
}
