package org.ambraproject.rhino.rest.response;

import org.ambraproject.rhino.model.Timestamped;

import java.io.IOException;
import java.time.Instant;
import java.util.Date;
import java.util.Objects;
import java.util.function.Function;

/**
 * A response that has a timestamp showing the last-modified time of the represented data, which can be sent to the
 * client for caching and compared against an "If-Modified-Since" header.
 */
public final class CacheableResponse<T> {

  /**
   * A function that lazily loads data to serve in a response body, in case the client does not have it cached.
   */
  @FunctionalInterface
  public static interface ResponseSupplier<T> {
    public abstract T get() throws IOException;
  }

  private final ResponseSupplier<? extends T> supplier;
  private final Instant lastModified;

  private CacheableResponse(Instant lastModified, ResponseSupplier<? extends T> supplier) {
    this.supplier = Objects.requireNonNull(supplier);
    this.lastModified = Objects.requireNonNull(lastModified);
  }

  /**
   * Serve a view representing a piece of data with a particular timestamp.
   *
   * @param lastModified the data's timestamp
   * @param supplier     a function that will supply the data when invoked
   * @return the response
   */
  public static <T> CacheableResponse<T> serveView(Instant lastModified, ResponseSupplier<? extends T> supplier) {
    return new CacheableResponse<>(lastModified, supplier);
  }

  /**
   * Serve a view representing a timestamped entity.
   *
   * @param entity       the entity to represent in the response
   * @param viewFunction a function that converts the entity into a serializable view
   * @param <T>          the entity's type
   * @return the response
   */
  public static <T, E extends Timestamped> CacheableResponse<T>
  serveEntity(E entity, Function<? super E, ? extends T> viewFunction) {
    Objects.requireNonNull(viewFunction);
    ResponseSupplier<T> supplier = () -> viewFunction.apply(entity);
    Instant lastModified = entity.getLastModified().toInstant();
    return new CacheableResponse<>(lastModified, supplier);
  }


  public ServiceResponse<T> getIfModified(Date ifModifiedSince) throws IOException {
    return getIfModified(ifModifiedSince == null ? null : ifModifiedSince.toInstant());
  }

  public ServiceResponse<T> getIfModified(Instant ifModifiedSince) throws IOException {
    if ((ifModifiedSince != null) && (ifModifiedSince.compareTo(lastModified) <= 0)) {
      return ServiceResponse.reportNotModified(lastModified);
    } else {
      T body = Objects.requireNonNull(supplier.get());
      return ServiceResponse.serveCacheableView(body, lastModified);
    }
  }


}

@@ -228,16 +228,52 @@
 unction;
+%0Aimport java.util.function.Supplier;
 %0A%0A/**%0A *
@@ -511,256 +511,8 @@
  %7B%0A%0A
-  /**%0A   * A function that lazily loads data to serve in a response body, in case the client does not have it cached.%0A   */%0A  @FunctionalInterface%0A  public static interface ResponseSupplier%3CT%3E %7B%0A    public abstract T get() throws IOException;%0A  %7D%0A%0A
   pr
@@ -523,24 +523,16 @@
 e final 
-Response
 Supplier
@@ -640,32 +640,24 @@
 stModified, 
-Response
 Supplier%3C? e
@@ -1112,24 +1112,16 @@
 dified, 
-Response
 Supplier
@@ -1684,24 +1684,16 @@
 n);%0A    
-Response
 Supplier

Delete ResponseSupplier interface