/*
 * 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.sections;

import com.facebook.flipper.core.FlipperArray;
import com.facebook.flipper.core.FlipperObject;
import com.facebook.litho.sections.ChangesInfo;
import com.facebook.litho.sections.ChangesetDebugConfiguration;
import com.facebook.litho.sections.ChangesetDebugConfiguration.ChangesetDebugListener;
import com.facebook.litho.sections.Section;
import com.facebook.litho.sections.SectionsLogEventUtils;
import com.facebook.litho.sections.SectionsLogEventUtils.ApplyNewChangeSet;
import java.util.concurrent.atomic.AtomicInteger;

public class ChangesetDebug implements ChangesetDebugListener {

  private static ChangesetListener sSectionsFlipperPlugin;
  private static ChangesetDebug sInstance;
  private static AtomicInteger sChangesetIdGenerator = new AtomicInteger();

  public interface ChangesetListener {
    void onChangesetApplied(
        String name,
        boolean isAsync,
        String surfaceId,
        String id,
        FlipperArray tree,
        FlipperObject changesetData);
  }

  public static void setListener(ChangesetListener listener) {
    if (sInstance == null) {
      sInstance = new ChangesetDebug(listener);
      ChangesetDebugConfiguration.setListener(sInstance);
    }
  }

  private ChangesetDebug(ChangesetListener listener) {
    sSectionsFlipperPlugin = listener;
  }

  @Override
  public void onChangesetApplied(
      Section rootSection,
      Section oldSection,
      ChangesInfo changesInfo,
      String surfaceId,
      @ApplyNewChangeSet int attribution,
      String extra) {
    final FlipperArray.Builder tree = new FlipperArray.Builder();
    final FlipperObject.Builder changesetData = new FlipperObject.Builder();

    final String sourceName = SectionsLogEventUtils.applyNewChangeSetSourceToString(attribution);

    sSectionsFlipperPlugin.onChangesetApplied(
        sourceName + " " + extra,
        isEventAsync(attribution),
        surfaceId,
        sChangesetIdGenerator.incrementAndGet() + "-" + surfaceId,
        tree.build(),
        changesetData.build());
  }

  private static boolean isEventAsync(@ApplyNewChangeSet int source) {
    switch (source) {
      case ApplyNewChangeSet.SET_ROOT_ASYNC:
      case ApplyNewChangeSet.UPDATE_STATE_ASYNC:
        return true;
      default:
        return false;
    }
  }
}

@@ -1990,24 +1990,83 @@
 ribution);%0A%0A
+    createSectionTree(rootSection, %22%22, tree, oldSection);%0A%0A
     sSection
@@ -2568,11 +2568,1810 @@
    %7D%0A  %7D
+%0A%0A  /** Finds the section with the same global key in the previous tree, if it existed. */%0A  private static Section findSectionInPreviousTree(Section previousRoot, String globalKey) %7B%0A    if (previousRoot == null) %7B%0A      return null;%0A    %7D%0A%0A    if (previousRoot.getGlobalKey().equals(globalKey)) %7B%0A      return previousRoot;%0A    %7D%0A%0A    if (previousRoot.getChildren() == null) %7B%0A      return null;%0A    %7D%0A%0A    int count = previousRoot.getChildren().size();%0A    for (int i = 0; i %3C count; i++) %7B%0A      Section child = previousRoot.getChildren().get(i);%0A      final Section previousSection = findSectionInPreviousTree(child, globalKey);%0A%0A      if (previousSection != null) %7B%0A        return previousSection;%0A      %7D%0A    %7D%0A%0A    return null;%0A  %7D%0A%0A  static void createSectionTree(%0A      Section rootSection, String parentKey, FlipperArray.Builder tree, Section oldRootSection) %7B%0A    if (rootSection == null) %7B%0A      return;%0A    %7D%0A%0A    final String globalKey = rootSection.getGlobalKey();%0A    final FlipperObject.Builder nodeBuilder = new FlipperObject.Builder();%0A%0A    final Section oldSection = findSectionInPreviousTree(oldRootSection, globalKey);%0A    final boolean isDirty = ChangesetDebugConfiguration.isSectionDirty(oldSection, rootSection);%0A%0A    nodeBuilder.put(%22identifier%22, globalKey);%0A    nodeBuilder.put(%22name%22, rootSection.getSimpleName());%0A    nodeBuilder.put(%22parent%22, parentKey);%0A    nodeBuilder.put(%22isDirty%22, isDirty);%0A    nodeBuilder.put(%22isReused%22, !isDirty);%0A    nodeBuilder.put(%22didTriggerStateUpdate%22, false); // TODO%0A    tree.put(nodeBuilder.build());%0A%0A    if (rootSection.getChildren() == null) %7B%0A      return;%0A    %7D%0A%0A    for (int i = 0; i %3C rootSection.getChildren().size(); i++) %7B%0A      createSectionTree(rootSection.getChildren().get(i), globalKey, tree, oldRootSection);%0A    %7D%0A  %7D
 %0A%7D%0A

Create SectionTree in plugin