use {
    crate::{
        config::{
            context::Context,
            extractor::{opt, recover, s32, str, val, Extractor, ExtractorError},
            parser::{DataType, ParseResult, Parser, UnexpectedDataType},
            parsers::color::ColorParser,
            Theme,
        },
        toml::{
            toml_span::{DespanExt, Span, Spanned},
            toml_value::Value,
        },
    },
    indexmap::IndexMap,
    thiserror::Error,
};

pub struct ThemeParser<'a>(pub &'a Context<'a>);

#[derive(Debug, Error)]
pub enum ThemeParserError {
    #[error(transparent)]
    Expected(#[from] UnexpectedDataType),
    #[error(transparent)]
    Extractor(#[from] ExtractorError),
}

impl Parser for ThemeParser<'_> {
    type Value = Theme;
    type Error = ThemeParserError;
    const EXPECTED: &'static [DataType] = &[DataType::Table];

    fn parse_table(
        &mut self,
        span: Span,
        table: &IndexMap<Spanned<String>, Spanned<Value>>,
    ) -> ParseResult<Self> {
        let mut ext = Extractor::new(self.0, span, table);
        let (
            (
                attention_requested_bg_color,
                bg_color,
                bar_bg_color,
                bar_status_text_color,
                border_color,
                captured_focused_title_bg_color,
                captured_unfocused_title_bg_color,
                focused_inactive_title_bg_color,
                focused_inactive_title_text_color,
                focused_title_bg_color,
            ),
            (
                focused_title_text_color,
                separator_color,
                unfocused_title_bg_color,
                unfocused_title_text_color,
                highlight_color,
                border_width,
                title_height,
                font,
            ),
        ) = ext.extract((
            (
                opt(val("attention-requested-bg-color")),
                opt(val("bg-color")),
                opt(val("bar-bg-color")),
                opt(val("bar-status-text-color")),
                opt(val("border-color")),
                opt(val("captured-focused-title-bg-color")),
                opt(val("captured-unfocused-title-bg-color")),
                opt(val("focused-inactive-title-bg-color")),
                opt(val("focused-inactive-title-text-color")),
                opt(val("focused-title-bg-color")),
            ),
            (
                opt(val("focused-title-text-color")),
                opt(val("separator-color")),
                opt(val("unfocused-title-bg-color")),
                opt(val("unfocused-title-text-color")),
                opt(val("highlight-color")),
                recover(opt(s32("border-width"))),
                recover(opt(s32("title-height"))),
                recover(opt(str("font"))),
            ),
        ))?;
        macro_rules! color {
            ($e:expr) => {
                match $e {
                    None => None,
                    Some(v) => match v.parse(&mut ColorParser) {
                        Ok(v) => Some(v),
                        Err(e) => {
                            log::warn!("Could not parse a color: {}", self.0.error(e));
                            None
                        }
                    },
                }
            };
        }
        Ok(Theme {
            attention_requested_bg_color: color!(attention_requested_bg_color),
            bg_color: color!(bg_color),
            bar_bg_color: color!(bar_bg_color),
            bar_status_text_color: color!(bar_status_text_color),
            border_color: color!(border_color),
            captured_focused_title_bg_color: color!(captured_focused_title_bg_color),
            captured_unfocused_title_bg_color: color!(captured_unfocused_title_bg_color),
            focused_inactive_title_bg_color: color!(focused_inactive_title_bg_color),
            focused_inactive_title_text_color: color!(focused_inactive_title_text_color),
            focused_title_bg_color: color!(focused_title_bg_color),
            focused_title_text_color: color!(focused_title_text_color),
            separator_color: color!(separator_color),
            unfocused_title_bg_color: color!(unfocused_title_bg_color),
            unfocused_title_text_color: color!(unfocused_title_text_color),
            highlight_color: color!(highlight_color),
            border_width: border_width.despan(),
            title_height: title_height.despan(),
            font: font.map(|f| f.value.to_string()),
        })
    }
}
