// @flow

import React, { useCallback, useContext, useMemo } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import { FixedSizeList } from 'react-window';
import { ProfilerContext } from './ProfilerContext';
import NoCommitData from './NoCommitData';
import CommitFlamegraphListItem from './CommitFlamegraphListItem';
import { barHeight } from './constants';
import { scale } from './utils';
import { StoreContext } from '../context';

import styles from './CommitFlamegraph.css';

import type { ChartData, ChartNode } from './FlamegraphChartBuilder';

export type ItemData = {|
  chartData: ChartData,
  scaleX: (value: number, fallbackValue: number) => number,
  selectedChartNode: ChartNode,
  selectedChartNodeIndex: number,
  selectFiber: (id: number | null) => void,
  width: number,
|};

export default function CommitFlamegraphAutoSizer(_: {||}) {
  const { selectFiber } = useContext(ProfilerContext);
  const deselectCurrentFiber = useCallback(
    event => {
      event.stopPropagation();
      selectFiber(null);
    },
    [selectFiber]
  );

  return (
    <div className={styles.Container} onClick={deselectCurrentFiber}>
      <AutoSizer>
        {({ height, width }) => (
          <CommitFlamegraph height={height} width={width} />
        )}
      </AutoSizer>
    </div>
  );
}

function CommitFlamegraph({
  height,
  width,
}: {|
  height: number,
  width: number,
|}) {
  const {
    rendererID,
    rootID,
    selectedCommitIndex,
    selectFiber,
    selectedFiberID,
  } = useContext(ProfilerContext);

  const { profilingCache } = useContext(StoreContext);

  const profilingSummary = profilingCache.ProfilingSummary.read({
    rendererID: ((rendererID: any): number),
    rootID: ((rootID: any): number),
  });

  const commitDetails = profilingCache.CommitDetails.read({
    commitIndex: ((selectedCommitIndex: any): number),
    rendererID: ((rendererID: any): number),
    rootID: ((rootID: any): number),
  });

  const commitTree = profilingCache.getCommitTree({
    commitIndex: ((selectedCommitIndex: any): number),
    profilingSummary,
    rendererID: ((rendererID: any): number),
    rootID: ((rootID: any): number),
  });

  const chartData = profilingCache.getFlamegraphChartData({
    commitDetails,
    commitIndex: ((selectedCommitIndex: any): number),
    commitTree,
    rootID: ((rootID: any): number),
  });

  const selectedChartNodeIndex = useMemo(
    () =>
      selectedFiberID === null
        ? 0
        : ((chartData.idToDepthMap.get(selectedFiberID): any): number) - 1,
    [chartData, selectedFiberID]
  );

  const selectedChartNode = useMemo(() => {
    if (selectedFiberID === null) {
      return chartData.rows[0][0];
    }
    return ((chartData.rows[selectedChartNodeIndex].find(
      chartNode => chartNode.id === selectedFiberID
    ): any): ChartNode);
  }, [chartData, selectedFiberID, selectedChartNodeIndex]);

  const itemData = useMemo<ItemData>(
    () => ({
      chartData,
      scaleX: scale(0, selectedChartNode.treeBaseDuration, 0, width),
      selectedChartNode,
      selectedChartNodeIndex,
      selectFiber,
      width,
    }),
    [chartData, selectedChartNode, selectedChartNodeIndex, selectFiber, width]
  );

  // If a commit contains no fibers with an actualDuration > 0,
  // Display a fallback message.
  if (chartData.depth === 0) {
    return <NoCommitData />;
  }

  return (
    <FixedSizeList
      height={height}
      innerElementType="svg"
      itemCount={chartData.depth}
      itemData={itemData}
      itemSize={barHeight}
      width={width}
    >
      {CommitFlamegraphListItem}
    </FixedSizeList>
  );
}

@@ -2415,26 +2415,33 @@
 Memo
-(%0A    
+%3Cnumber%3E(
 () =%3E
+ %7B
 %0A    
-  
+if (
 sele
@@ -2464,100 +2464,271 @@
 null
+) %7B
 %0A      
-  ? 0%0A        : ((chartData.idToDepthMap.get(selectedFiberID): any): number) - 1,%0A   
+return 0;%0A    %7D%0A    // The selected node might not be in the tree for this commit,%0A    // so it's important that we have a fallback plan.%0A    const depth = chartData.idToDepthMap.get(selectedFiberID);%0A    return depth !== undefined ? depth - 1 : 0;%0A  %7D,
  %5Bch
@@ -2752,19 +2752,16 @@
 FiberID%5D
-%0A  
 );%0A%0A  co
@@ -2792,32 +2792,58 @@
 useMemo(() =%3E %7B%0A
+    let chartNode = null;%0A
     if (selected
@@ -2842,33 +2842,33 @@
 selectedFiberID 
-=
+!
 == null) %7B%0A     
@@ -2872,53 +2872,19 @@
     
-return chartData.rows%5B0%5D%5B0%5D;%0A    %7D%0A    return
+chartNode =
  ((c
@@ -2929,24 +2929,26 @@
 find(%0A      
+  
 chartNode =%3E
@@ -2985,16 +2985,18 @@
 rID%0A    
+  
 ): any):
@@ -3008,16 +3008,236 @@
 tNode);%0A
+    %7D%0A    // The selected node might not be in the tree for this commit,%0A    // so it's important that we have a fallback plan.%0A    if (chartNode == null) %7B%0A      return chartData.rows%5B0%5D%5B0%5D;%0A    %7D%0A    return chartNode;%0A
   %7D, %5Bch

Handle RTE in profiler when commit does not contain selected node