/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.drawee.backends.pipeline;

import android.content.Context;
import com.facebook.common.logging.FLog;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.core.ImagePipelineFactory;
import com.facebook.imagepipeline.core.NativeCodeSetup;
import com.facebook.imagepipeline.systrace.FrescoSystrace;
import com.facebook.soloader.nativeloader.NativeLoader;
import com.facebook.soloader.nativeloader.SystemDelegate;
import java.lang.reflect.InvocationTargetException;
import javax.annotation.Nullable;

/**
 * Fresco entry point.
 *
 * <p>You must initialize this class before use. The simplest way is to just do {#code
 * Fresco.initialize(Context)}.
 */
public class Fresco {

  private static final Class<?> TAG = Fresco.class;

  private static PipelineDraweeControllerBuilderSupplier sDraweeControllerBuilderSupplier;
  private static volatile boolean sIsInitialized = false;

  private Fresco() {}

  /** Initializes Fresco with the default config. */
  public static void initialize(Context context) {
    initialize(context, null, null);
  }

  /** Initializes Fresco with the default Drawee config. */
  public static void initialize(
      Context context, @Nullable ImagePipelineConfig imagePipelineConfig) {
    initialize(context, imagePipelineConfig, null);
  }

  /** Initializes Fresco with the specified config and native code enabled. */
  public static void initialize(
      Context context,
      @Nullable ImagePipelineConfig imagePipelineConfig,
      @Nullable DraweeConfig draweeConfig) {
    initialize(context, imagePipelineConfig, draweeConfig, true);
  }

  /** Initializes Fresco with the specified config. */
  public static void initialize(
      Context context,
      @Nullable ImagePipelineConfig imagePipelineConfig,
      @Nullable DraweeConfig draweeConfig,
      boolean useNativeCode) {
    if (FrescoSystrace.isTracing()) {
      FrescoSystrace.beginSection("Fresco#initialize");
    }
    if (sIsInitialized) {
      FLog.w(
          TAG,
          "Fresco has already been initialized! `Fresco.initialize(...)` should only be called "
              + "1 single time to avoid memory leaks!");
    } else {
      sIsInitialized = true;
    }

    NativeCodeSetup.setUseNativeCode(useNativeCode);

    if (!NativeLoader.isInitialized()) {
      if (FrescoSystrace.isTracing()) {
        FrescoSystrace.beginSection("Fresco.initialize->SoLoader.init");
      }
      try {
        Class<?> clazz =
            Class.forName("com.facebook.imagepipeline.nativecode.NativeCodeInitializer");
        clazz.getMethod("init", Context.class).invoke(null, context);
      } catch (ClassNotFoundException e) {
        // Failed to initialize SoLoader
        NativeLoader.init(new SystemDelegate());
      } catch (IllegalAccessException e) {
        // Failed to initialize SoLoader
        NativeLoader.init(new SystemDelegate());
      } catch (InvocationTargetException e) {
        // Failed to initialize SoLoader
        NativeLoader.init(new SystemDelegate());
      } catch (NoSuchMethodException e) {
        // Failed to initialize SoLoader
        NativeLoader.init(new SystemDelegate());
      } finally {
        if (FrescoSystrace.isTracing()) {
          FrescoSystrace.endSection();
        }
      }
    }
    // we should always use the application context to avoid memory leaks
    context = context.getApplicationContext();
    if (imagePipelineConfig == null) {
      ImagePipelineFactory.initialize(context);
    } else {
      ImagePipelineFactory.initialize(imagePipelineConfig);
    }
    initializeDrawee(context, draweeConfig);
    if (FrescoSystrace.isTracing()) {
      FrescoSystrace.endSection();
    }
  }

  /** Initializes Drawee with the specified config. */
  private static void initializeDrawee(Context context, @Nullable DraweeConfig draweeConfig) {
    if (FrescoSystrace.isTracing()) {
      FrescoSystrace.beginSection("Fresco.initializeDrawee");
    }
    sDraweeControllerBuilderSupplier =
        new PipelineDraweeControllerBuilderSupplier(context, draweeConfig);
    SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
    if (FrescoSystrace.isTracing()) {
      FrescoSystrace.endSection();
    }
  }

  /** Gets the supplier of Fresco Drawee controller builders. */
  public static PipelineDraweeControllerBuilderSupplier getDraweeControllerBuilderSupplier() {
    return sDraweeControllerBuilderSupplier;
  }

  /** Returns a new instance of Fresco Drawee controller builder. */
  public static PipelineDraweeControllerBuilder newDraweeControllerBuilder() {
    return sDraweeControllerBuilderSupplier.get();
  }

  public static ImagePipelineFactory getImagePipelineFactory() {
    return ImagePipelineFactory.getInstance();
  }

  /** Gets the image pipeline instance. */
  public static ImagePipeline getImagePipeline() {
    return getImagePipelineFactory().getImagePipeline();
  }

  /** Shuts Fresco down. */
  public static void shutDown() {
    sDraweeControllerBuilderSupplier = null;
    SimpleDraweeView.shutDown();
    ImagePipelineFactory.shutDown();
  }

  /** Returns true if Fresco has been initialized. */
  public static boolean hasBeenInitialized() {
    return sIsInitialized;
  }
}

@@ -3023,32 +3023,42 @@
 oLoader%0A        
+initialize
 NativeLoader.ini
@@ -3049,34 +3049,28 @@
 NativeLoader
-.init(new 
+With
 SystemDelega
@@ -3065,33 +3065,32 @@
 SystemDelegate()
-)
 ;%0A      %7D catch 
@@ -3159,32 +3159,42 @@
 oLoader%0A        
+initialize
 NativeLoader.ini
@@ -3185,34 +3185,28 @@
 NativeLoader
-.init(new 
+With
 SystemDelega
@@ -3201,33 +3201,32 @@
 SystemDelegate()
-)
 ;%0A      %7D catch 
@@ -3298,32 +3298,42 @@
 oLoader%0A        
+initialize
 NativeLoader.ini
@@ -3324,34 +3324,28 @@
 NativeLoader
-.init(new 
+With
 SystemDelega
@@ -3340,33 +3340,32 @@
 SystemDelegate()
-)
 ;%0A      %7D catch 
@@ -3437,24 +3437,34 @@
 der%0A        
+initialize
 NativeLoader
@@ -3467,18 +3467,12 @@
 ader
-.init(new 
+With
 Syst
@@ -3483,17 +3483,16 @@
 legate()
-)
 ;%0A      
@@ -3970,32 +3970,32 @@
 .isTracing()) %7B%0A
-
       FrescoSyst
@@ -4020,24 +4020,242 @@
 %0A    %7D%0A  %7D%0A%0A
+  private static void initializeNativeLoaderWithSystemDelegate() %7B%0A    synchronized (NativeLoader.class) %7B%0A      if (!NativeLoader.isInitialized()) %7B%0A        NativeLoader.init(new SystemDelegate());%0A      %7D%0A    %7D%0A  %7D%0A%0A
   /** Initia

Add synchronized to help prevent race condition with fresco client code (#2579)