gled 2.28.5

gled is an application for creating animations and effects on artnet or dmx installations
use crate::midi::monitor;
use chrono::{Local, TimeZone};
use egui::Ui;
use egui_phosphor_icons::icons;
use std::collections::VecDeque;

use super::iconized;

pub(super) fn show_live_midi_monitor(
    ui: &mut Ui,
    events: &mut VecDeque<monitor::MidiMonitorEvent>,
    diagnostics: &[monitor::MidiPortDiagnostics],
) {
    ui.horizontal(|ui| {
        ui.label(icons::GAUGE);
        ui.heading("Diagnostics");
    });

    let diagnostics_height = ui.text_style_height(&egui::TextStyle::Body) * 3.0 + 8.0;
    egui::ScrollArea::vertical()
        .max_height(diagnostics_height)
        .id_salt("midi_diagnostics_scroll")
        .show(ui, |ui| {
            if diagnostics.is_empty() {
                ui.label("No MIDI ports observed yet.");
                return;
            }

            for diag in diagnostics {
                ui.label(format!(
                    "{} | in:{} out:{} | msgs:{} | in_fail:{} out_fail:{}",
                    diag.port_name,
                    if diag.input_connected { "yes" } else { "no" },
                    if diag.output_connected { "yes" } else { "no" },
                    diag.message_count,
                    diag.input_connect_failed,
                    diag.output_connect_failed
                ));
            }
        });
    ui.separator();

    ui.horizontal(|ui| {
        ui.label(icons::WAVEFORM);
        ui.heading("Live MIDI Monitor");
        if ui.button(iconized(ui, icons::TRASH, " Clear")).clicked() {
            events.clear();
        }
    });

    let events_slice: &[_] = events.make_contiguous();
    if events_slice.is_empty() {
        ui.label("No MIDI messages yet. Move a control on your controller.");
        return;
    }

    egui::ScrollArea::vertical()
        .max_height(ui.available_height())
        .stick_to_bottom(true)
        .id_salt("live_midi_monitor_scroll")
        .show(ui, |ui| {
            let log_text = events_slice
                .iter()
                .map(|event| {
                    let bytes = if event.bytes.is_empty() {
                        "-".to_owned()
                    } else {
                        event
                            .bytes
                            .iter()
                            .map(|b| format!("{b:03}"))
                            .collect::<Vec<_>>()
                            .join(", ")
                    };
                    let time = Local
                        .timestamp_millis_opt(event.timestamp_ms as i64)
                        .single()
                        .map(|dt| dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string())
                        .unwrap_or_else(|| event.timestamp_ms.to_string());
                    format!(
                        "{} | {} | {} | [{}]",
                        time, event.port_name, event.label, bytes
                    )
                })
                .collect::<Vec<_>>()
                .join("\n");

            ui.add(egui::Label::new(log_text).selectable(true).wrap());
        });
}