// Copyright (c) 2025, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import '../../paths.dart';

/// Tree of event paths relative to the watched path.
///
/// If [isSingleEvent] then there is an event at the current path. Because
/// changed directories must be fully polled, events "under" the current path
/// are not useful, and are discarded from the tree.
class EventTree {
  Map<PathSegment, EventTree>? _events;

  EventTree() : _events = {};
  EventTree.singleEvent() : _events = null;

  /// Adds an event at [path].
  ///
  /// If there are already events under [path], that part of the tree has
  /// [isSingleEvent] set and events under it are discarded.
  ///
  /// If there is already an event for a parent of [path], it is discarded
  /// instead of added.
  void add(RelativePath path) {
    final segments = path.segments;
    var current = this;

    for (final segment in segments) {
      final events = current._events;
      if (events == null) {
        // There is already an event for a parent of [path], discard the
        // new event.
        return;
      }
      // Add to the tree for [segment].
      current = events.putIfAbsent(segment, EventTree.new);
    }

    // Mark [path] as a [singleEvent] and discard any events under it.
    current._events = null;
  }

  /// Whether this event tree is actually a single event.
  ///
  /// There can be no events under it; [entries] will throw.
  bool get isSingleEvent => _events == null;

  /// Returns the event tree at [segment], or `null` if there is none.
  EventTree? operator [](PathSegment segment) => _events?[segment];

  /// Returns child event trees by path segment.
  ///
  /// Throws if [isSingleEvent].
  Iterable<MapEntry<PathSegment, EventTree>> get entries => _events!.entries;

  @override
  String toString() => _events == null ? 'event' : '$_events';
}
