package li.vin.net;

import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;

import auto.parcel.AutoParcel;
import rx.Observable;
import rx.internal.operators.OnSubscribeFromIterable;

@AutoParcel
public abstract class Page<T extends VinliItem> {
  /*package*/ static final <T extends VinliItem> Builder<T> builder() {
    return new AutoParcel_Page.Builder<>();
  }

  /*package*/ abstract List<T> items();
  /*package*/ abstract Meta meta();
  /*package*/ abstract LinkLoader linkLoader();
  /*package*/ abstract Type type();

  public int size() {
    return items().size();
  }

  public List<T> getItems() {
    return Collections.unmodifiableList(items());
  }

  public Observable<T> observeItems() {
    return Observable.create(new OnSubscribeFromIterable<>(items()));
  }

  public boolean hasNextPage() {
    return meta().pagination.links.next != null;
  }

  public Observable<Page<T>> loadPrevPage() {
    return linkLoader().loadPage(meta().pagination.links.prev, type());
  }

  public Observable<Page<T>> loadNextPage() {
    return linkLoader().loadPage(meta().pagination.links.next, type());
  }

  public Observable<Page<T>> loadFirstPage() {
    return linkLoader().loadPage(meta().pagination.links.first, type());
  }

  public Observable<Page<T>> loadLastPage() {
    return linkLoader().loadPage(meta().pagination.links.last, type());
  }

  /*package*/ Page() { }

  /*package*/ static final class Meta {
    // CHECKSTYLE.OFF: VisibilityModifier
    public final Pagination pagination;
    // CHECKSTYLE.ON

    public Meta(Pagination pagination) {
      this.pagination = pagination;
    }

    public static final class Pagination {
      public final int total, limit, offset;
      // CHECKSTYLE.OFF: VisibilityModifier
      public final Links links;
      // CHECKSTYLE.ON

      public Pagination(int total, int limit, int offset, Links links) {
        this.total = total;
        this.limit = limit;
        this.offset = offset;
        this.links = links;
      }

      public static final class Links {
        public final String first, last, next, prev;

        public Links(String first, String last, String next, String prev) {
          this.first = first;
          this.last = last;
          this.next = next;
          this.prev = prev;
        }
      }
    }
  }

  @AutoParcel.Builder
  /*package*/ interface Builder<T extends VinliItem> {
    Builder<T> items(List<T> l);
    Builder<T> meta(Meta m);
    Builder<T> linkLoader(LinkLoader ll);
    Builder<T> type(Type t);

    Page<T> build();
  }
}

@@ -152,16 +152,43 @@
 rvable;%0A
+import rx.functions.Func1;%0A
 import r
@@ -296,16 +296,928 @@
 Item%3E %7B%0A
+  public static final Func1 EXTRACT_ITEMS = new Func1%3CPage%3C?%3E, Observable%3C?%3E%3E() %7B%0A    @Override public Observable%3C?%3E call(Page%3C?%3E tPage) %7B%0A      return tPage.observeItems();%0A    %7D%0A  %7D;%0A%0A  @SuppressWarnings(%22unchecked%22)%0A  public static final %3CT extends VinliItem%3E Func1%3CPage%3CT%3E, Observable%3CT%3E%3E extractItems() %7B%0A    return EXTRACT_ITEMS;%0A  %7D%0A%0A  public static final Func1 ALL_ITEMS = new Func1%3CPage%3C? extends VinliItem%3E, Observable%3C? extends VinliItem%3E%3E() %7B%0A    @Override%0A    @SuppressWarnings(%22unchecked%22)%0A    public Observable%3C? extends VinliItem%3E call(Page%3C? extends VinliItem%3E tPage) %7B%0A      if (tPage.hasNextPage()) %7B%0A        return tPage.observeItems().concatWith(tPage.loadNextPage().flatMap(ALL_ITEMS));%0A      %7D%0A%0A      return tPage.observeItems();%0A    %7D%0A  %7D;%0A%0A  @SuppressWarnings(%22unchecked%22)%0A  public static final %3CT extends VinliItem%3E Func1%3CPage%3CT%3E, Observable%3CT%3E%3E allItems() %7B%0A    return ALL_ITEMS;%0A  %7D%0A%0A
   /*pack
@@ -1785,21 +1785,125 @@
     
-return meta()
+final Meta meta = meta();%0A    return meta.pagination != null &&%0A        meta.pagination.links != null &&%0A        meta
 .pag

add some util helpers for pages and fix checking for next link