use crate::app::{AppState, PanelState};
use crate::ui::format::value_to_heatmap_color;
use ratatui::{
prelude::*,
widgets::{Block, Borders, Paragraph},
};
pub(super) fn render_heatmap(frame: &mut Frame, area: Rect, p: &PanelState, app: &AppState) {
let theme = &app.theme;
if p.series.is_empty() {
let para = Paragraph::new("No data").style(Style::default().fg(theme.text));
frame.render_widget(para, area);
return;
}
let rows_available = area.height.saturating_sub(2) as usize; let cols_available = area.width as usize;
if rows_available == 0 || cols_available == 0 {
return;
}
let visible_series: Vec<_> = p.series.iter().filter(|s| s.visible).collect();
if visible_series.is_empty() {
let para = Paragraph::new("No visible series").style(Style::default().fg(theme.text));
frame.render_widget(para, area);
return;
}
let (mut global_min, mut global_max) = (f64::MAX, f64::MIN);
for s in &visible_series {
for (_, v) in &s.points {
if v.is_finite() {
global_min = global_min.min(*v);
global_max = global_max.max(*v);
}
}
}
if !global_min.is_finite() || !global_max.is_finite() || global_min == global_max {
let para = Paragraph::new("Invalid data range").style(Style::default().fg(theme.text));
frame.render_widget(para, area);
return;
}
let mut lines = Vec::new();
for series in visible_series.iter().take(rows_available) {
let mut spans = Vec::new();
let total_points = series.points.len();
if total_points == 0 {
continue;
}
let step = (total_points as f64 / cols_available as f64).max(1.0);
for col_idx in 0..cols_available {
let point_idx = ((col_idx as f64 * step) as usize).min(total_points - 1);
let (_, value) = series.points[point_idx];
let color = if value.is_finite() {
let normalized = ((value - global_min) / (global_max - global_min)).clamp(0.0, 1.0);
value_to_heatmap_color(normalized)
} else {
Color::DarkGray
};
spans.push(Span::styled("█", Style::default().fg(color)));
}
lines.push(Line::from(spans));
}
if lines.is_empty() {
let para = Paragraph::new("No data to display").style(Style::default().fg(theme.text));
frame.render_widget(para, area);
return;
}
let heatmap_widget = Paragraph::new(lines).block(Block::default().borders(Borders::NONE));
frame.render_widget(heatmap_widget, area);
}