#![allow(unknown_lints)]

use interface;
use interface::Term;
use utils::insert_or_get;
use rc::ATerm;

use std::cell::RefCell;
use std::rc::Rc;
use std::collections::HashSet;
use std::hash::Hash;
use std::marker::PhantomData;
use std::iter;

#[derive(Default, Debug)]
pub struct ATermFactory<'s, B: Hash + Eq> {
    pub arena: RefCell<HashSet<Rc<ATerm<'s, B>>>>,
    _nothing: PhantomData<B>,
}

impl<'t, 's : 't, B: Hash + Eq> ATermFactory<'s, B> {
    pub fn new() -> Self {
        ATermFactory {
            arena: RefCell::new(HashSet::new()),
            _nothing: PhantomData,
        }
    }

    pub fn with_capacity(capacity: usize) -> Self {
        ATermFactory {
            arena: RefCell::new(HashSet::with_capacity(capacity)),
            _nothing: PhantomData,
        }
    }
}

impl<'s, B: Clone + Hash + Eq> 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 {
        insert_or_get(&mut self.arena.borrow_mut(), Rc::new(ATerm::no_annos(term))).clone()
    }

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

    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)
    }
}

impl<'t, 's : 't, B: Clone + Hash + Eq> interface::SharedATermFactory<B, &'s str> for ATermFactory<'s, B> {
    fn get_shared(&self, value: Self::ATermRef) -> Self::ATermRef
        where Self::ATermRef: Clone
    {
        insert_or_get(&mut self.arena.borrow_mut(), value.clone()).clone()
    }
}
