package com.facebook.react;

import android.support.annotation.NonNull;
import com.facebook.react.bridge.ModuleHolder;
import com.facebook.react.bridge.ModuleSpec;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Provider;

/** This will eventually replace {@link LazyReactPackage} when TurboModules are finally done. */
public abstract class TurboReactPackage implements ReactPackage {

  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
    throw new UnsupportedOperationException(
        "In case of TurboModules, createNativeModules is not supported. NativeModuleRegistry should instead use getModuleList or getModule method");
  }

  /**
   * This is only a temporary method to test the impact of initializing some modules eagerly. This
   * will deleted whenn
   * qe_react_native_startup_module_optimization.eager_init_current_viewer_native_module is removed
   *
   * @return
   */
  protected List<String> getEagerNativeModules() {
    return Collections.emptyList();
  }

  /**
   * The API needed for TurboModules. Given a module name, it returns an instance of {@link
   * NativeModule} for the name
   *
   * @param name
   * @param reactContext
   * @return
   */
  public abstract NativeModule getModule(String name, final ReactApplicationContext reactContext);

  /**
   * This is a temporary method till we implement TurboModules. Once we implement TurboModules, we
   * will be able to directly call {@link TurboReactPackage#getModule(String,
   * ReactApplicationContext)} This method will be removed when TurboModule implementation is
   * complete
   *
   * @param reactContext
   * @return
   */
  public Iterable<ModuleHolder> getNativeModuleIterator(
      final ReactApplicationContext reactContext) {
    final Set<Map.Entry<String, ReactModuleInfo>> entrySet =
        getReactModuleInfoProvider().getReactModuleInfos().entrySet();
    final Iterator<Map.Entry<String, ReactModuleInfo>> entrySetIterator = entrySet.iterator();
    final List<String> eagerNativeModules = getEagerNativeModules();
    return new Iterable<ModuleHolder>() {
      @NonNull
      @Override
      // This should ideally be an IteratorConvertor, but we don't have any internal library for it
      public Iterator<ModuleHolder> iterator() {
        return new Iterator<ModuleHolder>() {
          @Override
          public boolean hasNext() {
            return entrySetIterator.hasNext();
          }

          @Override
          public ModuleHolder next() {
            Map.Entry<String, ReactModuleInfo> entry = entrySetIterator.next();
            String name = entry.getKey();
            ReactModuleInfo reactModuleInfo = entry.getValue();
            if (eagerNativeModules.contains(name)) {
              return new ModuleHolder(getModule(name, reactContext));
            } else {
              return new ModuleHolder(
                  reactModuleInfo, new ModuleHolderProvider(name, reactContext));
            }
          }

          @Override
          public void remove() {
            throw new UnsupportedOperationException("Cannot remove native modules from the list");
          }
        };
      }
    };
  }

  /**
   * @param reactContext react application context that can be used to create View Managers.
   * @return list of module specs that can create the View Managers.
   */
  protected List<ModuleSpec> getViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    List<ModuleSpec> viewManagerModuleSpecs = getViewManagers(reactContext);
    if (viewManagerModuleSpecs == null || viewManagerModuleSpecs.isEmpty()) {
      return Collections.emptyList();
    }

    List<ViewManager> viewManagers = new ArrayList<>();
    for (ModuleSpec moduleSpec : viewManagerModuleSpecs) {
      viewManagers.add((ViewManager) moduleSpec.getProvider().get());
    }
    return viewManagers;
  }

  public abstract ReactModuleInfoProvider getReactModuleInfoProvider();

  private class ModuleHolderProvider implements Provider<NativeModule> {

    private final String mName;
    private final ReactApplicationContext mReactContext;

    public ModuleHolderProvider(String name, ReactApplicationContext reactContext) {
      mName = name;
      mReactContext = reactContext;
    }

    @Override
    public NativeModule get() {
      return getModule(mName, mReactContext);
    }
  }
}

@@ -1081,353 +1081,8 @@
  %7D%0A%0A
-  /**%0A   * This is only a temporary method to test the impact of initializing some modules eagerly. This%0A   * will deleted whenn%0A   * qe_react_native_startup_module_optimization.eager_init_current_viewer_native_module is removed%0A   *%0A   * @return%0A   */%0A  protected List%3CString%3E getEagerNativeModules() %7B%0A    return Collections.emptyList();%0A  %7D%0A%0A
   /*
@@ -1175,16 +1175,16 @@
  %7B@link%0A
+
    * Nat
@@ -2053,77 +2053,8 @@
 ();%0A
-    final List%3CString%3E eagerNativeModules = getEagerNativeModules();%0A
     
@@ -2695,197 +2695,32 @@
     
-if (eagerNativeModules.contains(name)) %7B%0A              return new ModuleHolder(getModule(name, reactContext));%0A            %7D else %7B%0A              return new ModuleHolder(%0A                  
+return new ModuleHolder(
 reac
@@ -2783,22 +2783,8 @@
 ));%0A
-            %7D%0A
     

Remove getEagerNativeModules()