package com.reactnativenavigation.react;

import android.app.Activity;
import android.content.Intent;
import android.view.View;

import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;
import com.reactnativenavigation.NavigationApplication;
import com.reactnativenavigation.bridge.NavigationReactEventEmitter;
import com.reactnativenavigation.bridge.NavigationReactPackage;
import com.reactnativenavigation.events.EventBus;
import com.reactnativenavigation.events.JsDevReloadEvent;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nullable;

public class NavigationReactGateway implements ReactGateway {

    private final ReactNativeHost host;
    private NavigationReactEventEmitter reactEventEmitter;
    private JsDevReloadHandler jsDevReloadHandler;

    public NavigationReactGateway() {
        host = new ReactNativeHostImpl();
        jsDevReloadHandler = new JsDevReloadHandler();
    }

    @Override
    public void startReactContextOnceInBackgroundAndExecuteJS() {
        getReactInstanceManager().createReactContextInBackground();
    }

    public boolean isInitialized() {
        return host.hasInstance() && getReactInstanceManager().getCurrentReactContext() != null;
    }

    @Override
    public boolean hasStartedCreatingContext() {
        return getReactInstanceManager().hasStartedCreatingInitialContext();
    }

    public ReactContext getReactContext() {
        return getReactInstanceManager().getCurrentReactContext();
    }

    public NavigationReactEventEmitter getReactEventEmitter() {
        return reactEventEmitter;
    }

    @Override
    public ReactInstanceManager getReactInstanceManager() {
        return host.getReactInstanceManager();
    }

    public void onBackPressed() {
        getReactInstanceManager().onBackPressed();
    }

    public void onDestroyApp() {
        getReactInstanceManager().onHostDestroy();
        host.clear();
    }

    public void onPauseActivity() {
        getReactInstanceManager().onHostPause();
        jsDevReloadHandler.onPauseActivity();
    }

    public void onNewIntent(Intent intent) {
        getReactInstanceManager().onNewIntent(intent);
    }

    @Override
    public boolean onKeyUp(View currentFocus, int keyCode) {
        return jsDevReloadHandler.onKeyUp(currentFocus, keyCode);
    }

    public void onResumeActivity(Activity activity, DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler) {
        getReactInstanceManager().onHostResume(activity, defaultHardwareBackBtnHandler);
        jsDevReloadHandler.onResumeActivity();
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Activity currentActivity = getReactInstanceManager().getCurrentReactContext().getCurrentActivity();
        getReactInstanceManager().onActivityResult(currentActivity, requestCode, resultCode, data);
    }

    public ReactNativeHost getReactNativeHost() {
        return host;
    }

    //TODO temp hack
    private void onReactContextInitialized() {
        reactEventEmitter = new NavigationReactEventEmitter(getReactContext());
    }

    private static class ReactNativeHostImpl extends ReactNativeHost implements ReactInstanceManager.ReactInstanceEventListener {

        ReactNativeHostImpl() {
            super(NavigationApplication.instance);
        }

        @Override
        public boolean getUseDeveloperSupport() {
            return NavigationApplication.instance.isDebug();
        }

        @Override
        protected List<ReactPackage> getPackages() {
            List<ReactPackage> list = new ArrayList<>();
            list.add(new MainReactPackage());
            list.add(new NavigationReactPackage());
            addAdditionalReactPackagesIfNeeded(list);
            return list;
        }

        private void addAdditionalReactPackagesIfNeeded(List<ReactPackage> list) {
            List<ReactPackage> additionalReactPackages = NavigationApplication.instance.createAdditionalReactPackages();
            if (additionalReactPackages == null) {
                return;
            }

            for (ReactPackage reactPackage : additionalReactPackages) {
                if (reactPackage instanceof MainReactPackage)
                    throw new RuntimeException("Do not create a new MainReactPackage. This is created for you.");
                if (reactPackage instanceof NavigationReactPackage)
                    throw new RuntimeException("Do not create a new NavigationReactPackage. This is created for you.");
            }

            list.addAll(additionalReactPackages);
        }

        @Override
        protected ReactInstanceManager createReactInstanceManager() {
            ReactInstanceManager manager = super.createReactInstanceManager();
            if (NavigationApplication.instance.isDebug()) {
                replaceJsDevReloadListener(manager);
            }
            manager.addReactInstanceEventListener(this);
            return manager;
        }

        private void replaceJsDevReloadListener(ReactInstanceManager manager) {
            new JsDevReloadListenerReplacer(manager, new JsDevReloadListenerReplacer.Listener() {
                @Override
                public void onJsDevReload() {
                    EventBus.instance.post(new JsDevReloadEvent());
                }
            }).replace();
        }

        @Override
        public void onReactContextInitialized(ReactContext context) {
            ((NavigationReactGateway) NavigationApplication.instance.getReactGateway()).onReactContextInitialized();
            NavigationApplication.instance.onReactInitialized(context);
        }

        @Override
        public void clear() {
            getReactInstanceManager().removeReactInstanceEventListener(this);
            super.clear();
        }

        @Override
        protected String getJSMainModuleName() {
            String jsMainModuleName = NavigationApplication.instance.getJSMainModuleName();
            if (jsMainModuleName != null)
                return jsMainModuleName;
            return super.getJSMainModuleName();
        }

        @Nullable
        @Override
        protected String getJSBundleFile() {
            String jsBundleFile = NavigationApplication.instance.getJSBundleFile();
            if (jsBundleFile != null)
                return jsBundleFile;
            return super.getJSBundleFile();
        }

        @Nullable
        @Override
        protected String getBundleAssetName() {
            String bundleAssetName = NavigationApplication.instance.getBundleAssetName();
            if (bundleAssetName != null)
                return bundleAssetName;
            return super.getBundleAssetName();
        }
    }
}
