package com.atlassian.bitbucket.util;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.SortedMap;
import java.util.function.Function;
import java.util.stream.Stream;

import static com.atlassian.bitbucket.util.MoreStreams.streamIterable;

/**
 * Provides support for pagination. This interface makes <em>no guarantee</em> that the next Page will have a start
 * offset of {@link Page#getStart()} + {@link Page#getLimit()}.
 *
 * <p>Clients should <em>always</em> use {@link Page#getNextPageRequest()}
 * to navigate to the next page.</p>
 * @param <T>
 */
public interface Page<T> {

    /**
     * @return the offset into the overall result set this page starts at (0 based)
     */
    int getStart();

    /**
     * @return the number of results in this 'page' of results
     */
    int getSize();

    /**
     * @return the original limit on the {@link PageRequest} that generated this page
     */
    int getLimit();

    /**
     * @return {@code true} if there are no more results
     */
    boolean getIsLastPage();

    /**
     * @return an iterable of the values on this page
     */
    @Nonnull
    Iterable<T> getValues();

    /**
     * @return A page request instance  which can be used to request the next page
     */
    @Nullable
    PageRequest getNextPageRequest();

    /**
     * Get a map of the page values mapped by their ordinal values. For filtered pages, the ordinals
     * are the ordinals in the underlying paged collection.
     *
     * @return The values mapped by their ordinal value in the page.
     */
    @Nonnull
    SortedMap<Integer, T> getOrdinalIndexedValues();

    /**
     * @return A new sequential {@code Stream} of the values on this page
     */
    @Nonnull
    default Stream<T> stream() {
        return streamIterable(getValues());
    }

    /**
     * Transforms the page from a page of Ts to a page of Es
     * @param transformFunction the transformer
     * @return the new page of E
     */
    @Nonnull
    <E> Page<E> transform(@Nonnull Function<? super T, ? extends E> transformFunction);
}
