package cn.reactnative.modules.pack;

import android.app.Activity;
import android.app.Application;
import android.util.Log;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.JSBundleLoader;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;


/**
 * Created by tdzl2003 on 3/31/16.
 */
public class PackModule extends ReactContextBaseJavaModule{
    PackContext packContext;

    public PackModule(ReactApplicationContext reactContext, PackContext packContext) {
        super(reactContext);
        this.packContext = packContext;
    }

    public PackModule(ReactApplicationContext reactContext) {
        this(reactContext, new PackContext(reactContext));
    }

    @Override
    public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        constants.put("Env", packContext.getPackageEnv());
        constants.put("downloadRootDir", packContext.getRootDir());
        constants.put("packageVersion", packContext.getPackageVersion());
        constants.put("currentVersion", packContext.getCurrentVersion());
        constants.put("currentBundle", packContext.getCurrentBundle());
        boolean isFirstTime = packContext.isFirstTime();
        constants.put("isFirstTime", isFirstTime);
        if (isFirstTime) {
            packContext.clearFirstTime();
        }
        boolean isRolledBack = packContext.isRolledBack();
        constants.put("isRolledBack", isRolledBack);
        if (isRolledBack) {
            packContext.clearRollbackMark();
        }
        return constants;
    }

    @Override
    public String getName() {
        return "RCTPack";
    }

    @ReactMethod
    public void downloadApk(ReadableMap options, final Promise promise){
        String url = options.getString("updateUrl");
        String hash = options.getString("hashName");
        packContext.downloadApk(url, hash, new PackContext.DownloadFileListener() {
            @Override
            public void onDownloadCompleted() {
                promise.resolve(null);
            }

            @Override
            public void onDownloadFailed(Throwable error) {
                promise.reject(error);
            }
        });
    }

    @ReactMethod
    public void downloadUpdate(ReadableMap options, final Promise promise){
        String url = options.getString("updateUrl");
        String hash = options.getString("hashName");
        packContext.downloadFile(url, hash, new PackContext.DownloadFileListener() {
            @Override
            public void onDownloadCompleted() {
                promise.resolve(null);
            }

            @Override
            public void onDownloadFailed(Throwable error) {
                promise.reject(error);
            }
        });
    }

    @ReactMethod
    public void downloadPatchFromPackage(ReadableMap options, final Promise promise){
        String url = options.getString("updateUrl");
        String hash = options.getString("hashName");
        packContext.downloadPatchFromApk(url, hash, new PackContext.DownloadFileListener() {
            @Override
            public void onDownloadCompleted() {
                promise.resolve(null);
            }

            @Override
            public void onDownloadFailed(Throwable error) {
                promise.reject(error);
            }
        });
    }

    @ReactMethod
    public void downloadPatchFromPpk(ReadableMap options, final Promise promise){
        String url = options.getString("updateUrl");
        String hash = options.getString("hashName");
        String originHash = options.getString("originHashName");
        packContext.downloadPatchFromPpk(url, hash, originHash, new PackContext.DownloadFileListener() {
            @Override
            public void onDownloadCompleted() {
                promise.resolve(null);
            }

            @Override
            public void onDownloadFailed(Throwable error) {
                promise.reject(error);
            }
        });
    }

    @ReactMethod
    public void reloadUpdate(ReadableMap options) {
        final String hash = options.getString("hashName");
        final String bundleName = options.getString("bundleName");

        UiThreadUtil.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                packContext.switchVersion(hash, bundleName);
                try {
                    Activity activity = getCurrentActivity();
                    Application application = activity.getApplication();
                    ReactInstanceManager instanceManager = ((ReactApplication) application).getReactNativeHost().getReactInstanceManager();
                    String className = instanceManager.getClass().getSimpleName();
                    Log.d("PACK", "instanceManager name:" + className);
                    if (className.equals("XReactInstanceManagerImpl")) {
                        JSBundleLoader loader = JSBundleLoader.createFileLoader(PackContext.getBundleUrl(application));
                        Field jsBundleField = instanceManager.getClass().getDeclaredField("mBundleLoader");
                        jsBundleField.setAccessible(true);
                        jsBundleField.set(instanceManager, loader);
                    } else if (className.equals("ReactInstanceManager")) {
                        JSBundleLoader loader = JSBundleLoader.createFileLoader(PackContext.getBundleUrl(application));
                        Field jsBundleField = instanceManager.getClass().getDeclaredField("mBundleLoader");
                        jsBundleField.setAccessible(true);
                        jsBundleField.set(instanceManager, loader);
                    }else {
                        Field jsBundleField = instanceManager.getClass().getDeclaredField("mJSBundleFile");
                        jsBundleField.setAccessible(true);
                        jsBundleField.set(instanceManager, PackContext.getBundleUrl(application));
                    }

                    final Method recreateMethod = instanceManager.getClass().getMethod("recreateReactContextInBackground");

                    final ReactInstanceManager finalizedInstanceManager = instanceManager;

                    recreateMethod.invoke(finalizedInstanceManager);

                    activity.recreate();
                } catch (Throwable err) {
                    Log.e("pushy", "Failed to restart application", err);
                }
            }
        });
    }

    @ReactMethod
    public void setNeedUpdate(ReadableMap options) {
        final String hash = options.getString("hashName");
        final String bundleName = options.getString("bundleName");

        UiThreadUtil.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                packContext.switchVersion(hash, bundleName);
            }
        });
    }

    @ReactMethod
    public void markSuccess() {
        UiThreadUtil.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                packContext.markSuccess();
            }
        });
    }
}
