//! Error handling

use std::{error, fmt, io};

use fluent::FluentError;
use fluent_syntax::parser::ParserError;
use unic_langid::LanguageIdentifierError;

/// `legacytranslate`'s Error type.
///
/// This type is bundles all errors that could occur in
/// `legacytranslate` or in the
/// [`Lang::deconstruct_lang_id`](crate::Lang::deconstruct_lang_id)
/// implementation of
/// [`legacylisten`](https://crates.io/crates/legacylisten).
#[derive(Debug)]
pub enum Error
{
    Io(io::Error),
    /// The language id has to be correct, not only so that minor
    /// things like decimal separator are handled correctly, but also
    /// because fluent will complain if it can't parse it.
    LangId(LanguageIdentifierError),
    Fluent(FluentError),
    FluentParse(ParserError),
    NoLanguage,
    Custom(String),
    Vec(Vec<Error>),
}

impl fmt::Display for Error
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>
    {
        match self
        {
            Self::Io(err) => write!(f, "IO error: {err}"),
            Self::LangId(err) => write!(f, "Malformated lang id: {err}"),
            Self::Fluent(err) => write!(f, "Fluent error: {err}"),
            Self::FluentParse(err) => write!(f, "Fluent parse error: {err}"),
            Self::NoLanguage => write!(f, "No language was specified"),
            Self::Custom(err) => write!(f, "Custom error: {err}"),
            Self::Vec(v) =>
            {
                if v.len() == 1
                {
                    write!(f, "{}", v[0])
                }
                else
                {
                    writeln!(f, "Multiple errors ({}):", v.len())?;
                    for (i, e) in v.iter().enumerate()
                    {
                        write!(f, "{i}: {e}")?;
                        if i != v.len() + 1
                        {
                            writeln!(f)?;
                        }
                    }

                    Ok(())
                }
            }
        }
    }
}

impl error::Error for Error {}

impl From<io::Error> for Error
{
    fn from(err: io::Error) -> Self
    {
        Self::Io(err)
    }
}

impl From<LanguageIdentifierError> for Error
{
    fn from(err: LanguageIdentifierError) -> Self
    {
        Self::LangId(err)
    }
}

impl From<FluentError> for Error
{
    fn from(err: FluentError) -> Self
    {
        Self::Fluent(err)
    }
}

impl From<ParserError> for Error
{
    fn from(err: ParserError) -> Self
    {
        Self::FluentParse(err)
    }
}

impl From<String> for Error
{
    fn from(err: String) -> Self
    {
        Self::Custom(err)
    }
}

impl<T> From<Vec<T>> for Error
where
    Self: From<T>,
{
    fn from(err: Vec<T>) -> Self
    {
        Self::Vec(err.into_iter().map(Into::into).collect())
    }
}
