// Copyright (C) 2026 The Android Open Source Project
//
// Licensed 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.

import m from 'mithril';

import {
  type GridColumn,
  GridHeaderCell,
  Grid,
  GridCell,
} from '../../widgets/grid';
import type {TrackPinningManager} from '../../components/related_events/utils';
import {Icons} from '../../base/semantic_icons';
import type {duration} from '../../base/time';
import {DurationWidget} from '../../components/widgets/duration';
import type {Trace} from '../../public/trace';
import {Anchor} from '../../widgets/anchor';
import {Checkbox} from '../../widgets/checkbox';
import {EmptyState} from '../../widgets/empty_state';
import {DetailsShell} from '../../widgets/details_shell';
import {Spinner} from '../../widgets/spinner';

import type {InputChainRow, NavTarget} from './android_input_event_source';

export interface AndroidInputLifecycleTabAttrs {
  trace: Trace;
  rows: InputChainRow[];
  visibleRowIds: Set<string>;
  loading: boolean;
  pinningManager: TrackPinningManager;
  onToggleVisibility: (rowId: string) => void;
  onToggleAllVisibility: () => void;
}

export class AndroidInputLifecycleTab
  implements m.ClassComponent<AndroidInputLifecycleTabAttrs>
{
  view({attrs}: m.Vnode<AndroidInputLifecycleTabAttrs>): m.Children {
    if (attrs.loading) {
      return m(
        DetailsShell,
        {title: 'Android Input Lifecycle'},
        m(
          'div',
          {
            style: {
              display: 'flex',
              justifyContent: 'center',
              padding: '20px',
            },
          },
          m(Spinner, {}),
        ),
      );
    }

    return m(
      DetailsShell,
      {title: 'Android Input Lifecycle'},
      this.renderGrid(attrs),
    );
  }

  private renderGrid(attrs: AndroidInputLifecycleTabAttrs): m.Children {
    const {rows, visibleRowIds, trace, pinningManager} = attrs;
    const allVisible =
      rows.length > 0 && rows.every((r) => visibleRowIds.has(r.uiRowId));

    const columns: GridColumn[] = [
      {
        key: 'show',
        widthPx: 40,
        header: m(
          GridHeaderCell,
          {},
          m(Checkbox, {
            checked: allVisible,
            onchange: () => attrs.onToggleAllVisibility(),
          }),
        ),
      },
      {
        key: 'pin',
        widthPx: 40,
        header: m(GridHeaderCell, {}, 'Pin'),
      },
      {key: 'chan', header: m(GridHeaderCell, {}, 'Channel')},
      {
        key: 'total',
        minWidthPx: 100,
        header: m(GridHeaderCell, {}, 'Total Latency'),
      },
      {
        key: 'read',
        minWidthPx: 100,
        header: m(GridHeaderCell, {}, 'InputReader'),
      },
      {
        key: 'disp',
        minWidthPx: 100,
        header: m(GridHeaderCell, {}, 'Dispatcher'),
      },
      {
        key: 'recv',
        minWidthPx: 100,
        header: m(GridHeaderCell, {}, 'App Receive'),
      },
      {
        key: 'cons',
        minWidthPx: 100,
        header: m(GridHeaderCell, {}, 'App Consume'),
      },
      {
        key: 'frame',
        minWidthPx: 100,
        header: m(GridHeaderCell, {}, 'App Frame'),
      },
    ];

    return m(Grid, {
      columns,
      rowData: rows.map((row) => [
        m(
          GridCell,
          {},
          m(Checkbox, {
            checked: visibleRowIds.has(row.uiRowId),
            onchange: () => attrs.onToggleVisibility(row.uiRowId),
          }),
        ),
        m(
          GridCell,
          {},
          m(Checkbox, {
            checked: isRowPinned(row, pinningManager),
            onchange: () => togglePinning(row, pinningManager),
          }),
        ),
        m(GridCell, {}, row.channel),
        m(
          GridCell,
          {},
          row.totalLatency !== null
            ? m(DurationWidget, {dur: row.totalLatency, trace})
            : '-',
        ),
        renderCell(row.durReader, row.navReader, trace),
        renderCell(row.deltaDispatch, row.navDispatch, trace),
        renderCell(row.deltaReceive, row.navReceive, trace),
        renderCell(row.deltaConsume, row.navConsume, trace),
        renderCell(row.deltaFrame, row.navFrame, trace),
      ]),
      emptyState: m(EmptyState, {
        title: 'No input event selected',
        description: 'Select an input event to see latency breakdown.',
        icon: Icons.Android,
      }),
    });
  }
}

function isRowPinned(
  row: InputChainRow,
  pinningManager: TrackPinningManager,
): boolean {
  return (
    row.allTrackUris.length > 0 &&
    row.allTrackUris.every((uri) => pinningManager.isTrackPinned(uri))
  );
}

function togglePinning(
  row: InputChainRow,
  pinningManager: TrackPinningManager,
) {
  if (isRowPinned(row, pinningManager)) {
    pinningManager.unpinTracks(row.allTrackUris);
  } else {
    pinningManager.pinTracks(row.allTrackUris);
  }
}

function renderCell(
  dur: duration | null,
  nav: NavTarget | undefined,
  trace: Trace,
) {
  return m(
    GridCell,
    {},
    dur !== null ? m(DurationWidget, {dur, trace}) : m('span', '-'),
    nav !== undefined &&
      m(Anchor, {
        icon: Icons.GoTo,
        onclick: () => {
          trace.selection.selectTrackEvent(nav.trackUri, nav.id, {
            scrollToSelection: true,
            switchToCurrentSelectionTab: false,
          });
        },
        title: 'Go to event slice',
      }),
  );
}
