//! Module responsible for making [Point]s [Mentionable].

use std::{error::Error, fmt::Display};

use crate::rcore::*;

impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> Serializable for Point<'a, Ctx, A> {
    fn serialize(&self, serializer: &mut dyn Serializer) {
        serializer.write(&self.point)
    }
}

impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> Mentionable<'a, Ctx> for Point<'a, Ctx, A> {
    type Fctr = PointFactory<A::Fctr>;

    fn factory(&self) -> Self::Fctr {
        PointFactory {
            factory: self.origin.factory(),
        }
    }

    fn topology(&self) -> Hash {
        Ctx::hash(&self.point)
    }

    fn points_typed(&self, points: &mut impl TakesPoints<'a, Ctx>) {
        points.take(self)
    }
}

#[derive(Clone)]
pub struct PointFactory<F> {
    factory: F,
}

impl<F: Clone> PointFactory<F> {
    pub fn inner(&self) -> F {
        self.factory.clone()
    }
}

#[derive(Debug)]
pub enum PointParseError {
    WrongLength(usize),
}

impl Display for PointParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::WrongLength(length) => {
                write!(f, "expected {} bytes, received {}.", HASH_SIZE, length)
            }
        }
    }
}

impl Error for PointParseError {}

impl From<&[u8]> for PointParseError {
    fn from(value: &[u8]) -> Self {
        PointParseError::WrongLength(value.len())
    }
}

impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> AsRef<[u8]> for Point<'a, Ctx, A> {
    fn as_ref(&self) -> &[u8] {
        &self.point
    }
}

impl<'a, Ctx: Context<'a>, F: Factory<'a, Ctx>> Factory<'a, Ctx> for PointFactory<F> {
    type Mtbl = Point<'a, Ctx, F::Mtbl>;

    type ParseError = PointParseError;

    fn deserialize(&self, dectx: &mut dyn DeCtx<'a, Ctx>) -> ParseResult<'a, Ctx, Self> {
        let (addresses, deserializer, resolver) = dectx.adr();
        Ok(addresses.next_point(deserializer, resolver.clone(), self.factory.clone())?)
    }

    fn extend(&self, _mentionable: Self::Mtbl, tail: &[u8]) -> ParseResult<'a, Ctx, Self> {
        Err(PointParseError::WrongLength(HASH_SIZE + tail.len()))
    }
}