use super::PANEL_WIDTH;
use std::fmt::{self, Write};

use libamdgpu_top::stat::{Sensors, PcieBw};

const WIDTH: usize = PANEL_WIDTH / 2;

use crate::AppTextView;

impl AppTextView {
    pub const SENSORS_TITLE: &str = "Sensors";

    pub fn print_sensors(&mut self, sensors: &Sensors) -> Result<(), fmt::Error> {
        const NAME_LEN: usize = 10;
        const VAL_LEN: usize = 5;
        self.text.clear();

        let mut c = 0;

        for (name, val, unit) in [
            ("GFX_SCLK", sensors.sclk, "MHz"),
            ("GFX_MCLK", sensors.mclk, "MHz"),
            ("FCLK", sensors.fclk_dpm.as_ref().map(|f| f.current_mhz), "MHz"),
            ("VDDNB", sensors.vddnb, "mV"),
            ("VDDGFX", sensors.vddgfx, "mV"),
        ] {
            let Some(val) = val else { continue };
            c += 1;
            write!(
                self.text.buf,
                " {:<WIDTH$}",
                format!("{name:<NAME_LEN$} => {val:>VAL_LEN$} {unit:3}")
            )?;
            if (c % 2) == 0 { writeln!(self.text.buf)? };
        }
        if (c % 2) == 1 { writeln!(self.text.buf)?; }

        if sensors.average_power.is_some() || sensors.input_power.is_some() {
            write!(self.text.buf, " GPU Power  =>")?;

            for power in [&sensors.average_power, &sensors.input_power] {
                let Some(power) = power else { continue };
                write!(
                    self.text.buf,
                    " {:3} W ({})",
                    power.value,
                    power.type_,
                )?;
            }

            if let Some(cap) = &sensors.power_cap {
                write!(
                    self.text.buf,
                    " (Cap. {} W, {}-{} W)", cap.current, cap.min, cap.max,
                )?;
            }
            writeln!(self.text.buf)?;
        }

        for temp in [&sensors.edge_temp, &sensors.junction_temp, &sensors.memory_temp] {
            let Some(temp) = temp else { continue };
            let label = format!("{} Temp.", temp.type_);
            write!(self.text.buf, " {label:<15} => {:3} C", temp.current)?;
            if let Some(crit) = temp.critical {
                write!(self.text.buf, " (Crit. {crit} C)")?;
            }
            if let Some(e) = temp.emergency {
                write!(self.text.buf, " (Emergency {e} C)")?;
            }
            writeln!(self.text.buf)?;
        }

        if let Some(tctl) = sensors.tctl {
            write!(self.text.buf, " CPU Tctl   => {:3} C", tctl / 1000)?;
            writeln!(self.text.buf)?;
        }

        if let Some(fan_rpm) = sensors.fan_rpm {
            write!(self.text.buf, " Fan => {fan_rpm:4} RPM")?;
            if let Some(max_rpm) = sensors.fan_max_rpm {
                if let Some(per) = fan_rpm.saturating_mul(100).checked_div(max_rpm) {
                    write!(self.text.buf, " ({per:>3}%)")?;
                }

                write!(self.text.buf, " (Max. {max_rpm} RPM)")?;
            }
            writeln!(self.text.buf)?;
        }

        if let Some(cur) = sensors.current_link {
            write!(self.text.buf, " PCIe Link Speed => Gen{}x{:<2}", cur.r#gen, cur.width)?;

            if let [Some(min), Some(max)] = [sensors.min_dpm_link, sensors.max_dpm_link] {
                write!(
                    self.text.buf,
                    " (Gen{}x{} - Gen{}x{})",
                    min.r#gen,
                    min.width,
                    max.r#gen,
                    max.width,
                )?;
            } else if let Some(max) = sensors.max_dpm_link {
                write!(self.text.buf, " (Max. Gen{}x{})", max.r#gen, max.width)?;
            }

            writeln!(self.text.buf)?;
        }

        if let Some(power_state) = &sensors.pci_power_state {
            writeln!(self.text.buf, " PCI Power State: {power_state}")?;
        }

        sensors.print_all_cpu_core_cur_freq(
            &mut self.text.buf,
            " CPU Core freq (MHz)",
            false,
        )?;

        Ok(())
    }

    pub fn print_pcie_bw(&mut self, pcie_bw: &PcieBw) -> Result<(), fmt::Error> {
        let Some(mps) = pcie_bw.max_payload_size else { return Ok(()) };
        let Some(sent) = pcie_bw.sent.map(|v| (v * mps as u64) >> 20) else { return Ok(()) };
        let Some(rec) = pcie_bw.received.map(|v| (v * mps as u64) >> 20) else { return Ok(()) };

        writeln!(
            self.text.buf,
            " PCIe Bandwidth Usage => Sent: {sent:6} MiB/s, Received: {rec:6} MiB/s",
        )?;

        Ok(())
    }

    pub fn sensors_name(index: usize) -> String {
        format!("{} {index}", Self::SENSORS_TITLE)
    }

    pub fn cb_sensors(siv: &mut cursive::Cursive) {
        use crate::{set_min_height, set_visible_height, Opt};
        use cursive::views::TextView;

        let visible;
        let indexes = {
            let mut opt = siv.user_data::<Opt>().unwrap().lock().unwrap();
            opt.sensor ^= true;

            visible = opt.sensor;

            opt.indexes.clone()
        };

        for i in &indexes {
            let name = Self::sensors_name(*i);
            if visible {
                siv.call_on_name(&name, set_visible_height::<TextView>);
            } else {
                siv.call_on_name(&name, set_min_height::<TextView>);
            }
        }
    }
}
