//! Core module for ADN functionality.
//! Brings [`Mentionable`]/[`Factory`]/[`Origin`] concepts from the original implementation in Python.
//! Allows for more generic behaviour via [`Context`], as opposed to original async-only.

mod addresses;
mod context;
mod dectx;
mod demoted;
mod diagnostic;
mod hashing;
mod inctx;
mod inlining;
mod modes;
mod origin;
mod point;
mod points;
mod regular;
mod resolution;
mod resolver_origin;

use std::{error::Error, sync::Arc};

use crate::func::context::*;
use crate::func::*;
use crate::mode::*;

pub use self::context::Context;
pub use self::demoted::Demoted;
pub use self::diagnostic::Diagnostic;
pub use self::hashing::{Hash, HASH_SIZE, HASH_ZEROS};
pub use self::inctx::InCtx;
pub use self::inlining::{CInliningFactory, IParseResult, InliningFactory};
pub use self::modes::{
    ExtensionResultM, ExtensionSourceM, FactoryModeParse, FactoryModeProxy, ModeResultM,
};
pub use self::origin::{OFctr, Origin, OriginMap};
pub use self::point::Point;
pub use self::points::PointsVisitor;
pub use self::regular::{CRegularFactory, RegularFactory};
pub use self::resolution::{
    Address, HashResolution, HashResolutionResult, LookupError, Resolution, ResolutionError,
    ResolutionFailure, ResolutionResult, Resolver, ResolverMap,
};

/// Helper alias for [`WeakFunctor::F`] of [`FunctorContext::T`].
pub type Wrapped<'a, Ctx, A> = WrapC<'a, A, Ctx>;

/// [Mentionable] base.
pub trait MentionableBase<'a, Ctx: Context<'a>>: 'a + Send + Sync + Serializable + Sized {
    /// Type of the associated factory.
    type Fctr: FactoryBase<'a, Ctx, Mtbl = Self>;

    /// Value of the associated factory.
    fn factory(&self) -> Self::Fctr;
}

/// Topological [Mentionable]. Allows iterating over [Point]s it references, if any;
pub trait MentionableTop<'a, Ctx: Context<'a>>: 'a {
    /// See implementation for the definition.
    /// Hash of all the references' points concatenated, ordered, non-unique.
    /// Used for walking over object trees to ensure two objects with different references don't collide.
    fn topology(&self) -> Hash
    where
        Self: Mentionable<'a, Ctx>,
    {
        let mut vec = Vec::new();
        self.points_typed(&mut vec);
        Ctx::hash(&vec)
    }
    /// References ([Point]s) to other objects. Typed.
    fn points_typed(&self, points: &mut impl PointsVisitor<'a, Ctx>)
    where
        Self: Mentionable<'a, Ctx>;
}

/// Fundamental trait for ADN objects.
pub trait Mentionable<'a, Ctx: Context<'a>>:
    MentionableBase<'a, Ctx, Fctr = Self::_Fctr> + MentionableTop<'a, Ctx>
{
    type _Fctr: Factory<'a, Ctx, _Mtbl = Self>;
}

impl<'a, Ctx: Context<'a>, A: MentionableBase<'a, Ctx> + MentionableTop<'a, Ctx>>
    Mentionable<'a, Ctx> for A
where
    Self::Fctr: Factory<'a, Ctx, _Mtbl = Self>,
{
    type _Fctr = Self::Fctr;
}

/// [`Factory`] associated with the [`Mentionable`]. Mostly useful for `type` definitions.
pub type Fctr<'a, Ctx, A> = <A as MentionableBase<'a, Ctx>>::Fctr;

/// Shorthand for the type of values returned by [`FactoryParse::deserialize`].
pub type ParseResult<'a, Ctx, F> = Result<Mtbl<'a, Ctx, F>, ParseError<'a, Ctx, F>>;

/// [`ParseResult`] associated with a [`Mentionable`] (instead of a [`Factory`]).
pub type ParseResultA<'a, Ctx, A> = Result<A, ParseErrorA<'a, Ctx, A>>;

/// [Factory] base.
pub trait FactoryBase<'a, Ctx: Context<'a>>: 'a + Send + Sync + Clone {
    /// Type of the associated objects.
    type Mtbl: MentionableBase<'a, Ctx, Fctr = Self>;
    /// Type of an error that [`FactoryParse::deserialize`] can fail with.
    type ParseError: 'a + Send + Error;
}

/// [Factory] that allows parsing consuming the parser.
pub trait FactoryParse<'a, Ctx: Context<'a>>: FactoryModeParse<'a, Ctx> {
    /// Consumes the parser. See [`Deserializer`], [`Resolver`].
    fn deserialize(&self, inctx: impl InCtx<'a, Ctx>) -> ParseResult<'a, Ctx, Self>;
    /// Called by finite stream parsers if there's any data left.
    fn extend(&self, mentionable: Self::Mtbl, tail: &[u8]) -> ParseResult<'a, Ctx, Self>;
}

/// Trait representing deserialisation rules for [Mentionable]s.
/// Crucial for [typeless]ness.
///
/// [typeless]: crate::rstd::typeless
pub trait Factory<'a, Ctx: Context<'a>>:
    FactoryParse<'a, Ctx, Mtbl = Self::_Mtbl> + ParseMode
{
    type _Mtbl: MentionableBase<'a, Ctx, Fctr = Self> + MentionableTop<'a, Ctx>;
}

/// [`Mentionable`] associated with the [`Factory`]. Mostly useful for `type` definitions.
pub type Mtbl<'a, Ctx, F> = <F as FactoryBase<'a, Ctx>>::Mtbl;

/// [`FactoryBase::ParseError`]. Mostly useful for `type` definitions.
pub type ParseError<'a, Ctx, F> = <F as FactoryBase<'a, Ctx>>::ParseError;

/// [`FactoryBase::ParseError`] associated with the [`Mentionable`].
/// Mostly useful for `type` definitions.
pub type ParseErrorA<'a, Ctx, A> = ParseError<'a, Ctx, Fctr<'a, Ctx, A>>;

/// Extension trait for factories.
pub trait FactoryExt<'a, Ctx: Context<'a>>: FactoryParse<'a, Ctx> {
    /// Parse the object from a slice.
    fn parse_slice(
        &self,
        slice: &[u8],
        resolver: &Arc<dyn Resolver<'a, Ctx>>,
    ) -> ParseResult<'a, Ctx, Self>;
}