/*
 * Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
 */

package play.core.j;

import play.api.http.HttpConfiguration;
import play.http.ActionCreator;
import play.mvc.Action;
import play.mvc.BodyParser;
import scala.concurrent.ExecutionContext;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

/**
 * The components necessary to handle a Java handler.
 *
 * <p>But this implementation does not uses an Injector. Instead, the necessary {@link
 * play.mvc.Action} and {@link play.mvc.BodyParser} must be added here manually. This is way we
 * avoid mixing runtime dependency injector components with compile time injected ones.
 */
public class MappedJavaHandlerComponents implements JavaHandlerComponents {

  private final ActionCreator actionCreator;
  private final HttpConfiguration httpConfiguration;
  private final ExecutionContext executionContext;
  private final JavaContextComponents contextComponents;

  private final Map<Class<? extends Action<?>>, Supplier<Action<?>>> actions = new HashMap<>();
  private final Map<Class<? extends BodyParser<?>>, Supplier<BodyParser<?>>> bodyPasers =
      new HashMap<>();

  public MappedJavaHandlerComponents(
      ActionCreator actionCreator,
      HttpConfiguration httpConfiguration,
      ExecutionContext executionContext,
      JavaContextComponents contextComponents) {
    this.actionCreator = actionCreator;
    this.httpConfiguration = httpConfiguration;
    this.executionContext = executionContext;
    this.contextComponents = contextComponents;
  }

  @Override
  @SuppressWarnings("unchecked")
  public <A extends BodyParser<?>> A getBodyParser(Class<A> parserClass) {
    return (A) this.bodyPasers.get(parserClass).get();
  }

  @Override
  @SuppressWarnings("unchecked")
  public <A extends Action<?>> A getAction(Class<A> actionClass) {
    return (A) this.actions.get(actionClass).get();
  }

  @Override
  public ActionCreator actionCreator() {
    return this.actionCreator;
  }

  @Override
  public HttpConfiguration httpConfiguration() {
    return this.httpConfiguration;
  }

  @Override
  public ExecutionContext executionContext() {
    return this.executionContext;
  }

  @Override
  public JavaContextComponents contextComponents() {
    return this.contextComponents;
  }

  public <A extends Action<?>> MappedJavaHandlerComponents addAction(
      Class<A> clazz, Supplier<A> actionSupplier) {
    this.actions.put(clazz, (Supplier<Action<?>>) actionSupplier);
    return this;
  }

  public <B extends BodyParser<?>> MappedJavaHandlerComponents addBodyParser(
      Class<B> clazz, Supplier<B> bodyParserSupplier) {
    this.bodyPasers.put(clazz, (Supplier<BodyParser<?>>) bodyParserSupplier);
    return this;
  }
}

@@ -2432,29 +2432,24 @@
 lier) %7B%0A    
-this.
 actions.put(
@@ -2455,25 +2455,29 @@
 (clazz, 
-(
+widen
 Supplier
 %3CAction%3C
@@ -2472,21 +2472,9 @@
 lier
-%3CAction%3C?%3E%3E) 
+(
 acti
@@ -2476,32 +2476,33 @@
 (actionSupplier)
+)
 ;%0A    return thi
@@ -2647,21 +2647,16 @@
 ) %7B%0A    
-this.
 bodyPase
@@ -2673,17 +2673,21 @@
 zz, 
-(
+widen
 Supplier
 %3CBod
@@ -2674,34 +2674,34 @@
 z, widenSupplier
-%3CB
+(b
 odyParser%3C?%3E%3E) b
@@ -2697,50 +2697,272 @@
 rser
-%3C?%3E%3E) bodyParserSupplier);%0A    return this
+Supplier));%0A    return this;%0A  %7D%0A%0A  @SuppressWarnings(%22unchecked%22)%0A  // covariance: Supplier%3C?%3E %3C: Supplier%3CObject%3E, given Supplier%3CA%3E is covariant in A%0A  static %3CA extends B, B%3E Supplier%3CB%3E widenSupplier(final Supplier%3CA%3E parser) %7B%0A    return (Supplier%3CB%3E) parser
 ;%0A  

Allow covariant suppliers to widen