use {
    crate::{
        config::{
            extractor::ExtractorError,
            parser::{DataType, ParseResult, Parser, UnexpectedDataType},
        },
        toml::toml_span::{Span, SpannedExt},
    },
    jay_config::theme::Color,
    std::{num::ParseIntError, ops::Range},
    thiserror::Error,
};

pub struct ColorParser;

#[derive(Debug, Error)]
pub enum ColorParserError {
    #[error(transparent)]
    DataType(#[from] UnexpectedDataType),
    #[error(transparent)]
    Extractor(#[from] ExtractorError),
    #[error("Color must start with `#`")]
    Prefix,
    #[error("String must have length 4, 5, 6, or 9")]
    Length,
    #[error(transparent)]
    ParseIntError(#[from] ParseIntError),
}

impl Parser for ColorParser {
    type Value = Color;
    type Error = ColorParserError;
    const EXPECTED: &'static [DataType] = &[DataType::String];

    fn parse_string(&mut self, span: Span, string: &str) -> ParseResult<Self> {
        let hex = match string.strip_prefix("#") {
            Some(s) => s,
            _ => return Err(ColorParserError::Prefix.spanned(span)),
        };
        let d = |range: Range<usize>| {
            u8::from_str_radix(&hex[range], 16)
                .map_err(|e| ColorParserError::ParseIntError(e).spanned(span))
        };
        let s = |range: Range<usize>| {
            let v = d(range)?;
            Ok((v << 4) | v)
        };
        let (r, g, b, a) = match hex.len() {
            3 => (s(0..1)?, s(1..2)?, s(2..3)?, u8::MAX),
            4 => (s(0..1)?, s(1..2)?, s(2..3)?, s(3..4)?),
            6 => (d(0..2)?, d(2..4)?, d(4..6)?, u8::MAX),
            8 => (d(0..2)?, d(2..4)?, d(4..6)?, d(6..8)?),
            _ => return Err(ColorParserError::Length.spanned(span)),
        };
        Ok(Color::new_straight(r, g, b, a))
    }
}
