//! 系统时钟源管理

use core::ops::DivAssign;

use crate::{
    println,
    time::{NSEC_PER_SEC, sched_clock::sched_clock_register, timekeeping_init},
};

/// 时钟源管理
pub struct ClockSource {
    read: fn() -> u64,
    mask: u64,
    mult: u32,
    shift: u32,
    max_idle_ns: u64,
    max_cycles: u64,
    maxadj: u32,
    name: &'static str,
}

pub(crate) fn clocks_calc_mult_shift(
    mult: &mut u32,
    shift: &mut u32,
    from: u32,
    to: u32,
    maxsec: u32,
) {
    let mut sftacc = 32;
    let mut tmp = (u64::from(maxsec) * u64::from(from)) >> 32;
    while tmp != 0 {
        tmp >>= 1;
        sftacc -= 1;
    }

    let mut sft = 32;
    while sft > 0 {
        tmp = u64::from(to) << sft;
        tmp += u64::from(from) / 2;
        tmp.div_assign(u64::from(from));
        if (tmp >> sftacc) == 0 {
            break;
        }
        sft -= 1;
    }
    *mult = tmp as u32;
    *shift = sft;
}

#[inline(always)]
fn clocksource_cyc2ns(cycles: u64, mult: u32, shift: u32) -> u64 {
    (cycles * u64::from(mult)) >> shift
}

pub(crate) fn clocks_calc_max_nsecs(
    mult: u32,
    shift: u32,
    maxadj: u32,
    mask: u64,
    max_cyc: &mut u64,
) -> u64 {
    let mut max_cycles = u64::MAX;
    max_cycles.div_assign(u64::from(mult) + u64::from(maxadj));

    max_cycles = max_cycles.min(mask);
    let mut max_nsecs = clocksource_cyc2ns(max_cycles, mult - maxadj, shift);
    *max_cyc = max_cycles;

    max_nsecs >>= 1;

    max_nsecs
}

#[inline(always)]
fn dummy_read() -> u64 {
    0
}

impl ClockSource {
    pub(crate) const fn new() -> Self {
        Self {
            read: dummy_read,
            mask: 0,
            mult: 0,
            shift: 0,
            max_idle_ns: 0,
            max_cycles: 0,
            maxadj: 0,
            name: "dummy clock",
        }
    }

    /// 创建一个时钟源
    pub fn create(read: fn() -> u64, mask: u64, name: &'static str) -> Self {
        Self { read, mask, mult: 0, shift: 0, max_idle_ns: 0, max_cycles: 0, maxadj: 0, name }
    }

    #[inline]
    fn clocksource_update_max_deferment(&mut self) {
        self.max_idle_ns = clocks_calc_max_nsecs(
            self.mult,
            self.shift,
            self.maxadj,
            self.mask,
            &mut self.max_cycles,
        );
    }

    #[inline]
    fn clocksource_max_adjustment(&self) -> u32 {
        let mut ret = self.mult * 11;
        ret.div_assign(100);
        ret
    }

    #[allow(clippy::panicking_overflow_checks)]
    fn __clocksource_update_freq_scale(&mut self, scale: u32, freq: u32) {
        if freq != 0 {
            let mut sec = self.mask;
            sec.div_assign(u64::from(freq));
            sec.div_assign(u64::from(scale));
            if sec == 0 {
                sec = 1;
            } else if sec > 600 && self.mask > u64::from(u32::MAX) {
                sec = 600;
            }

            clocks_calc_mult_shift(
                &mut self.mult,
                &mut self.shift,
                freq,
                NSEC_PER_SEC as u32 / scale,
                sec as u32 * scale,
            );
        }

        self.maxadj = self.clocksource_max_adjustment();
        while freq != 0
            && ((self.mult + self.maxadj < self.mult) || (self.mult - self.maxadj > self.mult))
        {
            self.mult >>= 1;
            self.shift -= 1;
            self.maxadj = self.clocksource_max_adjustment();
        }

        debug_assert!((self.mult + self.maxadj >= self.mult));

        self.clocksource_update_max_deferment();

        println!(
            "{}: mask: {:#x} max_cycles: {}, max_idle_ns {} ns",
            self.name, self.mask, self.max_cycles, self.max_idle_ns
        );
    }

    // 根据频率更新时钟参数(hz)
    #[inline(always)]
    fn clocksource_update_freq_hz(&mut self, hz: usize) {
        self.__clocksource_update_freq_scale(1, hz as u32);
    }

    #[inline(always)]
    pub(crate) fn read(&self) -> u64 {
        (self.read)()
    }

    #[inline(always)]
    pub(crate) fn mask(&self) -> u64 {
        self.mask
    }

    #[inline(always)]
    pub(crate) fn mult(&self) -> u32 {
        self.mult
    }

    #[inline(always)]
    pub(crate) fn shift(&self) -> u32 {
        self.shift
    }
}

// 时钟源初始化
//
// 由驱动直接调用
#[allow(unused)]
pub(crate) fn clocksource_init(mut clocksource: ClockSource, rate: usize) {
    let read = clocksource.read;
    let mask = clocksource.mask;
    sched_clock_register(read, mask, rate);

    clocksource.clocksource_update_freq_hz(rate);
    timekeeping_init(clocksource);
}
