/* src/processes/smaps_rollup.rs
 *
 * Copyright 2026 Mission Center Developers
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

use std::fmt::Write;

use arrayvec::ArrayString;

use super::MAX_U32_LEN;

#[derive(Default)]
pub struct SmapsRollup {
    pub pss_kb: u64,
    pub swap_pss: u64,
}

pub fn open(pid: u32) -> Option<std::fs::File> {
    const MAX_PATH_LEN: usize = "/proc/".len() + "/smaps_rollup".len() + MAX_U32_LEN;

    let mut path: ArrayString<MAX_PATH_LEN> = ArrayString::new();
    let _ = write!(path, "/proc/{}/smaps_rollup", pid);

    let file = match std::fs::OpenOptions::new().read(true).open(path) {
        Ok(f) => f,
        Err(e) => {
            log::debug!(
                "Failed to open `smaps_rollup` file for process {}: {}",
                pid,
                e,
            );
            return None;
        }
    };

    Some(file)
}

pub fn parse(content: &str) -> SmapsRollup {
    let mut out = SmapsRollup::default();

    for line in content.lines() {
        if let Some(rest) = line.strip_prefix("Pss:") {
            out.pss_kb = parse_kb_value(rest);
        } else if let Some(rest) = line.strip_prefix("SwapPss:") {
            out.swap_pss = parse_kb_value(rest);
            // should be last line in file
            break;
        }
    }

    out
}

// Parse a value like `"   181 kB"` into `181u64`.
// and convert to KB
fn parse_kb_value(s: &str) -> u64 {
    s.split_whitespace()
        .next()
        .and_then(|v| v.parse::<u64>().ok())
        .map(|v| v * 1024)
        .unwrap_or(0)
}
