use interface;
use interface::Term;
use print::ATermWrite;

use std::fmt;
use std::rc::Rc;
use std::marker::PhantomData;
use std::iter;

pub mod shared;

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct ATerm<'s, B>(interface::ATermInner<Rc<ATerm<'s, B>>, B, &'s str>);

impl<'s, B> ATerm<'s, B> {
    pub fn no_annos(term: Term<Rc<ATerm<'s, B>>, B, &'s str>) -> Self {
        ATerm(interface::ATermInner::no_annos(term))
    }

    pub fn with_annos<A>(term: Term<Rc<ATerm<'s, B>>, B, &'s str>, annos: A) -> Self
        where A: IntoIterator<Item = Rc<ATerm<'s, B>>>
    {
        ATerm(interface::ATermInner::with_annos(term, annos))
    }
}

impl<'s, B> interface::ATerm for ATerm<'s, B> {
    type Rec = Rc<ATerm<'s, B>>;
    type Blob = B;
    type Cons = &'s str;

    #[inline]
    fn into_inner(self) -> interface::ATermInner<Rc<ATerm<'s, B>>, B, &'s str> {
        self.0
    }
    #[inline]
    fn as_inner(&self) -> &interface::ATermInner<Rc<ATerm<'s, B>>, B, &'s str> {
        &self.0
    }
}

impl<'s, B> fmt::Display for ATerm<'s, B>
    where B: ATermWrite
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.to_ascii(f)
    }
}

impl<'a, 's, B: 'a + ATermWrite> ATermWrite for ATerm<'s, B> {
    #[inline]
    fn to_ascii<W: fmt::Write>(&self, writer: &mut W) -> fmt::Result {
        self.0.to_ascii(writer)
    }

    #[inline]
    fn to_taf<W: fmt::Write>(&self, writer: &mut W) -> fmt::Result {
        self.0.to_taf(writer)
    }

    #[inline]
    fn to_baf<W: fmt::Write>(&self, writer: &mut W) -> fmt::Result {
        self.0.to_baf(writer)
    }

    #[inline]
    fn to_saf<W: fmt::Write>(&self, writer: &mut W) -> fmt::Result {
        self.0.to_saf(writer)
    }
}

#[derive(Default, Debug)]
pub struct ATermFactory<'s, B> {
    _nothing: PhantomData<ATerm<'s, B>>,
}

impl<'s, B> ATermFactory<'s, B> {
    pub fn new() -> Self {
        ATermFactory { _nothing: PhantomData }
    }
}

impl<'s, B: Clone> interface::ATermFactory<B, &'s str> for ATermFactory<'s, B> {
    type ATerm = ATerm<'s, B>;
    type ATermRef = Rc<ATerm<'s, B>>;

    fn no_annos(&self, term: Term<Rc<ATerm<'s, B>>, B, &'s str>) -> Self::ATermRef {
        Rc::new(ATerm::no_annos(term))
    }

    fn with_annos<A>(&self, term: Term<Rc<ATerm<'s, B>>, B, &'s str>, annos: A) -> Self::ATermRef
        where A: IntoIterator<Item = Self::ATermRef>
    {
        Rc::new(ATerm::with_annos(term, annos))
    }

    fn t_string(&self, value: &'s str) -> Term<Self::ATermRef, B, &'s str> {
        self.t_application(value, iter::empty())
    }

    fn t_tuple<I>(&self, children: I) -> Term<Self::ATermRef, B, &'s str> where I: IntoIterator<Item=Self::ATermRef> {
        self.t_application("", children)
    }
}
