content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   * 
   *   http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  package org.apache.weex;
  
  import android.os.Looper;
  import android.support.annotation.NonNull;
  import android.support.annotation.Nullable;
  import android.text.TextUtils;
  
  import org.apache.weex.adapter.ClassLoaderAdapter;
  import org.apache.weex.adapter.DefaultUriAdapter;
  import org.apache.weex.adapter.DefaultWXHttpAdapter;
  import org.apache.weex.adapter.IWXConfigAdapter;
  import org.apache.weex.adapter.ICrashInfoReporter;
  import org.apache.weex.adapter.IDrawableLoader;
  import org.apache.weex.adapter.IWXJscProcessManager;
  import org.apache.weex.adapter.ITracingAdapter;
  import org.apache.weex.adapter.IWXAccessibilityRoleAdapter;
  import org.apache.weex.adapter.IWXHttpAdapter;
  import org.apache.weex.adapter.IWXImgLoaderAdapter;
  import org.apache.weex.adapter.IWXJSExceptionAdapter;
  import org.apache.weex.adapter.IWXJsFileLoaderAdapter;
  import org.apache.weex.adapter.IWXSoLoaderAdapter;
  import org.apache.weex.adapter.IWXUserTrackAdapter;
  import org.apache.weex.adapter.URIAdapter;
  import org.apache.weex.appfram.navigator.IActivityNavBarSetter;
  import org.apache.weex.appfram.navigator.INavigator;
  import org.apache.weex.appfram.storage.DefaultWXStorage;
  import org.apache.weex.appfram.storage.IWXStorageAdapter;
  import org.apache.weex.appfram.websocket.IWebSocketAdapter;
  import org.apache.weex.appfram.websocket.IWebSocketAdapterFactory;
  import org.apache.weex.bridge.WXBridgeManager;
  import org.apache.weex.bridge.WXModuleManager;
  import org.apache.weex.bridge.WXValidateProcessor;
  import org.apache.weex.common.WXRefreshData;
  import org.apache.weex.common.WXRuntimeException;
  import org.apache.weex.common.WXThread;
  import org.apache.weex.common.WXWorkThreadManager;
  import org.apache.weex.font.FontAdapter;
  import org.apache.weex.performance.IApmGenerator;
  import org.apache.weex.performance.IWXAnalyzer;
  import org.apache.weex.ui.WXRenderManager;
  import org.apache.weex.utils.WXLogUtils;
  import org.apache.weex.utils.WXUtils;
  
  import java.io.File;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.CopyOnWriteArrayList;
  import java.util.concurrent.atomic.AtomicInteger;
  
  /**
   * Manger class for weex context.
   */
  public class WXSDKManager {
  
    private static volatile WXSDKManager sManager;
    private static AtomicInteger sInstanceId = new AtomicInteger(0);
    private final WXWorkThreadManager mWXWorkThreadManager;
    private WXBridgeManager mBridgeManager;
    /** package **/ WXRenderManager mWXRenderManager;
  
    private IWXUserTrackAdapter mIWXUserTrackAdapter;
    private IWXImgLoaderAdapter mIWXImgLoaderAdapter;
    private IWXSoLoaderAdapter mIWXSoLoaderAdapter;
    private IDrawableLoader mDrawableLoader;
    private IWXHttpAdapter mIWXHttpAdapter;
    private IActivityNavBarSetter mActivityNavBarSetter;
    private IWXAccessibilityRoleAdapter mRoleAdapter;
    private List<IWXAnalyzer> mWXAnalyzerList;
    private IApmGenerator mApmGenerater;
    private IWXJsFileLoaderAdapter mWXJsFileLoaderAdapter;
  
    private ICrashInfoReporter mCrashInfo;
  
    private IWXJSExceptionAdapter mIWXJSExceptionAdapter;
  
    private IWXConfigAdapter mConfigAdapter;
    private IWXStorageAdapter mIWXStorageAdapter;
    private IWXStatisticsListener mStatisticsListener;
    private URIAdapter mURIAdapter;
    private ClassLoaderAdapter mClassLoaderAdapter;
    private IWebSocketAdapterFactory mIWebSocketAdapterFactory;
    private ITracingAdapter mTracingAdapter;
    private WXValidateProcessor mWXValidateProcessor;
    private IWXJscProcessManager mWXJscProcessManager;
    // Tell weexv8 to initialize v8, default is true.
    private boolean mNeedInitV8 = true;
    private List<IInitListener> mInitListeners;
  
    //add when instance create,rm when instance destroy, not like WXRenderManager
    private Map<String,WXSDKInstance> mAllInstanceMap;
  
    private List<InstanceLifeCycleCallbacks> mLifeCycleCallbacks;
  
    private static final int DEFAULT_VIEWPORT_WIDTH = 750;
  
    private WXSDKManager() {
      this(new WXRenderManager());
    }
  
    private WXSDKManager(WXRenderManager renderManager) {
      mWXRenderManager = renderManager;
      mBridgeManager = WXBridgeManager.getInstance();
      mWXWorkThreadManager = new WXWorkThreadManager();
      mWXAnalyzerList = new CopyOnWriteArrayList<>();
      mAllInstanceMap = new HashMap<>();
      mInitListeners = new ArrayList<>();
    }
  
    /**
     * Used in junit test
     */
    static void initInstance(WXRenderManager renderManager){
      sManager = new WXSDKManager(renderManager);
    }
  
    public void registerStatisticsListener(IWXStatisticsListener listener) {
      mStatisticsListener = listener;
    }
  
    public IWXStatisticsListener getWXStatisticsListener() {
      return mStatisticsListener;
    }
  
    public void onSDKEngineInitialize() {
      if (mStatisticsListener != null) {
        mStatisticsListener.onSDKEngineInitialize();
      }
    }
  
    public synchronized void setInitListener(IInitListener listener) {
      if(WXSDKEngine.isInitialized()) {
        listener.onInitSuccess();
      }
      else {
        mInitListeners.add(listener);
      }
    }
  
    public synchronized void removeInitListener(IInitListener listener) {
      if(mInitListeners.contains(listener)) {
        mInitListeners.remove(listener);
      }
    }
  
    public synchronized void notifyInitSuccess() {
      for (IInitListener listener: mInitListeners) {
        listener.onInitSuccess();
      }
      mInitListeners.clear();
    }
  
    public void setNeedInitV8(boolean need) {
      mNeedInitV8 = need;
    }
  
    public boolean needInitV8() {
      return mNeedInitV8;
    }
  
    public void takeJSHeapSnapshot(String path) {
      File file = new File(path);
      if (!file.exists()) {
        if (!file.mkdir()) {
          return;
        }
      }
  
      String name = String.valueOf(sInstanceId.get());
      String filename = path;
  
      if (!path.endsWith(File.separator)) {
        filename += File.separator;
      }
      filename += name;
      filename += ".heapsnapshot";
  
      mBridgeManager.takeJSHeapSnapshot(filename);
    }
  
    public static WXSDKManager getInstance() {
      if (sManager == null) {
        synchronized (WXSDKManager.class) {
          if(sManager == null) {
            sManager = new WXSDKManager();
          }
        }
      }
      return sManager;
    }
  
    public static int getInstanceViewPortWidth(String instanceId){
      WXSDKInstance instance = getInstance().getSDKInstance(instanceId);
      if (instance == null) {
        return DEFAULT_VIEWPORT_WIDTH;
      }
      return instance.getInstanceViewPortWidth();
    }
  
    static void setInstance(WXSDKManager manager){
      sManager = manager;
    }
  
    public IActivityNavBarSetter getActivityNavBarSetter() {
      return mActivityNavBarSetter;
    }
  
    public void setActivityNavBarSetter(IActivityNavBarSetter mActivityNavBarSetter) {
      this.mActivityNavBarSetter = mActivityNavBarSetter;
    }
  
    public void restartBridge() {
      mBridgeManager.restart();
    }
  
    public WXBridgeManager getWXBridgeManager() {
      return mBridgeManager;
    }
  
    public WXRenderManager getWXRenderManager() {
      return mWXRenderManager;
    }
    public IWXJscProcessManager getWXJscProcessManager() {
      return mWXJscProcessManager;
    }
    public WXWorkThreadManager getWXWorkThreadManager() {
      return mWXWorkThreadManager;
    }
  
    public void setWxConfigAdapter(IWXConfigAdapter mConfigAdapter) {
      this.mConfigAdapter = mConfigAdapter;
    }
  
    public IWXConfigAdapter getWxConfigAdapter() {
      return mConfigAdapter;
    }
  
    public @Nullable WXSDKInstance getSDKInstance(String instanceId) {
      return instanceId == null? null : mWXRenderManager.getWXSDKInstance(instanceId);
    }
  
    public void postOnUiThread(Runnable runnable, long delayMillis) {
      mWXRenderManager.postOnUiThread(WXThread.secure(runnable), delayMillis);
    }
    public Map<String, WXSDKInstance> getAllInstanceMap() {
      return mAllInstanceMap;
    }
  
    public void destroy() {
      if (mWXWorkThreadManager != null) {
        mWXWorkThreadManager.destroy();
      }
      mAllInstanceMap.clear();
    }
  
    @Deprecated
    public void callback(String instanceId, String funcId, Map<String, Object> data) {
      mBridgeManager.callback(instanceId, funcId, data);
    }
  
    @Deprecated
    public void callback(String instanceId, String funcId, Map<String, Object> data,boolean keepAlive) {
      mBridgeManager.callback(instanceId, funcId, data,keepAlive);
    }
  
    public void initScriptsFramework(String framework) {
      mBridgeManager.initScriptsFramework(framework);
    }
  
    public void registerComponents(List<Map<String, Object>> components) {
      mBridgeManager.registerComponents(components);
    }
  
    public void registerModules(Map<String, Object> modules) {
      mBridgeManager.registerModules(modules);
    }
  
    /**
     * Do not direct invoke this method in Components, use {@link WXSDKInstance#fireEvent(String, String, Map, Map)} instead.
     */
    @Deprecated
    public void fireEvent(final String instanceId, String ref, String type) {
      fireEvent(instanceId, ref, type, new HashMap<String, Object>());
    }
  
    /**
     * FireEvent back to JS
     * Do not direct invoke this method in Components, use {@link WXSDKInstance#fireEvent(String, String, Map, Map)} instead.
     */
    @Deprecated
    public void fireEvent(final String instanceId, String ref, String type, Map<String, Object> params){
      fireEvent(instanceId,ref,type,params,null);
    }
  
    /**
     * Do not direct invoke this method in Components, use {@link WXSDKInstance#fireEvent(String, String, Map, Map)} instead.
     **/
    @Deprecated
    public void fireEvent(final String instanceId, String ref, String type, Map<String, Object> params,Map<String,Object> domChanges) {
      if (WXEnvironment.isApkDebugable() && Looper.getMainLooper().getThread().getId() != Thread.currentThread().getId()) {
        throw new WXRuntimeException("[WXSDKManager]  fireEvent error");
      }
      mBridgeManager.fireEventOnNode(instanceId, ref, type, params,domChanges);
    }
  
    void createInstance(WXSDKInstance instance, Script code, Map<String, Object> options, String jsonInitData) {
      mWXRenderManager.registerInstance(instance);
      mBridgeManager.createInstance(instance.getInstanceId(), code, options, jsonInitData);
      if (mLifeCycleCallbacks != null) {
        for (InstanceLifeCycleCallbacks callbacks : mLifeCycleCallbacks) {
          callbacks.onInstanceCreated(instance.getInstanceId());
        }
      }
    }
  
    void refreshInstance(String instanceId, WXRefreshData jsonData) {
      mBridgeManager.refreshInstance(instanceId, jsonData);
    }
  
    void destroyInstance(String instanceId) {
      setCrashInfo(WXEnvironment.WEEX_CURRENT_KEY,"");
      if (TextUtils.isEmpty(instanceId)) {
        return;
      }
      if (!WXUtils.isUiThread()) {
        throw new WXRuntimeException("[WXSDKManager] destroyInstance error");
      }
      if (mLifeCycleCallbacks != null) {
        for (InstanceLifeCycleCallbacks callbacks : mLifeCycleCallbacks) {
          callbacks.onInstanceDestroyed(instanceId);
        }
      }
      mWXRenderManager.removeRenderStatement(instanceId);
      mBridgeManager.destroyInstance(instanceId);
      WXModuleManager.destroyInstanceModules(instanceId);
    }
  
    String generateInstanceId() {
      return String.valueOf(sInstanceId.incrementAndGet());
    }
  
    public IWXUserTrackAdapter getIWXUserTrackAdapter() {
      return mIWXUserTrackAdapter;
    }
  
    public IWXImgLoaderAdapter getIWXImgLoaderAdapter() {
      return mIWXImgLoaderAdapter;
    }
  
    public IWXJsFileLoaderAdapter getIWXJsFileLoaderAdapter() {
      return mWXJsFileLoaderAdapter;
    }
  
    public IDrawableLoader getDrawableLoader() {
      return mDrawableLoader;
    }
  
    public IWXJSExceptionAdapter getIWXJSExceptionAdapter() {
      return mIWXJSExceptionAdapter;
    }
  
    public void setIWXJSExceptionAdapter(IWXJSExceptionAdapter IWXJSExceptionAdapter) {
      mIWXJSExceptionAdapter = IWXJSExceptionAdapter;
    }
  
    public @NonNull IWXHttpAdapter getIWXHttpAdapter() {
      if (mIWXHttpAdapter == null) {
        mIWXHttpAdapter = new DefaultWXHttpAdapter();
      }
      return mIWXHttpAdapter;
    }
  
    public IApmGenerator getApmGenerater() {
      return mApmGenerater;
    }
  
    public @NonNull URIAdapter getURIAdapter() {
      if(mURIAdapter == null){
        mURIAdapter = new DefaultUriAdapter();
      }
      return mURIAdapter;
    }
  
    public ClassLoaderAdapter getClassLoaderAdapter() {
      if(mClassLoaderAdapter == null){
        mClassLoaderAdapter = new ClassLoaderAdapter();
      }
      return mClassLoaderAdapter;
    }
  
    public IWXSoLoaderAdapter getIWXSoLoaderAdapter() {
      return mIWXSoLoaderAdapter;
    }
  
    public List<IWXAnalyzer> getWXAnalyzerList(){
      return mWXAnalyzerList;
    }
  
    public void addWXAnalyzer(IWXAnalyzer analyzer){
      if (!mWXAnalyzerList.contains(analyzer)) {
        mWXAnalyzerList.add(analyzer);
      }
    }
  
    public void rmWXAnalyzer(IWXAnalyzer analyzer){
      mWXAnalyzerList.remove(analyzer);
    }
  
    void setInitConfig(InitConfig config){
      this.mIWXHttpAdapter = config.getHttpAdapter();
      this.mIWXImgLoaderAdapter = config.getImgAdapter();
      this.mDrawableLoader = config.getDrawableLoader();
      this.mIWXStorageAdapter = config.getStorageAdapter();
      this.mIWXUserTrackAdapter = config.getUtAdapter();
      this.mURIAdapter = config.getURIAdapter();
      this.mIWebSocketAdapterFactory = config.getWebSocketAdapterFactory();
      this.mIWXJSExceptionAdapter = config.getJSExceptionAdapter();
      this.mIWXSoLoaderAdapter = config.getIWXSoLoaderAdapter();
      this.mClassLoaderAdapter = config.getClassLoaderAdapter();
      this.mApmGenerater = config.getApmGenerater();
      this.mWXJsFileLoaderAdapter = config.getJsFileLoaderAdapter();
      this.mWXJscProcessManager = config.getJscProcessManager();
    }
  
    public IWXStorageAdapter getIWXStorageAdapter(){
      if(mIWXStorageAdapter == null){
        if(WXEnvironment.sApplication != null){
          mIWXStorageAdapter = new DefaultWXStorage(WXEnvironment.sApplication);
        }else{
          WXLogUtils.e("WXStorageModule", "No Application context found,you should call WXSDKEngine#initialize() method in your application");
        }
      }
      return mIWXStorageAdapter;
    }
  
    /**
     * Weex embedders can use <code>notifyTrimMemory</code> to reduce
     * memory at a proper time.
     *
     * It's not a good idea to reduce memory at any time, because
     * memory trimming is a expense operation, and V8 needs to do
     * a full GC and all the inline caches get to be cleared.
     *
     * The embedder needs to make some scheduling strategies to
     * ensure that the embedded application is just on an idle time.
     * If the application use the same js bundle to render pages,
     * it's not a good idea to trim memory every time of exiting
     * pages.
     */
    public void notifyTrimMemory() {
      mBridgeManager.notifyTrimMemory();
    }
  
    /**
     * Weex embedders can use <code>notifySerializeCodeCache</code> to
     * serialize code caches if the jsfm has the alility to compile 'new Function'
     * against js bundles on the weex native side.
     *
     * It's a good time to serialize a code cache after exiting a weex page.
     * Then, the next time of entering the same weex page, V8 would compile
     * 'new Function' against the code cache deseriazed from the js bundle.
     */
    public void notifySerializeCodeCache() {
      mBridgeManager.notifySerializeCodeCache();
    }
  
    public @Nullable
    IWebSocketAdapter getIWXWebSocketAdapter() {
      if (mIWebSocketAdapterFactory != null) {
        return mIWebSocketAdapterFactory.createWebSocketAdapter();
      }
      return null;
    }
  
    public void registerValidateProcessor(WXValidateProcessor processor){
      this.mWXValidateProcessor = processor;
    }
  
    public WXValidateProcessor getValidateProcessor(){
      return mWXValidateProcessor;
    }
  
  
    public void setCrashInfoReporter(ICrashInfoReporter mCrashInfo) {
      this.mCrashInfo = mCrashInfo;
    }
  
    public void setCrashInfo(String key, String value) {
      if(mCrashInfo!=null){
        mCrashInfo.addCrashInfo(key,value);
      }
    }
  
    public interface IInitListener {
      public void onInitSuccess();
    }
  
    public void setTracingAdapter(ITracingAdapter adapter) {
      this.mTracingAdapter = adapter;
    }
  
    public ITracingAdapter getTracingAdapter() {
      return mTracingAdapter;
    }
  
    public void registerInstanceLifeCycleCallbacks(InstanceLifeCycleCallbacks callbacks) {
      if (mLifeCycleCallbacks == null) {
        mLifeCycleCallbacks = new ArrayList<>();
      }
      mLifeCycleCallbacks.add(callbacks);
    }
  
    public void setAccessibilityRoleAdapter(IWXAccessibilityRoleAdapter adapter) {
      this.mRoleAdapter = adapter;
    }
  
    public IWXAccessibilityRoleAdapter getAccessibilityRoleAdapter() {
      return mRoleAdapter;
    }
  
    public interface InstanceLifeCycleCallbacks {
      void onInstanceDestroyed(String instanceId);
      void onInstanceCreated(String instanceId);
    }
  
    private INavigator mNavigator;
  
    public INavigator getNavigator() {
      return mNavigator;
    }
  
    public void setNavigator(INavigator mNavigator) {
      this.mNavigator = mNavigator;
    }
  
  
    private FontAdapter mFontAdapter;
  
    public FontAdapter getFontAdapter(){
        if(mFontAdapter == null){
          synchronized (this){
            if(mFontAdapter == null){
                mFontAdapter = new FontAdapter();
            }
          }
        }
        return mFontAdapter;
    }
  }
  