use iref::{Iri, IriRef, IriRefBuf};
use smallvec::SmallVec;
pub mod definition;
mod print;
pub mod term_definition;
mod try_from_json;
pub use definition::Definition;
pub use term_definition::TermDefinition;
pub use try_from_json::InvalidContext;
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum Context {
	One(ContextEntry),
	Many(Vec<ContextEntry>),
}
impl Default for Context {
	fn default() -> Self {
		Self::Many(Vec::new())
	}
}
impl Context {
	pub fn one(context: ContextEntry) -> Self {
		Self::One(context)
	}
	pub fn null() -> Self {
		Self::one(ContextEntry::Null)
	}
	pub fn iri_ref(iri_ref: IriRefBuf) -> Self {
		Self::one(ContextEntry::IriRef(iri_ref))
	}
	pub fn definition(def: Definition) -> Self {
		Self::one(ContextEntry::Definition(def))
	}
}
impl Context {
	pub fn len(&self) -> usize {
		match self {
			Self::One(_) => 1,
			Self::Many(l) => l.len(),
		}
	}
	pub fn is_empty(&self) -> bool {
		match self {
			Self::One(_) => false,
			Self::Many(l) => l.is_empty(),
		}
	}
	pub fn as_slice(&self) -> &[ContextEntry] {
		match self {
			Self::One(c) => std::slice::from_ref(c),
			Self::Many(list) => list,
		}
	}
	pub fn is_object(&self) -> bool {
		match self {
			Self::One(c) => c.is_object(),
			_ => false,
		}
	}
	pub fn is_array(&self) -> bool {
		matches!(self, Self::Many(_))
	}
	pub fn traverse(&self) -> Traverse {
		match self {
			Self::One(c) => Traverse::new(FragmentRef::Context(c)),
			Self::Many(m) => Traverse::new(FragmentRef::ContextArray(m)),
		}
	}
	pub fn iter(&self) -> std::slice::Iter<ContextEntry> {
		self.as_slice().iter()
	}
}
pub enum IntoIter {
	One(Option<ContextEntry>),
	Many(std::vec::IntoIter<ContextEntry>),
}
impl Iterator for IntoIter {
	type Item = ContextEntry;
	fn next(&mut self) -> Option<Self::Item> {
		match self {
			Self::One(t) => t.take(),
			Self::Many(t) => t.next(),
		}
	}
}
impl IntoIterator for Context {
	type Item = ContextEntry;
	type IntoIter = IntoIter;
	fn into_iter(self) -> Self::IntoIter {
		match self {
			Self::One(t) => IntoIter::One(Some(t)),
			Self::Many(t) => IntoIter::Many(t.into_iter()),
		}
	}
}
impl<'a> IntoIterator for &'a Context {
	type IntoIter = std::slice::Iter<'a, ContextEntry>;
	type Item = &'a ContextEntry;
	fn into_iter(self) -> Self::IntoIter {
		self.iter()
	}
}
impl From<ContextEntry> for Context {
	fn from(c: ContextEntry) -> Self {
		Self::One(c)
	}
}
impl From<IriRefBuf> for Context {
	fn from(i: IriRefBuf) -> Self {
		Self::One(ContextEntry::IriRef(i))
	}
}
impl<'a> From<&'a IriRef> for Context {
	fn from(i: &'a IriRef) -> Self {
		Self::One(ContextEntry::IriRef(i.to_owned()))
	}
}
impl From<iref::IriBuf> for Context {
	fn from(i: iref::IriBuf) -> Self {
		Self::One(ContextEntry::IriRef(i.into()))
	}
}
impl<'a> From<&'a Iri> for Context {
	fn from(i: &'a Iri) -> Self {
		Self::One(ContextEntry::IriRef(i.to_owned().into()))
	}
}
impl From<Definition> for Context {
	fn from(c: Definition) -> Self {
		Self::One(ContextEntry::Definition(c))
	}
}
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(
	feature = "serde",
	derive(serde::Serialize, serde::Deserialize),
	serde(untagged)
)]
pub enum ContextEntry {
	Null,
	IriRef(IriRefBuf),
	Definition(Definition),
}
impl ContextEntry {
	fn sub_items(&self) -> ContextSubFragments {
		match self {
			Self::Definition(d) => ContextSubFragments::Definition(Box::new(d.iter())),
			_ => ContextSubFragments::None,
		}
	}
	pub fn is_object(&self) -> bool {
		matches!(self, Self::Definition(_))
	}
}
impl From<IriRefBuf> for ContextEntry {
	fn from(i: IriRefBuf) -> Self {
		ContextEntry::IriRef(i)
	}
}
impl<'a> From<&'a IriRef> for ContextEntry {
	fn from(i: &'a IriRef) -> Self {
		ContextEntry::IriRef(i.to_owned())
	}
}
impl From<iref::IriBuf> for ContextEntry {
	fn from(i: iref::IriBuf) -> Self {
		ContextEntry::IriRef(i.into())
	}
}
impl<'a> From<&'a Iri> for ContextEntry {
	fn from(i: &'a Iri) -> Self {
		ContextEntry::IriRef(i.to_owned().into())
	}
}
impl From<Definition> for ContextEntry {
	fn from(c: Definition) -> Self {
		ContextEntry::Definition(c)
	}
}
pub enum FragmentRef<'a> {
	ContextArray(&'a [ContextEntry]),
	Context(&'a ContextEntry),
	DefinitionFragment(definition::FragmentRef<'a>),
}
impl<'a> FragmentRef<'a> {
	pub fn is_array(&self) -> bool {
		match self {
			Self::ContextArray(_) => true,
			Self::DefinitionFragment(i) => i.is_array(),
			_ => false,
		}
	}
	pub fn is_object(&self) -> bool {
		match self {
			Self::Context(c) => c.is_object(),
			Self::DefinitionFragment(i) => i.is_object(),
			_ => false,
		}
	}
	pub fn sub_items(&self) -> SubFragments<'a> {
		match self {
			Self::ContextArray(a) => SubFragments::ContextArray(a.iter()),
			Self::Context(c) => SubFragments::Context(c.sub_items()),
			Self::DefinitionFragment(d) => SubFragments::Definition(Box::new(d.sub_items())),
		}
	}
}
pub enum ContextSubFragments<'a> {
	None,
	Definition(Box<definition::Entries<'a>>),
}
impl<'a> Iterator for ContextSubFragments<'a> {
	type Item = FragmentRef<'a>;
	fn next(&mut self) -> Option<Self::Item> {
		match self {
			Self::None => None,
			Self::Definition(e) => e
				.next()
				.map(|e| FragmentRef::DefinitionFragment(definition::FragmentRef::Entry(e))),
		}
	}
}
pub enum SubFragments<'a> {
	ContextArray(std::slice::Iter<'a, ContextEntry>),
	Context(ContextSubFragments<'a>),
	Definition(Box<definition::SubItems<'a>>),
}
impl<'a> Iterator for SubFragments<'a> {
	type Item = FragmentRef<'a>;
	fn next(&mut self) -> Option<Self::Item> {
		match self {
			Self::ContextArray(a) => a.next().map(FragmentRef::Context),
			Self::Context(i) => i.next(),
			Self::Definition(i) => i.next().map(FragmentRef::DefinitionFragment),
		}
	}
}
pub struct Traverse<'a> {
	stack: SmallVec<[FragmentRef<'a>; 8]>,
}
impl<'a> Traverse<'a> {
	pub(crate) fn new(item: FragmentRef<'a>) -> Self {
		let mut stack = SmallVec::new();
		stack.push(item);
		Self { stack }
	}
}
impl<'a> Iterator for Traverse<'a> {
	type Item = FragmentRef<'a>;
	fn next(&mut self) -> Option<Self::Item> {
		match self.stack.pop() {
			Some(item) => {
				self.stack.extend(item.sub_items());
				Some(item)
			}
			None => None,
		}
	}
}
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ContextDocument {
	#[cfg_attr(feature = "serde", serde(rename = "@context"))]
	pub context: Context,
}