text: // Copyright (c) FIRST and other WPILib contributors.
  // Open Source Software; you can modify and/or share it under the terms of
  // the WPILib BSD license file in the root directory of this project.
  
  #include "frc/AsynchronousInterrupt.h"
  
  #include <frc/DigitalSource.h>
  
  using namespace frc;
  
  AsynchronousInterrupt::AsynchronousInterrupt(
      DigitalSource& source, std::function<void(bool, bool)> callback)
      : m_interrupt{source}, m_callback{std::move(callback)} {}
  AsynchronousInterrupt::AsynchronousInterrupt(
      DigitalSource* source, std::function<void(bool, bool)> callback)
      : m_interrupt{source}, m_callback{std::move(callback)} {}
  AsynchronousInterrupt::AsynchronousInterrupt(
      std::shared_ptr<DigitalSource> source,
      std::function<void(bool, bool)> callback)
      : m_interrupt{source}, m_callback{std::move(callback)} {}
  
  AsynchronousInterrupt::~AsynchronousInterrupt() {
    Disable();
  }
  
  void AsynchronousInterrupt::ThreadMain() {
    while (m_keepRunning) {
      auto result = m_interrupt.WaitForInterrupt(10_s, false);
      if (!m_keepRunning) {
        break;
      }
      if (result == SynchronousInterrupt::WaitResult::kTimeout) {
        continue;
      }
      m_callback((result & SynchronousInterrupt::WaitResult::kRisingEdge) != 0,
                 (result & SynchronousInterrupt::WaitResult::kFallingEdge) != 0);
    }
  }
  
  void AsynchronousInterrupt::Enable() {
    if (m_keepRunning) {
      return;
    }
  
    m_keepRunning = true;
    m_thread = std::thread([this] { this->ThreadMain(); });
  }
  
  void AsynchronousInterrupt::Disable() {
    m_keepRunning = false;
    m_interrupt.WakeupWaitingInterrupt();
    if (m_thread.joinable()) {
      m_thread.join();
    }
  }
  
  void AsynchronousInterrupt::SetInterruptEdges(bool risingEdge,
                                                bool fallingEdge) {
    m_interrupt.SetInterruptEdges(risingEdge, fallingEdge);
  }
  
  units::second_t AsynchronousInterrupt::GetRisingTimestamp() {
    return m_interrupt.GetRisingTimestamp();
  }
  units::second_t AsynchronousInterrupt::GetFallingTimestamp() {
    return m_interrupt.GetFallingTimestamp();
  }
  