/*!
 * Copyright 2019-2026, Benjamin Vaisvil and the zenith contributors
 */

use super::style::{max_style, ok_style};
use super::{percent_of, Render, LEFT_PANE_WIDTH};
use crate::float_to_byte_string;
use crate::metrics::histogram::{HistogramKind, View};
use crate::metrics::CPUTimeApp;
use byte_unit::{Byte, Unit};
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::style::{Color, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, List, ListItem, ListState, Sparkline};
use ratatui::Frame;
use std::borrow::Cow;

pub fn render_graphics(
    app: &CPUTimeApp,
    layout: Rect,
    f: &mut Frame<'_>,
    view: View,
    gfx_list_state: &mut ListState,
    border_style: Style,
) {
    Block::default()
        .title("Graphics")
        .borders(Borders::ALL)
        .border_style(border_style)
        .render(f, layout);
    let gfx_layout = Layout::default()
        .margin(0)
        .direction(Direction::Horizontal)
        .constraints([Constraint::Length(LEFT_PANE_WIDTH), Constraint::Min(10)].as_ref())
        .split(layout);
    let area = Layout::default()
        .margin(1)
        .direction(Direction::Vertical)
        .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
        .split(gfx_layout[1]);
    if app.gfx_devices.is_empty() {
        return;
    }

    let view = View {
        width: area[0].width as usize,
        ..view
    };

    let gd = &app.gfx_devices[gfx_list_state.selected().unwrap_or(0)];
    let h_gpu = match app
        .histogram_map
        .get_zoomed(&HistogramKind::GpuUse(gd.uuid.clone()), &view)
    {
        Some(h) => h,
        None => return,
    };
    let fan = if !gd.fans.is_empty() {
        format!("FAN [{:3.0}%]", gd.fans[0])
    } else {
        String::from("")
    };

    #[cfg(not(all(target_os = "linux", feature = "nvidia")))]
    let version = "";
    #[cfg(all(target_os = "linux", feature = "nvidia"))]
    let version = if let (Some(dv), Some(cv), Some(nv)) = (
        &app.nvml_driver_version,
        &app.nvml_cuda_version,
        &app.nvml_version,
    ) {
        format!(" VER [DRIVER: {:} CUDA: {:} NVML: {:}]", dv, cv, nv)
    } else {
        String::new()
    };
    let title = if gd.name == "Total" {
        format!(
            "AVG GPU [{:3.0}%] AVG ENC [{:3.0}%] AVG DEC [{:3.0}%]{:}",
            gd.gpu_utilization, gd.encoder_utilization, gd.decoder_utilization, version
        )
    } else {
        format!(
            "GPU [{:3.0}%] ENC [{:3.0}%] DEC [{:3.0}%] PROC [{:}] CLOCK [{:} Mhz / {:} Mhz]{:}",
            gd.gpu_utilization,
            gd.encoder_utilization,
            gd.decoder_utilization,
            gd.processes.len(),
            gd.clock,
            gd.max_clock,
            version
        )
    };
    Sparkline::default()
        .block(Block::default().title(title.as_str()))
        .data(h_gpu.data())
        .style(Style::default().fg(Color::LightYellow))
        .max(100)
        .render(f, area[0]);

    let h_mem = match app
        .histogram_map
        .get_zoomed(&HistogramKind::GpuMem(gd.uuid.clone()), &view)
    {
        Some(h) => h,
        None => return,
    };
    let title2 = if gd.name == "Total" {
        format!(
            "AVG FB [{:3.0}%] AVG MEM [{:} / {:} - {:}%]",
            gd.mem_utilization,
            float_to_byte_string!(gd.used_memory as f64, Unit::B),
            float_to_byte_string!(gd.total_memory as f64, Unit::B),
            percent_of(gd.used_memory, gd.total_memory) as u64,
        )
    } else {
        format!(
            "FB [{:3.0}%] MEM [{:} / {:} - {:}%] {:}",
            gd.mem_utilization,
            float_to_byte_string!(gd.used_memory as f64, Unit::B),
            float_to_byte_string!(gd.total_memory as f64, Unit::B),
            percent_of(gd.used_memory, gd.total_memory) as u64,
            fan,
        )
    };
    Sparkline::default()
        .block(Block::default().title(Line::from(vec![
            Span::raw(title2.as_str()),
            Span::raw(if gd.name == "Total" {
                " AVG PWR ["
            } else {
                " PWR ["
            }),
            Span::styled(
                format!("{:} W / {:} W", gd.power_usage / 1000, gd.max_power / 1000),
                if gd.power_usage > gd.max_power {
                    max_style()
                } else {
                    ok_style()
                },
            ),
            Span::raw("] TEMP ["),
            Span::styled(
                format!("{:} C / {:} C", gd.temperature, gd.temperature_max),
                if gd.temperature > gd.temperature_max {
                    max_style()
                } else {
                    ok_style()
                },
            ),
            Span::raw("]"),
        ])))
        .data(h_mem.data())
        .style(Style::default().fg(Color::LightMagenta))
        .max(100)
        .render(f, area[1]);
    let devices: Vec<_> = app
        .gfx_devices
        .iter()
        .map(|d| {
            let style = if d.gpu_utilization > 90 {
                max_style()
            } else {
                ok_style()
            };
            Span::styled(
                Cow::Owned(format!(" {:3.0}%: {}", d.gpu_utilization, d.name)),
                style,
            )
        })
        .map(ListItem::new)
        .collect();
    let devices_list = List::new(devices)
        .block(
            Block::default()
                .title(Span::styled("Graphics Devices", border_style))
                .borders(Borders::ALL)
                .border_style(border_style),
        )
        .highlight_symbol("→");
    f.render_stateful_widget(devices_list, gfx_layout[0], gfx_list_state);
}
