#![cfg(feature = "unstable")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, doc(cfg(unstable)))]
#![doc(html_logo_url = "https://knurling.ferrous-systems.com/knurling_logo_light_text.svg")]
mod display_hint;
#[cfg(test)]
mod tests;
mod types;
use std::{borrow::Cow, ops::Range};
pub use crate::{
    display_hint::{DisplayHint, TimePrecision},
    types::Type,
};
#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)]
pub enum Error {
    #[error("invalid type specifier `{0:?}`")]
    InvalidTypeSpecifier(String),
    #[error("unable to parse given integer")]
    InvalidInteger(#[from] std::num::ParseIntError),
    #[error("invalid array specifier (missing length)")]
    InvalidArraySpecifierMissingLength,
    #[error("invalid array specifier (missing `]`")]
    InvalidArraySpecifierMissingBracket,
    #[error("trailing data after bitfield range")]
    TrailingDataAfterBitfieldRange,
    #[error("malformed format string (missing display hint after ':')")]
    MalformedFormatString,
    #[error("unknown display hint: {0:?}")]
    UnknownDisplayHint(String),
    #[error("unexpected content `{0:?}` in format string")]
    UnexpectedContentInFormatString(String),
    #[error("unmatched `{{` in format string")]
    UnmatchedOpenBracket,
    #[error("unmatched `}}` in format string")]
    UnmatchedCloseBracket,
    #[error("conflicting types for argument {0}: used as {1:?} and {2:?}")]
    ConflictingTypes(usize, Type, Type),
    #[error("argument {0} is not used in this format string")]
    UnusedArgument(usize),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Parameter {
    pub index: usize,
    pub ty: Type,
    pub hint: Option<DisplayHint>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Fragment<'f> {
    Literal(Cow<'f, str>),
    Parameter(Parameter),
}
#[derive(Debug, PartialEq)]
struct Param {
    index: Option<usize>,
    ty: Type,
    hint: Option<DisplayHint>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
pub enum Level {
    Trace,
    Debug,
    Info,
    Warn,
    Error,
}
impl Level {
    pub fn as_str(self) -> &'static str {
        match self {
            Level::Trace => "trace",
            Level::Debug => "debug",
            Level::Info => "info",
            Level::Warn => "warn",
            Level::Error => "error",
        }
    }
}
fn parse_range(mut s: &str) -> Option<(Range<u8>, usize )> {
    let start_digits = s
        .as_bytes()
        .iter()
        .take_while(|b| (**b as char).is_ascii_digit())
        .count();
    let start = s[..start_digits].parse().ok()?;
    if &s[start_digits..start_digits + 2] != ".." {
        return None;
    }
    s = &s[start_digits + 2..];
    let end_digits = s
        .as_bytes()
        .iter()
        .take_while(|b| (**b as char).is_ascii_digit())
        .count();
    let end = s[..end_digits].parse().ok()?;
    if end <= start || start >= 128 || end > 128 {
        return None;
    }
    Some((start..end, start_digits + end_digits + 2))
}
fn parse_array(mut s: &str) -> Result<usize, Error> {
    let len_pos = s
        .find(|c: char| c != ' ')
        .ok_or(Error::InvalidArraySpecifierMissingLength)?;
    s = &s[len_pos..];
    let after_len = s
        .find(|c: char| !c.is_ascii_digit())
        .ok_or(Error::InvalidArraySpecifierMissingBracket)?;
    let len = s[..after_len].parse::<usize>()?;
    s = &s[after_len..];
    if s != "]" {
        return Err(Error::InvalidArraySpecifierMissingBracket);
    }
    Ok(len)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ParserMode {
    Strict,
    ForwardsCompatible,
}
fn parse_param(mut input: &str, mode: ParserMode) -> Result<Param, Error> {
    const TYPE_PREFIX: &str = "=";
    const HINT_PREFIX: &str = ":";
    let mut index = None;
    let index_end = input
        .find(|c: char| !c.is_ascii_digit())
        .unwrap_or(input.len());
    if index_end != 0 {
        index = Some(input[..index_end].parse::<usize>()?);
    }
    let mut ty = Type::default(); input = &input[index_end..];
    if input.starts_with(TYPE_PREFIX) {
        input = &input[TYPE_PREFIX.len()..];
        let type_end = input.find(HINT_PREFIX).unwrap_or(input.len());
        let type_fragment = &input[..type_end];
        const FORMAT_ARRAY_START: &str = "[?;";
        const U8_ARRAY_START: &str = "[u8;";
        ty = if let Ok(ty) = type_fragment.parse() {
            Ok(ty)
        } else if let Some(s) = type_fragment.strip_prefix(U8_ARRAY_START) {
            Ok(Type::U8Array(parse_array(s)?))
        } else if let Some(s) = type_fragment.strip_prefix(FORMAT_ARRAY_START) {
            Ok(Type::FormatArray(parse_array(s)?))
        } else if let Some((range, used)) = parse_range(type_fragment) {
            match used != type_fragment.len() {
                true => Err(Error::TrailingDataAfterBitfieldRange),
                false => Ok(Type::BitField(range)),
            }
        } else {
            Err(Error::InvalidTypeSpecifier(input.to_owned()))
        }?;
        input = &input[type_end..];
    }
    let mut hint = None;
    if input.starts_with(HINT_PREFIX) {
        input = &input[HINT_PREFIX.len()..];
        if input.is_empty() {
            return Err(Error::MalformedFormatString);
        }
        hint = match (DisplayHint::parse(input), mode) {
            (Some(a), _) => Some(a),
            (None, ParserMode::Strict) => return Err(Error::UnknownDisplayHint(input.to_owned())),
            (None, ParserMode::ForwardsCompatible) => Some(DisplayHint::Unknown(input.to_owned())),
        };
    } else if !input.is_empty() {
        return Err(Error::UnexpectedContentInFormatString(input.to_owned()));
    }
    Ok(Param { index, ty, hint })
}
fn push_literal<'f>(frag: &mut Vec<Fragment<'f>>, unescaped_literal: &'f str) -> Result<(), Error> {
    let mut last_open = false;
    let mut last_close = false;
    for c in unescaped_literal.chars() {
        match c {
            '{' => last_open = !last_open,
            '}' => last_close = !last_close,
            _ if last_open => return Err(Error::UnmatchedOpenBracket),
            _ if last_close => return Err(Error::UnmatchedCloseBracket),
            _ => {}
        }
    }
    if last_open {
        return Err(Error::UnmatchedOpenBracket);
    } else if last_close {
        return Err(Error::UnmatchedCloseBracket);
    }
    let literal = unescaped_literal.replace("{{", "{").replace("}}", "}");
    frag.push(Fragment::Literal(literal.into()));
    Ok(())
}
pub fn get_max_bitfield_range<'a, I>(params: I) -> Option<(u8, u8)>
where
    I: Iterator<Item = &'a Parameter> + Clone,
{
    let largest_bit_index = params
        .clone()
        .map(|param| match ¶m.ty {
            Type::BitField(range) => range.end,
            _ => unreachable!(),
        })
        .max();
    let smallest_bit_index = params
        .map(|param| match ¶m.ty {
            Type::BitField(range) => range.start,
            _ => unreachable!(),
        })
        .min();
    match (smallest_bit_index, largest_bit_index) {
        (Some(smallest), Some(largest)) => Some((smallest, largest)),
        (None, None) => None,
        _ => unreachable!(),
    }
}
pub fn parse(format_string: &str, mode: ParserMode) -> Result<Vec<Fragment<'_>>, Error> {
    let mut fragments = Vec::new();
    let mut end_pos = 0;
    let mut next_arg_index = 0;
    let mut chars = format_string.char_indices();
    while let Some((brace_pos, ch)) = chars.next() {
        if ch != '{' {
            continue;
        }
        if chars.as_str().starts_with('{') {
            chars.next(); continue;
        }
        if brace_pos > end_pos {
            let unescaped_literal = &format_string[end_pos..brace_pos];
            push_literal(&mut fragments, unescaped_literal)?;
        }
        let len = chars
            .as_str()
            .find('}')
            .ok_or(Error::UnmatchedOpenBracket)?;
        end_pos = brace_pos + 1 + len + 1;
        let param_str = &format_string[brace_pos + 1..][..len];
        let param = parse_param(param_str, mode)?;
        fragments.push(Fragment::Parameter(Parameter {
            index: param.index.unwrap_or_else(|| {
                let idx = next_arg_index;
                next_arg_index += 1;
                idx
            }),
            ty: param.ty,
            hint: param.hint,
        }));
    }
    if end_pos != format_string.len() {
        push_literal(&mut fragments, &format_string[end_pos..])?;
    }
    let mut args = Vec::new();
    for frag in &fragments {
        if let Fragment::Parameter(Parameter { index, ty, .. }) = frag {
            if args.len() <= *index {
                args.resize(*index + 1, None);
            }
            match &args[*index] {
                None => args[*index] = Some(ty.clone()),
                Some(other_ty) => match (other_ty, ty) {
                    (Type::BitField(_), Type::BitField(_)) => {} (a, b) if a != b => {
                        return Err(Error::ConflictingTypes(*index, a.clone(), b.clone()))
                    }
                    _ => {}
                },
            }
        }
    }
    for (index, arg) in args.iter().enumerate() {
        if arg.is_none() {
            return Err(Error::UnusedArgument(index));
        }
    }
    Ok(fragments)
}