use std::io::Stdout;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use crossterm::event::{Event as CtEvent, EventStream, KeyEventKind};
use futures::StreamExt;
use ratatui::Terminal;
use ratatui::backend::CrosstermBackend;
use tokio::sync::mpsc;
use super::app::{App, AppMessage};
use super::qa_watcher::QAWatcher;
use super::ui::draw;
const TICK_RATE: Duration = Duration::from_millis(100);
pub type QASubmitCallback = Box<dyn FnMut(u32, &super::qa_protocol::QAnswersFile) + Send>;
pub async fn run_event_loop(
terminal: &mut Terminal<CrosstermBackend<Stdout>>,
app: &mut App,
engine_rx: mpsc::UnboundedReceiver<AppMessage>,
mut qa_watcher: Option<QAWatcher>,
mut on_qa_submit: Option<QASubmitCallback>,
run_id_holder: Arc<std::sync::RwLock<Option<String>>>,
project_root: String,
) -> std::io::Result<()> {
let mut reader = EventStream::new();
let mut tick_interval = tokio::time::interval(TICK_RATE);
let mut qa_poll_interval = tokio::time::interval(Duration::from_millis(500));
let mut engine_rx = Some(engine_rx);
terminal.draw(|frame| draw(frame, app))?;
loop {
if qa_watcher.is_none()
&& let Ok(guard) = run_id_holder.read()
&& let Some(ref run_id) = *guard
{
let run_state_dir = PathBuf::from(format!("{}/.7/runs/{}", project_root, run_id));
let watcher = QAWatcher::new(run_state_dir);
let _ = watcher.ensure_qa_dir();
qa_watcher = Some(watcher);
}
tokio::select! {
maybe_event = reader.next() => {
match maybe_event {
Some(Ok(CtEvent::Key(key))) => {
if key.kind == KeyEventKind::Press {
app.update(AppMessage::KeyPress(key));
}
}
Some(Ok(CtEvent::Resize(_, _))) => {
}
Some(Err(_)) => {
break;
}
None => {
break;
}
_ => {
}
}
}
Some(maybe_msg) = async {
match engine_rx.as_mut() {
Some(rx) => Some(rx.recv().await),
None => None,
}
} => {
match maybe_msg {
Some(msg) => {
app.update(msg);
}
None => {
engine_rx = None;
app.update(AppMessage::EngineFinished);
}
}
}
_ = qa_poll_interval.tick(), if qa_watcher.is_some() => {
if let Some(ref mut watcher) = qa_watcher
&& let Some((seq, qout)) = watcher.poll_for_questions()
{
app.update(AppMessage::QuestionsDetected(seq, qout));
}
}
_ = tick_interval.tick() => {
app.update(AppMessage::Tick);
}
}
if app.qa_submit_pending {
app.qa_submit_pending = false;
if let Some((seq, answers_file)) = app.take_qa_submit_data() {
if let Some(ref mut callback) = on_qa_submit {
callback(seq, &answers_file);
}
if let Some(ref mut watcher) = qa_watcher {
watcher.mark_answered(seq);
}
}
}
terminal.draw(|frame| draw(frame, app))?;
if app.should_quit {
break;
}
}
Ok(())
}