use {
    super::{
        DuDevice,
        diskutil_exec::*,
    },
    crate::*,
    lazy_regex::*,
};

pub fn mounted_du_devices() -> Result<Vec<DuDevice>, Error> {
    let lines = du_lines(&["info", "-all"])?;
    Ok(lines_to_devices(&lines))
}

fn lines_to_devices(lines: &[String]) -> Vec<DuDevice> {
    let mut devs = Vec::new();
    let mut start = 0;
    for (i, line) in lines.iter().enumerate() {
        if regex_is_match!(r"\s*\*{8,}\s*$", line) {
            if i > start + 3 {
                let dev_lines = &lines[start..i];
                if let Some(dev) = lines_to_device(dev_lines) {
                    devs.push(dev);
                } else {
                    eprintln!("Device not understood:\n{}", dev_lines.join("\n"),);
                }
            }
            start = i + 1;
        }
    }
    devs
}

fn lines_to_device(lines: &[String]) -> Option<DuDevice> {
    let mut id = None;
    let mut node = None;
    let mut file_system = None;
    let mut mount_point = None;
    let mut part_of_whole = None;
    let mut encrypted = None;
    let mut read_only = None;
    let mut removable = None;
    let mut protocol = None;
    let mut solid_state = None;
    let mut volume_total_space = None;
    let mut volume_free_space = None;
    let mut volume_used_space = None;
    let mut container_total_space = None;
    let mut container_free_space = None;
    let mut allocation_block_size = None;
    let mut uuid = None;
    let mut part_uuid = None;
    for line in lines {
        let Some((_, key, value)) = regex_captures!(r"^\s+([^\:]+):\s+(.+)$", &line) else {
            continue;
        };
        match key {
            "Device Identifier" => {
                id = Some(value.to_string());
            }
            "Device Node" => {
                node = Some(value.to_string());
            }
            "File System" | "File System Personality" => {
                if value != "None" {
                    file_system = Some(value.to_string());
                }
            }
            "Mount Point" => {
                mount_point = Some(value.to_string());
            }
            "Part of Whole" => {
                part_of_whole = Some(value.to_string());
            }
            "Protocol" => {
                protocol = Some(value.to_string());
            }
            "Encrypted" => {
                encrypted = extract_bool(value);
            }
            "Media Read-Only" => {
                read_only = extract_bool(value);
            }
            "Removable Media" => match value {
                "Removable" => {
                    removable = Some(true);
                }
                "Fixed" => {
                    removable = Some(false);
                }
                _ => {}
            },
            "Solid State" => {
                solid_state = extract_bool(value);
            }
            "Volume Total Space" => {
                volume_total_space = extract_bytes(value);
            }
            "Volume Free Space" => {
                volume_free_space = extract_bytes(value);
            }
            "Volume Used Space" => {
                volume_used_space = extract_bytes(value);
            }
            "Container Total Space" => {
                container_total_space = extract_bytes(value);
            }
            "Container Free Space" => {
                container_free_space = extract_bytes(value);
            }
            "Allocation Block Size" => {
                allocation_block_size = extract_bytes(value);
            }
            "Volume UUID" => {
                uuid = Some(value.to_string());
            }
            "Disk / Partition UUID" => {
                part_uuid = Some(value.to_string());
            }
            _ => {}
        }
    }
    Some(DuDevice {
        id: id?,
        node: node?,
        file_system,
        mount_point,
        part_of_whole,
        removable,
        protocol,
        solid_state,
        read_only,
        encrypted,
        volume_total_space,
        volume_free_space,
        volume_used_space,
        container_total_space,
        container_free_space,
        allocation_block_size,
        uuid,
        part_uuid,
    })
}

fn extract_bytes(s: &str) -> Option<u64> {
    let (_, num) = regex_captures!(r"(\d+)\sBytes", s)?;
    num.parse().ok()
}
fn extract_bool(value: &str) -> Option<bool> {
    regex_switch!(value,
        r"^Yes\b" => true,
        r"^No\b" => false,
    )
}
