//! JVM method descriptor parsing.

/// Count the number of parameter slots in a method descriptor.
/// Descriptor format: "(params)return" e.g. "(II)I" = two int params, returns int.
pub fn count_parameters(descriptor: &str) -> usize {
    let bytes = descriptor.as_bytes();
    if bytes.first() != Some(&b'(') {
        0
    } else {
        let mut i = 1;
        let mut count = 0;
        while i < bytes.len() {
            match bytes[i] {
                b')' => return count,
                b'I' | b'F' | b'B' | b'C' | b'S' | b'Z' => {
                    count += 1;
                    i += 1;
                }
                b'J' | b'D' => {
                    count += 2;
                    i += 1;
                }
                b'L' => {
                    i += 1;
                    while i < bytes.len() && bytes[i] != b';' {
                        i += 1;
                    }
                    if i >= bytes.len() {
                        return count;
                    }
                    count += 1;
                    i += 1;
                }
                b'[' => {
                    i += 1;
                    while i < bytes.len() && bytes[i] == b'[' {
                        i += 1;
                    }
                    if i >= bytes.len() {
                        return count;
                    }
                    if bytes[i] == b'L' {
                        i += 1;
                        while i < bytes.len() && bytes[i] != b';' {
                            i += 1;
                        }
                        if i >= bytes.len() {
                            return count;
                        }
                        i += 1;
                    } else {
                        i += 1;
                    }
                    count += 1;
                }
                _ => i += 1,
            }
        }
        count
    }
}

/// Parse method parameter type names from descriptor.
pub fn parse_method_params(descriptor: &str) -> Vec<String> {
    let mut params = Vec::new();
    let bytes = descriptor.as_bytes();
    if bytes.first() != Some(&b'(') {
        return params;
    }

    let mut i = 1;
    while i < bytes.len() {
        match bytes[i] {
            b')' => break,
            b'B' => {
                params.push("byte".to_string());
                i += 1;
            }
            b'C' => {
                params.push("char".to_string());
                i += 1;
            }
            b'D' => {
                params.push("double".to_string());
                i += 1;
            }
            b'F' => {
                params.push("float".to_string());
                i += 1;
            }
            b'I' => {
                params.push("int".to_string());
                i += 1;
            }
            b'J' => {
                params.push("long".to_string());
                i += 1;
            }
            b'S' => {
                params.push("short".to_string());
                i += 1;
            }
            b'Z' => {
                params.push("boolean".to_string());
                i += 1;
            }
            b'L' => {
                i += 1;
                while i < bytes.len() && bytes[i] != b';' {
                    i += 1;
                }
                if i >= bytes.len() {
                    break;
                }
                params.push("object".to_string());
                i += 1;
            }
            b'[' => {
                i += 1;
                while i < bytes.len() && bytes[i] == b'[' {
                    i += 1;
                }
                if i >= bytes.len() {
                    break;
                }
                if bytes[i] == b'L' {
                    i += 1;
                    while i < bytes.len() && bytes[i] != b';' {
                        i += 1;
                    }
                    if i >= bytes.len() {
                        break;
                    }
                    i += 1;
                } else {
                    i += 1;
                }
                params.push("array".to_string());
            }
            _ => {
                i += 1;
            }
        }
    }

    params
}
