// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/foundation.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/widgets.dart';

/// Provides an iterable that efficiently returns all the [Element]s
/// rooted at the given [Element]. See [CachingIterable] for details.
///
/// This function must be called again if the tree changes. You cannot
/// call this function once, then reuse the iterable after having
/// changed the state of the tree, because the iterable returned by
/// this function caches the results and only walks the tree once.
///
/// The same applies to any iterable obtained indirectly through this
/// one, for example the results of calling `where` on this iterable
/// are also cached.
Iterable<Element> collectAllElementsFrom(Element rootElement, {required bool skipOffstage}) {
  return CachingIterable<Element>(_DepthFirstElementTreeIterator(rootElement, !skipOffstage));
}

/// Provides an iterable that efficiently returns all the [SemanticsNode]s
/// rooted at the given [SemanticsNode]. See [CachingIterable] for details.
///
/// By default, this will traverse the semantics tree in semantic traversal
/// order, but the traversal order can be changed by passing in a different
/// value to `order`.
///
/// This function must be called again if the semantics change. You cannot call
/// this function once, then reuse the iterable after having changed the state
/// of the tree, because the iterable returned by this function caches the
/// results and only walks the tree once.
///
/// The same applies to any iterable obtained indirectly through this
/// one, for example the results of calling `where` on this iterable
/// are also cached.
Iterable<SemanticsNode> collectAllSemanticsNodesFrom(
  SemanticsNode root, {
  DebugSemanticsDumpOrder order = DebugSemanticsDumpOrder.traversalOrder,
}) {
  return CachingIterable<SemanticsNode>(_DepthFirstSemanticsTreeIterator(root, order));
}

/// Provides a recursive, efficient, depth first search of a tree.
///
/// This iterator executes a depth first search as an iterable, and iterates in
/// a left to right order:
///
///       1
///     /   \
///    2     3
///   / \   / \
///  4   5 6   7
///
/// Will iterate in order 2, 4, 5, 3, 6, 7. The given root element is not
/// included in the traversal.
abstract class _DepthFirstTreeIterator<ItemType> implements Iterator<ItemType> {
  _DepthFirstTreeIterator(ItemType root) {
    _fillStack(_collectChildren(root));
  }

  @override
  ItemType get current => _current!;
  late ItemType _current;

  final List<ItemType> _stack = <ItemType>[];

  @override
  bool moveNext() {
    if (_stack.isEmpty) {
      return false;
    }

    _current = _stack.removeLast();
    _fillStack(_collectChildren(_current));
    return true;
  }

  /// Fills the stack in such a way that the next element of a depth first
  /// traversal is easily and efficiently accessible when calling `moveNext`.
  void _fillStack(List<ItemType> children) {
    // We reverse the list of children so we don't have to do use expensive
    // `insert` or `remove` operations, and so the order of the traversal
    // is depth first when built lazily through the iterator.
    //
    // This is faster than `_stack.addAll(children.reversed)`, presumably since
    // we don't actually care about maintaining an iteration pointer.
    while (children.isNotEmpty) {
      _stack.add(children.removeLast());
    }
  }

  /// Collect the children from [root] in the order they are expected to traverse.
  List<ItemType> _collectChildren(ItemType root);
}

/// [Element.visitChildren] does not guarantee order, but does guarantee stable
/// order. This iterator also guarantees stable order, and iterates in a left
/// to right order:
///
///       1
///     /   \
///    2     3
///   / \   / \
///  4   5 6   7
///
/// Will iterate in order 2, 4, 5, 3, 6, 7.
///
/// Performance is important here because this class is on the critical path
/// for flutter_driver and package:integration_test performance tests.
/// Performance is measured in the all_elements_bench microbenchmark.
/// Any changes to this implementation should check the before and after numbers
/// on that benchmark to avoid regressions in general performance test overhead.
///
/// If we could use RTL order, we could save on performance, but numerous tests
/// have been written (and developers clearly expect) that LTR order will be
/// respected.
class _DepthFirstElementTreeIterator extends _DepthFirstTreeIterator<Element> {
  _DepthFirstElementTreeIterator(super.root, this.includeOffstage);

  final bool includeOffstage;

  @override
  List<Element> _collectChildren(Element root) {
    final children = <Element>[];
    if (includeOffstage) {
      root.visitChildren(children.add);
    } else {
      root.debugVisitOnstageChildren(children.add);
    }

    return children;
  }
}

/// Iterates the semantics tree starting at the given `root`.
///
/// This will iterate in the same order expected from accessibility services,
/// so the results can be used to simulate the same traversal the engine will
/// make. The results are not filtered based on flags or visibility, so they
/// will need to be further filtered to fully simulate an accessibility service.
class _DepthFirstSemanticsTreeIterator extends _DepthFirstTreeIterator<SemanticsNode> {
  _DepthFirstSemanticsTreeIterator(super.root, this.order);

  final DebugSemanticsDumpOrder order;

  @override
  List<SemanticsNode> _collectChildren(SemanticsNode root) {
    return root.debugListChildrenInOrder(order);
  }
}
