//! [`Pair`] implementation based on [`StaticPair`].

use std::error::Error;
use std::fmt::Display;

use crate::rcore::*;
use crate::rstd::inlining::{static_pair::*, *};

#[derive(Clone)]
pub struct Pair<A, B> {
    pub a: A,
    pub b: B,
}

pub type PairObject<A, B> = StaticPairObject<Pair<A, B>>;
pub type PairFactory<'a, Ctx, A, B> = StaticPairFactory<'a, Ctx, Pair<A, B>>;

impl<A: Serializable, B: Serializable> StaticPairSerializable for Pair<A, B> {
    type SA = A;
    type SB = B;

    fn elements(&self) -> (&Self::SA, &Self::SB) {
        (&self.a, &self.b)
    }

    fn into_elements(self) -> (Self::SA, Self::SB) {
        (self.a, self.b)
    }
}

#[derive(Debug)]
pub enum PairParseError<ErrorA: Error, ErrorB: Error> {
    A(ErrorA),
    B(ErrorB),
}

impl<ErrorA: Error, ErrorB: Error> Display for PairParseError<ErrorA, ErrorB> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::A(error) => {
                write!(f, "error while parsing first element of a pair: {}", error)
            }
            Self::B(error) => {
                write!(f, "error while parsing second element of a pair: {}", error)
            }
        }
    }
}

impl<ErrorA: Error, ErrorB: Error> Error for PairParseError<ErrorA, ErrorB> {}

impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>, B: Mentionable<'a, Ctx>> StaticPair<'a, Ctx>
    for Pair<A, B>
where
    A::Fctr: InlineableFactory<'a, Ctx>,
{
    type FactoryData = Pair<Self::FA, Self::FB>;
    type A = A;
    type B = B;
    type FA = A::Fctr;
    type FB = B::Fctr;
    type ParseError = PairParseError<ParseError<'a, Ctx, A::Fctr>, ParseError<'a, Ctx, B::Fctr>>;

    fn factories(factory_data: &Self::FactoryData) -> (&Self::FA, &Self::FB) {
        (&factory_data.a, &factory_data.b)
    }

    fn from_parsed(_factory_data: &Self::FactoryData, a: Self::A, b: Self::B) -> Self {
        Pair { a, b }
    }

    fn from_error_a(
        _factory_data: &Self::FactoryData,
        error: ParseError<'a, Ctx, Self::FA>,
    ) -> Self::ParseError {
        PairParseError::A(error)
    }

    fn from_error_b(
        _factory_data: &Self::FactoryData,
        error: ParseError<'a, Ctx, Self::FB>,
    ) -> Self::ParseError {
        PairParseError::B(error)
    }

    fn factory_data(&self) -> Self::FactoryData {
        Pair {
            a: self.a.factory(),
            b: self.b.factory(),
        }
    }
}