use std::borrow::{BorrowMut, Cow};
use std::sync::{Arc, Mutex};
use ratatui::layout::Rect;
use ratatui::style::{Color, Style};
use ratatui::text::Line;
use ratatui::widgets::{Paragraph, Scrollbar, ScrollbarOrientation};
use tracing::Level;
use crate::global_state::GlobalState;
use crate::logging::SharedLogBuffer;
use crate::types::tui_state::TuiState;
use super::{wrap_string, Theme};
pub fn draw(
f: &mut ratatui::Frame,
_global_state: Arc<GlobalState>,
tui_state: &mut TuiState,
log_buffer: &Arc<Mutex<SharedLogBuffer>>,
area: Rect,
theme: &Theme
) {
let size = area.as_size();
if size.height < 10 || size.width < 10 {
return
}
let mut buffer = log_buffer.lock().expect("locking shared buffer mutex should always work");
if tui_state.log_tab_stage.scroll_state.vertical_scroll.is_none() && buffer.limit.is_none() {
let l = buffer.limit.borrow_mut();
*l = Some(500);
} else if tui_state.log_tab_stage.scroll_state.vertical_scroll.is_some() && buffer.limit.is_some() {
let l = buffer.limit.borrow_mut();
*l = None;
}
let max_msg_width = area.width;
let item_count_len = buffer.logs.len().to_string().len().max(6);
let is_dark_theme = matches!(&theme,Theme::Dark(_));
let s = |level|if is_dark_theme {
match level {
Level::ERROR => Style::default().fg(Color::Red),
Level::TRACE => Style::default().fg(Color::Gray),
Level::DEBUG => Style::default().fg(Color::Magenta),
Level::WARN => Style::default().fg(Color::Yellow),
Level::INFO => Style::default().fg(Color::Blue),
}
} else {
match level {
Level::ERROR => Style::default().fg(Color::Red),
Level::TRACE => Style::default().fg(Color::Black),
Level::DEBUG => Style::default().fg(Color::Black),
Level::WARN => Style::default().fg(Color::Magenta),
Level::INFO => Style::default().fg(Color::Blue),
}
};
let fg_s = if is_dark_theme { Style::default().fg(Color::Gray) } else { Style::default().fg(Color::Black) };
let items: Vec<Line> =
buffer.logs.iter_mut().enumerate().flat_map(|(i, x)| {
let level = x.lvl;
let nr_str = format!("{:1$} | ", i + 1, item_count_len);
let lvl_str = format!("{:>1$} ", x.lvl.as_str(), 5);
let thread_str = if let Some(n) = &x.thread {
format!("{n} ")
} else {
("").into()
};
let max_width = (max_msg_width as usize)
.saturating_sub(8)
.saturating_sub(nr_str.len() + lvl_str.len() + thread_str.len());
if x.msg.len() > max_width {
wrap_string(x.msg.as_str(), max_width)
.into_iter().enumerate().map(|(i, m)| {
let level_span = if i == 0 {
ratatui::text::Span::styled(lvl_str.clone(), s(level))
} else {
ratatui::text::Span::styled(
Cow::from(" ".repeat(lvl_str.len())),
Style::default(),
)
};
Line::from(vec![
ratatui::text::Span::styled(nr_str.to_string(), fg_s),
level_span,
ratatui::text::Span::styled(thread_str.to_string(), fg_s),
ratatui::text::Span::styled(m.clone(), fg_s),
])
}).collect::<Vec<Line>>()
} else {
let message = ratatui::text::Span::styled(
format!("{} {}", &x.src, &x.msg),
fg_s,
);
vec![Line::from(vec![
ratatui::text::Span::styled(nr_str, fg_s),
ratatui::text::Span::styled(lvl_str, s(level)),
ratatui::text::Span::styled(thread_str, fg_s),
message,
])]
}
}).collect();
let wrapped_line_count = items.len();
tui_state.log_tab_stage.scroll_state.total_rows = wrapped_line_count;
let height_of_logs_area = area.height.saturating_sub(0); tui_state.log_tab_stage.scroll_state.area_height = height_of_logs_area as usize;
tui_state.log_tab_stage.scroll_state.area_width = area.width as usize;
let scroll_pos = { tui_state.log_tab_stage.scroll_state.vertical_scroll };
let scrollbar_hovered = tui_state.log_tab_stage.scroll_state.scroll_bar_hovered;
let max_scroll_pos = items.len().saturating_sub(height_of_logs_area as usize);
let visible_rows = area.height as usize;
let start = scroll_pos.unwrap_or(max_scroll_pos);
let end = std::cmp::min(start + visible_rows, items.len());
if start > items.len() || end > items.len() || start >= end {
return
}
let display_rows = &items[start..end];
let clamped_items : Vec<Line> = display_rows.iter().map(|x| {
x.clone()
}).collect();
let paragraph = Paragraph::new(clamped_items);
let mut scrollbar = Scrollbar::default()
.style( Style::default())
.orientation(ScrollbarOrientation::VerticalRight)
.begin_symbol(Some("↑"))
.end_symbol(Some("↓")).thumb_style(Style::new().fg(Color::LightBlue));
if scrollbar_hovered {
scrollbar = scrollbar.thumb_style(Style::default().fg(Color::Yellow).bg(Color::Red));
}
let mut scrollbar_state = tui_state.log_tab_stage.scroll_state.vertical_scroll_state.borrow_mut();
*scrollbar_state = scrollbar_state.content_length(items.len().saturating_sub(height_of_logs_area as usize));
if scroll_pos.is_none() {
*scrollbar_state = scrollbar_state.position(items.len().saturating_sub(height_of_logs_area as usize));
}
f.render_widget(paragraph, area);
f.render_stateful_widget(scrollbar,area, &mut scrollbar_state);
}