text: // Copyright 2018 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "src/ui/scenic/lib/input/pointer_event_buffer.h"
  
  namespace scenic_impl {
  namespace input {
  
  using AccessibilityPointerEvent = fuchsia::ui::input::accessibility::PointerEvent;
  
  PointerEventBuffer::PointerEventBuffer(DispatchEventFunction dispatch_events,
                                         ReportAccessibilityEventFunction report_to_accessibility)
      : dispatch_events_(std::move(dispatch_events)),
        report_to_accessibility_(report_to_accessibility) {}
  
  PointerEventBuffer::~PointerEventBuffer() {
    // Any remaining pointer events are dispatched to clients to keep a consistent state.
    for (auto& pointer_id_and_streams : buffer_) {
      for (auto& stream : pointer_id_and_streams.second) {
        for (auto& deferred_events : stream.serial_events) {
          dispatch_events_(std::move(deferred_events));
        }
      }
    }
  }
  
  void PointerEventBuffer::UpdateStream(uint32_t pointer_id,
                                        fuchsia::ui::input::accessibility::EventHandling handled) {
    auto it = buffer_.find(pointer_id);
    if (it == buffer_.end()) {
      // Empty buffer for this pointer id. Simply return.
      return;
    }
    auto& pointer_id_buffer = it->second;
    if (pointer_id_buffer.empty()) {
      // there are no streams left.
      return;
    }
    auto& stream = pointer_id_buffer.front();
    PointerIdStreamStatus status = PointerIdStreamStatus::WAITING_RESPONSE;
    switch (handled) {
      case fuchsia::ui::input::accessibility::EventHandling::CONSUMED:
        status = PointerIdStreamStatus::CONSUMED;
        break;
      case fuchsia::ui::input::accessibility::EventHandling::REJECTED:
        // the accessibility listener rejected this stream of pointer events
        // related to this pointer id. They follow their normal flow and are
        // sent to views. All buffered (past events), are sent, as well as
        // potential future (in case this stream is not done yet).
        status = PointerIdStreamStatus::REJECTED;
        for (auto& deferred_events : stream.serial_events) {
          dispatch_events_(std::move(deferred_events));
        }
        // Clears the stream -- objects have been moved, but container still holds
        // their space.
        stream.serial_events.clear();
        break;
    };
    // Remove this stream from the buffer, as it was already processed.
    pointer_id_buffer.pop_front();
    // If the buffer is now empty, this means that this stream hasn't finished
    // yet. Record this so that incoming future pointer events know where to go.
    // Please note that if the buffer is not empty, this means that there are
    // streams waiting for a response, thus, this is not the active stream
    // anymore. If this is the case, |active_stream_info_| will not be updated
    // and thus will still have a status of WAITING_RESPONSE.
    if (pointer_id_buffer.empty()) {
      SetActiveStreamInfo(pointer_id, status);
    }
    FXL_DCHECK(pointer_id_buffer.empty() ||
               active_stream_info_[pointer_id] == PointerIdStreamStatus::WAITING_RESPONSE)
        << "invariant: streams are waiting, so status is waiting";
  }
  
  void PointerEventBuffer::AddEvent(uint32_t pointer_id, DeferredPointerEvent views_and_event,
                                    AccessibilityPointerEvent accessibility_pointer_event) {
    auto it = active_stream_info_.find(pointer_id);
    FXL_DCHECK(it != active_stream_info_.end()) << "Received an invalid pointer id.";
    const auto status = it->second;
    if (status == PointerIdStreamStatus::WAITING_RESPONSE) {
      PointerIdStream& stream = buffer_[pointer_id].back();
      stream.serial_events.emplace_back(std::move(views_and_event));
    } else if (status == PointerIdStreamStatus::REJECTED) {
      // All previous events were already dispatched when this stream was
      // rejected. Sends this new incoming events to their normal flow as well.
      // There is still the possibility of triggering a focus change event, when
      // ADD -> a11y listener rejected -> DOWN event arrived.
      dispatch_events_(std::move(views_and_event));
      return;
    }
    // PointerIdStreamStatus::CONSUMED or PointerIdStreamStatus::WAITING_RESPONSE
    // follow the same path: accessibility listener needs to see the pointer event
    // to consume / decide if it will consume them.
    if (status == PointerIdStreamStatus::WAITING_RESPONSE ||
        status == PointerIdStreamStatus::CONSUMED) {
      report_to_accessibility_(std::move(accessibility_pointer_event));
    }
  }
  
  void PointerEventBuffer::AddStream(uint32_t pointer_id) {
    auto& pointer_id_buffer = buffer_[pointer_id];
    pointer_id_buffer.emplace_back();
    active_stream_info_[pointer_id] = PointerIdStreamStatus::WAITING_RESPONSE;
  }
  
  }  // namespace input
  }  // namespace scenic_impl
  