/*
 * 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.flipper.plugins.inspector.descriptors.utils;

import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.util.Log;
import android.util.TypedValue;
import com.facebook.flipper.core.FlipperObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public final class ContextDescriptorUtils {
  private static String TAG = "ContextDescriptor";

  private static Field sThemeImplField;
  private static Field sThemeImplThemeKeyField;
  private static Field sThemeImplAssetManagerField;
  private static Field sThemeKeyResIdField;
  private static Method sAssetManagerGetStyleAttributesMethod;

  private ContextDescriptorUtils() {}

  public static FlipperObject themeData(Context context) {
    FlipperObject.Builder themeData = new FlipperObject.Builder();
    Map<String, FlipperObject.Builder> builders = collectThemeValues(context);
    for (Map.Entry<String, FlipperObject.Builder> entry : builders.entrySet()) {
      themeData.put(entry.getKey(), entry.getValue().build());
    }
    return themeData.build();
  }

  private static Map<String, FlipperObject.Builder> collectThemeValues(Context context) {
    Map<String, FlipperObject.Builder> builderMap = new HashMap<>(3);
    try {
      Resources.Theme theme = context.getTheme();
      AssetManager assetManager = context.getAssets();
      final Object themeImpl;
      final Object themeKey;

      // Nasty reflection to get a list of theme attributes that apply to this context.
      if (sThemeImplField == null
          || sThemeImplThemeKeyField == null
          || sThemeKeyResIdField == null
          || sThemeImplAssetManagerField == null) {
        sThemeImplField = theme.getClass().getDeclaredField("mThemeImpl");
        sThemeImplField.setAccessible(true);

        themeImpl = sThemeImplField.get(theme);
        sThemeImplThemeKeyField = themeImpl.getClass().getDeclaredField("mKey");
        sThemeImplThemeKeyField.setAccessible(true);

        sThemeImplAssetManagerField = themeImpl.getClass().getDeclaredField("mAssets");
        sThemeImplAssetManagerField.setAccessible(true);

        sAssetManagerGetStyleAttributesMethod =
            assetManager.getClass().getDeclaredMethod("getStyleAttributes", int.class);
        sAssetManagerGetStyleAttributesMethod.setAccessible(true);

        themeKey = sThemeImplThemeKeyField.get(themeImpl);
        sThemeKeyResIdField = themeKey.getClass().getDeclaredField("mResId");
        sThemeKeyResIdField.setAccessible(true);
      } else {
        themeImpl = sThemeImplField.get(theme);
        themeKey = sThemeImplThemeKeyField.get(themeImpl);
      }

      int[] appliedThemeResIds = (int[]) sThemeKeyResIdField.get(themeKey);
      TypedValue typedValue = new TypedValue();
      Resources resources = context.getResources();
      for (int themeId : appliedThemeResIds) {
        String name = resources.getResourceName(themeId);
        // The res id array can have duplicates
        if (builderMap.containsKey(name)) {
          continue;
        }

        FlipperObject.Builder builder = new FlipperObject.Builder();
        builderMap.put(name, builder);

        int[] attributes =
            (int[]) sAssetManagerGetStyleAttributesMethod.invoke(assetManager, themeId);
        for (int attribute : attributes) {
          if (!theme.resolveAttribute(attribute, typedValue, true)) {
            continue;
          }
          String attributeName = context.getResources().getResourceName(attribute);
          String[] nameParts = attributeName.split(":");
          if (nameParts.length < 2) {
            Log.d(TAG, "Unknown attribute name format " + attributeName);
          } else {
            attributeName = nameParts[1].split("/")[1];
          }
          String strValue = TypedValue.coerceToString(typedValue.type, typedValue.data);
          if (strValue == null) {
            strValue = "null";
          } else if (strValue.startsWith("@")) {
            int resId = Integer.parseInt(strValue.substring(1));
            if (resId == 0) {
              strValue = "null";
            } else {
              strValue = context.getResources().getResourceName(resId);
            }
          }
          builder.put(attributeName, strValue);
        }
      }
    } catch (Throwable ignored) {
      Log.d(TAG, "Failed to generate theme attribute data!", ignored);
    }
    return builderMap;
  }
}

@@ -1947,23 +1947,198 @@
 null) %7B%0A
+%0A
        
+ // Early exiting as this bit of code causes too much of the metadata to be created and%0A        // ultimately leads to out of memory exception. Reference D20441839%0A        //
  sThemeI
@@ -2196,32 +2196,35 @@
 eImpl%22);%0A       
+ //
  sThemeImplField
@@ -2245,32 +2245,35 @@
 (true);%0A%0A       
+ //
  themeImpl = sTh
@@ -2296,32 +2296,35 @@
 (theme);%0A       
+ //
  sThemeImplTheme
@@ -2380,32 +2380,35 @@
 %22mKey%22);%0A       
+ //
  sThemeImplTheme
@@ -2437,32 +2437,35 @@
 (true);%0A%0A       
+ //
  sThemeImplAsset
@@ -2532,24 +2532,27 @@
 s%22);%0A       
+ //
  sThemeImplA
@@ -2589,32 +2589,35 @@
 (true);%0A%0A       
+ //
  sAssetManagerGe
@@ -2644,24 +2644,27 @@
 od =%0A       
+ //
      assetMa
@@ -2739,16 +2739,19 @@
 %0A       
+ //
  sAssetM
@@ -2802,32 +2802,35 @@
 (true);%0A%0A       
+ //
  themeKey = sThe
@@ -2864,32 +2864,35 @@
 meImpl);%0A       
+ //
  sThemeKeyResIdF
@@ -2953,16 +2953,19 @@
 %0A       
+ //
  sThemeK
@@ -2994,24 +2994,53 @@
 ible(true);%0A
+%0A        return builderMap;%0A%0A
       %7D else
@@ -3377,24 +3377,82 @@
 meResIds) %7B%0A
+        if (themeId == 0) %7B%0A          continue;%0A        %7D%0A
         Stri

Fix the broken export of layout plugin in android(not easily reproducible)