package com.facebook.react.bridge;

import android.content.res.AssetManager;
import android.os.AsyncTask;
import android.util.Log;
import com.facebook.common.logging.FLog;

import java.util.List;

/**
 * Accelerated version of JavaModuleWrapper
 */
public final class AcJavaModuleWrapper extends JavaModuleWrapper {

  private static ModuleProvider mCoreModuleProvider;
  private static ModuleProvider mCustomModuleProvider;
  private final ModuleHelper mModuleHelper;

  static {
    String clsName = JavaModuleWrapper.class.getName();
    try {
      // CoreModuleProvider generated by ReactNativeModuleProcessor.java,
      // aim to provide ModuleHelper for official native modules
      Class<?> coreHelperCls = Class.forName(clsName + "$CoreModuleProvider");
      //noinspection unchecked
      mCoreModuleProvider = (ModuleProvider) coreHelperCls.newInstance();
    } catch (ClassNotFoundException e) {
      FLog.w("JavaModuleHelper", "Could not find generated core module provider");
    } catch (InstantiationException e) {
      throw new RuntimeException("Unable to instantiate core module provider ", e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Unable to instantiate core module provider ", e);
    }
    try {
      // CustomModuleProvider generated by ReactNativeModuleProcessor.java,
      // aim to provide ModuleHelper for third-party native modules
      Class<?> customHelperCls = Class.forName(clsName + "$CustomModuleProvider");
      //noinspection unchecked
      mCustomModuleProvider = (ModuleProvider) customHelperCls.newInstance();
    } catch (ClassNotFoundException e) {
      FLog.w("JavaModuleHelper", "Could not find generated custom module provider");
    } catch (InstantiationException e) {
      throw new RuntimeException("Unable to instantiate custom module provider ", e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Unable to instantiate custom module provider ", e);
    }
  }

  public AcJavaModuleWrapper(JSInstance jsInstance, String moduleClass, ModuleHolder moduleHolder) {
    super(jsInstance, moduleHolder);
    ModuleHelper helper = null;
    if (helper == null && mCustomModuleProvider != null) {
      helper = mCustomModuleProvider.getModuleHelper(getModule());
    }
    if (mCoreModuleProvider != null) {
      helper = mCoreModuleProvider.getModuleHelper(getModule());
    }
    mModuleHelper = helper;
  }

  @Override
  public List<MethodDescriptor> getMethodDescriptors() {
    return mModuleHelper == null ? super.getMethodDescriptors() : mModuleHelper.getMethodDescriptors();
  }

  @Override
  public void invoke(int methodId, ReadableNativeArray parameters) {
    if (mModuleHelper == null) {
      super.invoke(methodId, parameters);
    } else {
      mModuleHelper.invoke(mJSInstance, methodId, parameters);
    }
  }

  public interface ModuleHelper {

    /**
     * @return
     */
    List<MethodDescriptor> getMethodDescriptors();

    /**
     * @param jsInstance
     * @param methodId
     * @param parameters
     */
    void invoke(JSInstance jsInstance, int methodId, ReadableNativeArray parameters);
  }

  interface ModuleProvider {
    ModuleHelper getModuleHelper(BaseJavaModule module);
  }

}
