use std::fmt::{Display, Formatter};
use std::num::ParseIntError;

#[derive(Debug, Clone)]
pub enum ByteArrayParseErr {
    EmptySrcArray,
    ParseIntError(ParseIntError),
}

impl From<ParseIntError> for ByteArrayParseErr {
    fn from(e: ParseIntError) -> Self {
        ByteArrayParseErr::ParseIntError(e)
    }
}

impl Display for ByteArrayParseErr {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let err_msg = match self {
            ByteArrayParseErr::EmptySrcArray => "Provided array is empty!".to_string(),
            ByteArrayParseErr::ParseIntError(e) => format!("Failed to parse as int: {}", e),
        };

        write!(f, "{}", err_msg)
    }
}

fn bytes_from_str_array(src: Vec<String>) -> Result<Vec<u8>, ByteArrayParseErr> {
    src.iter()
        .map(|element| {
            if element.starts_with("0x") || element.starts_with("0X") {
                u8::from_str_radix(&element[2..], 16)
            } else if element.starts_with("0b") || element.starts_with("0B") {
                u8::from_str_radix(&element[2..], 2)
            } else if element.starts_with('h') || element.starts_with('H') {
                u8::from_str_radix(&element[1..], 16)
            } else if element.len() > 1 && element.starts_with('0') {
                u8::from_str_radix(&element[1..], 8)
            } else {
                element.parse()
            }
        })
        .map(|e| e.map_err(ByteArrayParseErr::from))
        .collect()
}

pub fn parse_bytes_from_input_arg(src: Vec<String>) -> Result<Vec<u8>, ByteArrayParseErr> {
    if src.is_empty() {
        return Err(ByteArrayParseErr::EmptySrcArray);
    }

    let str_arr = if src.len() == 1 {
        src[0]
            .replace(',', " ")
            .replace('[', "")
            .replace(']', "")
            .split_whitespace()
            .map(|s| s.to_string())
            .collect()
    } else {
        src
    };

    bytes_from_str_array(str_arr)
}

#[cfg(test)]
mod tests {
    use crate::parser::parse_bytes_from_input_arg;

    #[test]
    fn parse_base_10_array() {
        let array = vec!["[0, 1, 2, 3, 4]".to_string()];

        let out = parse_bytes_from_input_arg(array).unwrap();

        assert_eq!(out, vec![0, 1, 2, 3, 4])
    }

    #[test]
    fn parse_base_2_array() {
        let array = vec!["[0b0, 0b1, 0b10, 0b11, 0b100]".to_string()];

        let out = parse_bytes_from_input_arg(array).unwrap();

        assert_eq!(out, vec![0, 1, 2, 3, 4])
    }

    #[test]
    fn parse_base_8_array() {
        let array = vec!["[00, 01, 02, 03, 04]".to_string()];

        let out = parse_bytes_from_input_arg(array).unwrap();

        assert_eq!(out, vec![0, 1, 2, 3, 4])
    }

    #[test]
    fn parse_base_16_array() {
        let array = vec!["[0x0, 0x1, 0x2, h3, H4]".to_string()];

        let out = parse_bytes_from_input_arg(array).unwrap();

        assert_eq!(out, vec![0, 1, 2, 3, 4])
    }

    #[test]
    fn parse_base_mixed() {
        let array = vec!["[00 0x1, 2, h3, H4]".to_string()];

        let out = parse_bytes_from_input_arg(array).unwrap();

        assert_eq!(out, vec![0, 1, 2, 3, 4])
    }

    #[test]
    fn parse_no_braces_multi_args() {
        let array = vec![
            "0".to_string(),
            "1".to_string(),
            "2".to_string(),
            "3".to_string(),
            "4".to_string(),
        ];
        let out = parse_bytes_from_input_arg(array).unwrap();

        assert_eq!(out, vec![0, 1, 2, 3, 4])
    }

    #[test]
    fn parse_no_braces_one_arg() {
        let array = vec!["0 1 2 3 4".to_string()];
        let out = parse_bytes_from_input_arg(array).unwrap();

        assert_eq!(out, vec![0, 1, 2, 3, 4])
    }
}