/*!
# Writer for restricted XML 1.0
*/
use alloc::boxed::Box;
use alloc::collections::btree_map::Entry;
use alloc::collections::BTreeMap;
use alloc::collections::BTreeSet;
use alloc::format;
use alloc::vec::Vec;

use bytes::{BufMut, BytesMut};

use crate::error::{Error, ErrorContext, Result};
use crate::parser::{Event, XmlVersion, XMLNS_XML, XMLNS_XMLNS};
use crate::strings::{Name, Namespace, NcName, NcNameStr};

const XML_DECL: &[u8] = b"<?xml version='1.0' encoding='utf-8'?>\n";

/// The static prefix used for XML built-in attributes (`xml`).
pub const PREFIX_XML: &NcNameStr = unsafe { core::mem::transmute("xml") };

/// The static prefix used for XML namespace declarations (`xmlns`).
pub const PREFIX_XMLNS: &NcNameStr = unsafe { core::mem::transmute("xmlns") };

fn escape_text<'a, B: BufMut>(
	out: &'a mut B,
	data: &'a str,
	attribute_context: bool,
) -> core::result::Result<(), rxml_validation::Error> {
	let data = data.as_bytes();
	let mut last_index = 0;
	for i in 0..data.len() {
		let ch = data[i];
		let escaped: &[u8] = match ch {
			b'\x00'..=b'\x08' | b'\x0b' | b'\x0c' | b'\x0e'..=b'\x1f' => {
				return Err(rxml_validation::Error::InvalidChar(ch.into()));
			}
			b'"' if attribute_context => &b"&#34;"[..],
			b'\'' if attribute_context => &b"&#39;"[..],
			b'<' => &b"&lt;"[..],
			b'>' => &b"&gt;"[..],
			b'&' => &b"&amp;"[..],
			b'\r' => &b"&#xd;"[..],
			b'\n' if attribute_context => &b"&#xa;"[..],
			b'\t' if attribute_context => &b"&#x9;"[..],
			b'\xbe' | b'\xbf' => {
				if i >= 2 && data[i - 2] == b'\xef' && data[i - 1] == b'\xbf' {
					// U+FFFE or U+FFFF
					let low_bit = ch & 0x01;
					// SAFETY: we are passing only 16 bits and the upper
					// nibble is set to all ones, so this is within the bounds
					// of a unicode code point and not a surrogate.
					return Err(rxml_validation::Error::InvalidChar(unsafe {
						char::from_u32_unchecked(0xfffe | low_bit as u32)
					}));
				} else {
					continue;
				}
			}
			_ => continue,
		};
		if i > last_index {
			out.put_slice(&data[last_index..i]);
		}
		out.put_slice(escaped);
		last_index = i + 1;
	}
	out.put_slice(&data[last_index..data.len()]);
	Ok(())
}

/// An encodable item.
///
/// This is separate from [`Event`], because events are owned, while
/// items can be borrowed to improve efficiency (as a copy will have to take
/// place anyway).
///
///   [`Event`]: crate::parser::Event
#[derive(Debug)]
pub enum Item<'x> {
	/// XML declaration
	XmlDeclaration(XmlVersion),

	/// Start of an element header
	ElementHeadStart(
		/// Namespace name
		Namespace<'x>,
		/// Local name of the attribute
		&'x NcNameStr,
	),

	/// An attribute key/value pair
	Attribute(
		/// Namespace name
		Namespace<'x>,
		/// Local name of the attribute
		&'x NcNameStr,
		/// Value of the attribute
		&'x str,
	),

	/// End of an element header
	ElementHeadEnd,

	/// A piece of text (in element content, not attributes)
	Text(&'x str),

	/// Footer of an element
	///
	/// This can be used either in places where [`Text`] could be used to
	/// close the most recently opened unclosed element, or it can be used
	/// instead of [`ElementHeadEnd`] to close the element using `/>`, without
	/// any child content.
	///
	///   [`Text`]: Self::Text
	///   [`ElementHeadEnd`]: Self::ElementHeadEnd
	ElementFoot,
}

impl Item<'_> {
	fn name(&self) -> &'static str {
		match self {
			Self::XmlDeclaration { .. } => "xml declaration",
			Self::ElementHeadStart { .. } => "element start",
			Self::Attribute { .. } => "attribute",
			Self::ElementHeadEnd => "element head end",
			Self::Text { .. } => "text",
			Self::ElementFoot => "element foot",
		}
	}
}

/// Represent an error returned by a prefix operation on [`TrackNamespace`].
#[derive(Debug)]
pub enum PrefixError {
	/// The requested prefix is not declared yet.
	Undeclared,
}

/// Trait for a thing tracking namespace declarations.
///
/// Indirection via this trait allows to have different paradigms for
/// declaring and managing namespace prefixes.
///
/// Objects implementing this trait expect the following protocol:
///
/// 1. Declare all namespace URIs introduced on an element using `declare` and `declare_auto`.
/// 2. Commit to the element using `push`
/// 3. Process all child elements by recursion
/// 4. Call `pop` to end the element.
///
/// Asymmetric calls to push/pop may cause panics or memory leaks.
pub trait TrackNamespace {
	/// Declare a namespace URI with a defined prefix.
	///
	/// Note: There is no guarantee that the given `prefix` will be returned
	/// from calls to `get_prefix` or `get_prefix_or_default` after the next
	/// call to `push`.
	///
	/// Returns whether the prefix is freshly declared.
	///
	/// # Panics
	///
	/// Calling this twice between two calls to `push` with the same `prefix`
	/// is a programming error and causes a panic.
	fn declare_fixed(&mut self, prefix: Option<&NcNameStr>, name: Namespace<'static>) -> bool;

	/// Declare a namespace URI with an auto-generated prefix or by using the
	/// default namespace.
	///
	/// Note: There is no guarantee that the returned `prefix` will be
	/// returned from calls to `get_prefix_or_default` after the next call to
	/// `push`.
	///
	/// Returns whether the prefix is freshly declared and the
	/// resulting prefix (or None, if prefixless).
	///
	/// This may return a non-auto-generated prefix if the namespace URI is
	/// already declared on this or a parent element.
	fn declare_auto(&mut self, name: Namespace<'static>) -> (bool, Option<&NcNameStr>);

	/// Declare a namespace URI with an auto-generated prefix.
	///
	/// Note: There is no guarantee that the returned `prefix` will be
	/// returned from calls to `get_prefix` or `get_prefix_or_default` after
	/// the next call to `push`.
	///
	/// Returns whether the prefix is freshly declared and the resulting
	/// prefix.
	///
	/// This may return a non-auto-generated prefix if the namespace URI is
	/// already declared on this or a parent element. If the URI is already
	/// used for the default namespace, this function will nontheless return
	/// a prefix.
	fn declare_with_auto_prefix(&mut self, name: Namespace<'static>) -> (bool, &NcNameStr);

	/// Get the prefix for a given URI, which may be empty if the namespace
	/// with that URI is defined as the default namespace.
	fn get_prefix_or_default(
		&self,
		name: Namespace<'static>,
	) -> core::result::Result<Option<&NcNameStr>, PrefixError>;

	/// Get the prefix for a given URI.
	///
	/// This returns an error if the given URI is declared as default
	/// namespace and there is no matching prefix.
	fn get_prefix(&self, name: Namespace<'static>)
		-> core::result::Result<&NcNameStr, PrefixError>;

	/// Complete an element declaration.
	fn push(&mut self);

	/// Signal end of element to undeclare nested namespace declarations.
	fn pop(&mut self);

	/// Return the newly declared default namespace, if any.
	///
	/// This returns `None` if and only if no default namespace has been
	/// declared yet for the upcoming element.
	///
	/// *Note:* In the root element, `None` will be returned even if the
	/// default namespace has been explicitly set to the empty namespace name,
	/// as that is the default.
	fn new_default_declaration(&self) -> Option<&Namespace<'static>>;

	/// Return an iterator over the the newly declared prefixes.
	fn new_prefix_declarations(
		&self,
	) -> Box<dyn Iterator<Item = (&Namespace<'static>, &NcNameStr)> + '_>;
}

/// Simple namespace tracker.
///
/// This is the default namespace tracker used by [`Encoder::new`]. At the
/// cost of increased output size, it reduces memory footprint by only
/// tracking unprefixed namespaces. Prefixed namespaces may be declared, but
/// are forgotten about once the element start is over.
///
/// Effectively, when used with an [`Encoder`], this means that prefixed
/// namespaces will only ever be used for attributes, and may be re-declared
/// a lot.
///
/// One exception is that prefixed namespaces declared on the root element
/// will actually be made available on all child elements.
#[derive(Debug, Default)]
pub struct SimpleNamespaces {
	// persistent state
	global_ns: BTreeMap<Namespace<'static>, NcName>,
	global_ns_rev: BTreeSet<NcName>,
	global_ns_ctr: usize,
	default_ns_stack: Vec<Namespace<'static>>,

	// temporary per-element state
	next_default_ns: Option<Namespace<'static>>,
	temp_ns_ctr: usize,
	temp_ns: BTreeMap<Namespace<'static>, NcName>,
	temp_ns_rev: BTreeSet<NcName>,
}

impl SimpleNamespaces {
	/// Create a new, empty namespace tracker.
	pub fn new() -> Self {
		Self::default()
	}

	/// Look up the namespace URI for a given prefix
	///
	/// *Note:* This function is implemented as O(n) function because it
	/// should rarely, if ever, be necessary to use. Speeding up runtime of
	/// this function would increase memory cost at neglegible gain.
	///
	/// The namespace URIs for the `xml` and `xmlns` prefixes are always
	/// returned, even if not explicitly declared.
	pub fn lookup_prefix(
		&self,
		prefix: Option<&NcNameStr>,
	) -> core::result::Result<Namespace<'static>, PrefixError> {
		match prefix {
			Some(prefix) if prefix == PREFIX_XML => Ok(Namespace::XML),
			Some(prefix) if prefix == PREFIX_XMLNS => Ok(Namespace::XMLNS),
			Some(prefix) => {
				for (decl_uri, decl_prefix) in self.temp_ns.iter().chain(self.global_ns.iter()) {
					if decl_prefix == prefix {
						return Ok(decl_uri.clone());
					}
				}
				Err(PrefixError::Undeclared)
			}
			None => {
				if let Some(uri) = self.next_default_ns.as_ref() {
					return Ok(uri.clone());
				}
				match self.default_ns_stack.last() {
					Some(uri) => Ok(uri.clone()),
					_ => Err(PrefixError::Undeclared),
				}
			}
		}
	}
}

impl TrackNamespace for SimpleNamespaces {
	fn declare_fixed(&mut self, prefix: Option<&NcNameStr>, name: Namespace<'static>) -> bool {
		match prefix.as_ref() {
			Some(v) if *v == PREFIX_XML => {
				if name == XMLNS_XML {
					return false;
				}
				panic!("xml is a reserved prefix")
			}
			Some(v) if *v == PREFIX_XMLNS => {
				if name == XMLNS_XMLNS {
					return false;
				}
				panic!("xmlns is a reserved prefix")
			}
			_ => {}
		}

		if name == XMLNS_XML {
			panic!("{} must be bound to xml prefix", name)
		}
		if name == XMLNS_XMLNS {
			panic!("{} must be bound to xmlns prefix", name)
		}

		match prefix {
			Some(prefix) => {
				if self.global_ns_rev.contains(prefix) {
					panic!(
						"prefix declaration conflicts with global prefix: {:?}",
						prefix
					)
				}
				if self.temp_ns_rev.contains(prefix) {
					panic!("duplicate prefix: {:?}", prefix);
				}
				self.temp_ns.insert(name, prefix.to_ncname());
				self.temp_ns_rev.insert(prefix.to_ncname());
				true
			}
			None => {
				if self.next_default_ns.is_some() {
					panic!("duplicate default namespace")
				}
				self.next_default_ns = Some(name);
				true
			}
		}
	}

	fn declare_auto(&mut self, name: Namespace<'static>) -> (bool, Option<&NcNameStr>) {
		if name == XMLNS_XML {
			return (false, Some(PREFIX_XML));
		}
		if name == XMLNS_XMLNS {
			return (false, Some(PREFIX_XMLNS));
		}

		// XXX: due to a limitation in the borrowchecker <https://github.com/rust-lang/rfcs/blob/master/text/2094-nll.md#problem-case-3-conditional-control-flow-across-functions>, we have to re-implement the entirety of get_prefix_or_default here.

		// Check if the namespace has been declared without prefix
		match self
			.next_default_ns
			.as_ref()
			.or(self.default_ns_stack.last())
		{
			// .. it has, return it prefixless.
			Some(default_name) if *default_name == name => return (false, None),
			// .. it has not, move on.
			_ => (),
		};

		// Check if the namespace has been declared locally already
		match self.temp_ns.entry(name) {
			// .. it has, return the prefix.
			Entry::Occupied(o) => (false, Some(o.into_mut())),
			// .. it has not, check if it has been declared globally
			Entry::Vacant(v_temp) => match self.global_ns.entry(v_temp.key().clone()) {
				// .. it has, return the prefix.
				Entry::Occupied(o) => (false, Some(o.into_mut())),
				// .. it has not, declare it.
				Entry::Vacant(_) => {
					// checked above already that it does not match the default namespace, but we need to see whether we can use the default namespace.
					match self.next_default_ns.as_ref() {
						// the default namespace has been declared at this element already, so we need to create a prefix.
						Some(_) => {
							let ctr = self.temp_ns_ctr;
							let temp_ns_prefix: NcName = format!("tns{}", ctr)
								.try_into()
								.expect("auto-generated prefix must always be valid");
							if self.global_ns_rev.contains(&temp_ns_prefix) {
								panic!(
									"automatic prefix declaration conflicts with global prefix: {:?}",
									temp_ns_prefix
								)
							}
							if self.temp_ns_rev.contains(&temp_ns_prefix) {
								panic!(
									"automatic prefix declaration conflicts with local prefix: {:?}",
									temp_ns_prefix
								)
							}
							self.temp_ns_ctr += 1;
							self.temp_ns_rev.insert(temp_ns_prefix.clone());
							(true, Some(v_temp.insert(temp_ns_prefix)))
						}
						// the default namespace has *not* been declared at this element yet, so we use it.
						None => {
							let name = v_temp.into_key();
							let new = match self.default_ns_stack.last() {
								Some(v) if v == &name => {
									// re-use the previous instad of cloning this one, might be more efficient.
									self.next_default_ns = Some(v.clone());
									// it wasn't new.
									false
								}
								_ => {
									// treat it as new iff it is in fact a namespace
									// this is to correctly return `new == false` if `declare_auto` is called with the empty namespace name on the root element.
									// this can only happen on the root element, because otherwise default_ns_stack would have had an element and we wouldn't be in this branch.
									let new = !name.is_none();
									// assign the new object as next default namespace
									self.next_default_ns = Some(name);
									new
								}
							};
							(new, None)
						}
					}
				}
			},
		}
	}

	fn declare_with_auto_prefix(&mut self, name: Namespace<'static>) -> (bool, &NcNameStr) {
		if name == XMLNS_XML {
			return (false, PREFIX_XML);
		}
		if name == XMLNS_XMLNS {
			return (false, PREFIX_XMLNS);
		}

		match self.temp_ns.entry(name.clone()) {
			Entry::Occupied(o) => (false, o.into_mut()),
			Entry::Vacant(v) => match self.global_ns.entry(name.clone()) {
				Entry::Occupied(o) => (false, o.into_mut()),
				Entry::Vacant(_) => {
					let ctr = self.temp_ns_ctr;
					let temp_ns_prefix: NcName = format!("tns{}", ctr)
						.try_into()
						.expect("auto-generated prefix must always be valid");
					if self.global_ns_rev.contains(&temp_ns_prefix) {
						panic!(
							"automatic prefix declaration conflicts with global prefix: {:?}",
							temp_ns_prefix
						)
					}
					if self.temp_ns_rev.contains(&temp_ns_prefix) {
						panic!(
							"automatic prefix declaration conflicts with local prefix: {:?}",
							temp_ns_prefix
						)
					}
					self.temp_ns_ctr += 1;
					self.temp_ns_rev.insert(temp_ns_prefix.clone());
					(true, v.insert(temp_ns_prefix))
				}
			},
		}
	}

	fn get_prefix_or_default(
		&self,
		name: Namespace<'static>,
	) -> core::result::Result<Option<&NcNameStr>, PrefixError> {
		match self
			.next_default_ns
			.as_ref()
			.or(self.default_ns_stack.last())
		{
			Some(default_name) if *default_name == name => Ok(None),
			_ => Ok(Some(self.get_prefix(name)?)),
		}
	}

	fn get_prefix(
		&self,
		name: Namespace<'static>,
	) -> core::result::Result<&NcNameStr, PrefixError> {
		self.temp_ns
			.get(&name)
			.or(self.global_ns.get(&name))
			.map(|x| &**x)
			.ok_or(PrefixError::Undeclared)
	}

	fn push(&mut self) {
		match self.next_default_ns.take() {
			None => {
				// duplicate the most recent one
				// if there is no previous one, we go with the default namespace.
				let old = self
					.default_ns_stack
					.last()
					.unwrap_or(Namespace::none())
					.clone();
				self.default_ns_stack.push(old);
			}
			Some(v) => self.default_ns_stack.push(v),
		}
		if self.default_ns_stack.len() == 1 {
			// the first element! globalize the declarations
			core::mem::swap(&mut self.global_ns, &mut self.temp_ns);
			core::mem::swap(&mut self.global_ns_rev, &mut self.temp_ns_rev);
			self.global_ns_ctr = self.temp_ns_ctr;
		}

		self.temp_ns_ctr = self.global_ns_ctr;
		self.temp_ns.clear();
		self.temp_ns_rev.clear();
	}

	fn pop(&mut self) {
		self.default_ns_stack.pop();
	}

	fn new_default_declaration(&self) -> Option<&Namespace<'static>> {
		match self.next_default_ns.as_ref() {
			// if this is the root element, we do not expose the empty default namespace as declaration
			Some(ns) if ns.is_none() && self.default_ns_stack.is_empty() => None,
			other => other,
		}
	}

	fn new_prefix_declarations(
		&self,
	) -> Box<dyn Iterator<Item = (&Namespace<'static>, &NcNameStr)> + '_> {
		Box::new(self.temp_ns.iter().map(|(k, v)| (k, &**v)))
	}
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
enum EncoderState {
	#[default]
	Start,
	Declared,
	ElementHead,
	Content,
	EndOfDocument,
}

impl EncoderState {
	fn ctx(&self) -> Option<ErrorContext> {
		Some(match self {
			Self::Start => ErrorContext::XmlDeclarationStart,
			Self::Declared => ErrorContext::DocumentBegin,
			Self::ElementHead => ErrorContext::Element,
			Self::Content => ErrorContext::Text,
			Self::EndOfDocument => ErrorContext::DocumentEnd,
		})
	}
}

/**
Encodes XML into buffers.

Encoders are stateful. They can only be used to encode a single XML document and have then to be disposed.

```rust
use rxml::{Encoder, Item, XmlVersion};
use bytes::BytesMut;

let mut enc = Encoder::new();
let mut buf = BytesMut::new();
enc.encode(Item::XmlDeclaration(XmlVersion::V1_0), &mut buf);
assert_eq!(&buf[..], b"<?xml version='1.0' encoding='utf-8'?>\n");
```
*/
#[derive(Default)]
pub struct Encoder<T> {
	state: EncoderState,
	qname_stack: Vec<Name>,
	ns: T,
}

impl Encoder<SimpleNamespaces> {
	/// Create a new default encoder.
	///
	/// This encoder uses the [`SimpleNamespaces`] strategy, which is not
	/// optimal with respect to the number of bytes written, but has reduced
	/// memory cost.
	pub fn new() -> Self {
		Self::default()
	}
}

impl<T: TrackNamespace> From<T> for Encoder<T> {
	/// Create an encoder from a [`TrackNamespace`] instance.
	///
	/// Use your own custom namespace strategy.
	fn from(ns: T) -> Self {
		Self {
			state: EncoderState::Start,
			qname_stack: Vec::new(),
			ns,
		}
	}
}

impl<T: TrackNamespace> Encoder<T> {
	fn encode_nsdecl<O: BufMut>(
		prefix: Option<&NcNameStr>,
		nsuri: &str,
		output: &mut O,
	) -> core::result::Result<(), rxml_validation::Error> {
		match prefix {
			Some(prefix) => {
				output.put_slice(b" xmlns:");
				output.put_slice(prefix.as_bytes());
				output.put_slice(b"='");
			}
			None => {
				output.put_slice(b" xmlns='");
			}
		}
		escape_text(output, nsuri, true)?;
		output.put_u8(b'\'');
		Ok(())
	}

	/// Access the namespace tracking implementation.
	pub fn ns_tracker(&self) -> &T {
		&self.ns
	}

	/// Access the namespace tracking implementation, mutably.
	pub fn ns_tracker_mut(&mut self) -> &mut T {
		&mut self.ns
	}

	/// Encode a single item into a buffer.
	///
	/// There is no requirement for the buffer to be the same for subsequent
	/// calls to this function. This allows users to use small, but
	/// long-lived, buffers for serialization before sending data over the
	/// network, for instance.
	pub fn encode<O: BufMut>(&mut self, item: Item<'_>, output: &mut O) -> Result<()> {
		if self.state == EncoderState::EndOfDocument {
			return Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None));
		}

		match item {
			Item::XmlDeclaration(XmlVersion::V1_0) => match self.state {
				EncoderState::Start => {
					output.put_slice(XML_DECL);
					self.state = EncoderState::Declared;
					Ok(())
				}
				_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
			},
			Item::ElementHeadStart(ref nsuri, local_name) => match self.state {
				EncoderState::Start | EncoderState::Declared | EncoderState::Content => {
					output.put_u8(b'<');
					let (_, prefix) = self.ns.declare_auto(nsuri.clone().into_static());
					let qname = match prefix {
						Some(prefix) => {
							output.put_slice(prefix.as_bytes());
							output.put_u8(b':');
							output.put_slice(local_name.as_bytes());
							prefix.with_suffix(local_name)
						}
						None => {
							output.put_slice(local_name.as_bytes());
							local_name.to_name()
						}
					};
					self.qname_stack.push(qname);
					if let Some(name) = self.ns.new_default_declaration() {
						Self::encode_nsdecl(None, name, output)?;
					}
					for (name, prefix) in self.ns.new_prefix_declarations() {
						// if new, we have to declare it
						Self::encode_nsdecl(Some(prefix), name, output)?;
					}
					self.state = EncoderState::ElementHead;
					Ok(())
				}
				_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
			},
			Item::Attribute(ref nsuri, local_name, value) => match self.state {
				EncoderState::ElementHead => {
					match nsuri.as_namespace_name() {
						Some(v) => {
							let (new, prefix) = self
								.ns
								.declare_with_auto_prefix(nsuri.clone().into_static());
							if new {
								Self::encode_nsdecl(Some(prefix), v, output)?;
							}
							output.put_u8(b' ');
							output.put_slice(prefix.as_bytes());
							output.put_u8(b':');
							output.put_slice(local_name.as_bytes());
						}
						None => {
							output.put_u8(b' ');
							output.put_slice(local_name.as_bytes());
						}
					}
					output.put_u8(b'=');
					output.put_u8(b'\'');
					escape_text(output, value, true)?;
					output.put_u8(b'\'');
					Ok(())
				}
				_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
			},
			Item::ElementHeadEnd => match self.state {
				EncoderState::ElementHead => {
					output.put_u8(b'>');
					self.ns.push();
					self.state = EncoderState::Content;
					Ok(())
				}
				_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
			},
			Item::Text(cdata) => match self.state {
				EncoderState::Content => {
					escape_text(output, cdata, false)?;
					Ok(())
				}
				_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
			},
			Item::ElementFoot => match self.state {
				EncoderState::Content => {
					self.ns.pop();
					output.put_slice(b"</");
					output.put_slice(self.qname_stack.pop().unwrap().as_bytes());
					output.put_u8(b'>');
					if self.qname_stack.is_empty() {
						self.state = EncoderState::EndOfDocument
					}
					Ok(())
				}
				EncoderState::ElementHead => {
					output.put_slice(b"/>");
					self.ns.push();
					self.ns.pop();
					self.qname_stack.pop();
					if self.qname_stack.is_empty() {
						self.state = EncoderState::EndOfDocument
					} else {
						self.state = EncoderState::Content;
					}
					Ok(())
				}
				_ => Err(Error::UnexpectedToken(self.state.ctx(), item.name(), None)),
			},
		}
	}

	/// Encode a single item into a BytesMut.
	///
	/// This might have a slight performance advantage over
	/// [`encode`] in some scenarios, as it might be able to give
	/// the BytesMut a heads up about required space, thus avoiding frequent
	/// reallocations.
	///
	/// There is no requirement for the buffer to be the same for subsequent
	/// calls to this function. This allows users to use small, but
	/// long-lived, buffers for serialization before sending data over the
	/// network, for instance.
	///
	///   [`encode`]: Self::encode
	pub fn encode_into_bytes(&mut self, item: Item<'_>, output: &mut BytesMut) -> Result<()> {
		self.encode(item, output)
	}

	/// Encode a single event into a buffer.
	///
	/// This internally decomposes the event into multiple items and then
	/// encodes these into the given buffer using [`encode`].
	///
	///    [`encode`]: Self::encode.
	pub fn encode_event<O: BufMut>(&mut self, ev: &Event, output: &mut O) -> Result<()> {
		match ev {
			Event::XmlDeclaration(_, version) => {
				self.encode(Item::XmlDeclaration(*version), output)?;
			}
			Event::StartElement(_, (ns, name), attrs) => {
				self.encode(Item::ElementHeadStart(ns.borrow(), name.as_ref()), output)?;
				for ((ns, name), v) in attrs.iter() {
					self.encode(Item::Attribute(ns.borrow(), name.as_ref(), v), output)?
				}
				self.encode(Item::ElementHeadEnd, output)?;
			}
			Event::EndElement(_) => self.encode(Item::ElementFoot, output)?,
			Event::Text(_, text) => self.encode(Item::Text(text.as_ref()), output)?,
		}
		Ok(())
	}

	/// Encode a single event into a BytesMut.
	///
	/// This internally decomposes the event into multiple items and then
	/// encodes these into the given buffer using [`encode_into_bytes`].
	///
	///    [`encode_into_bytes`]: Self::encode_into_bytes.
	pub fn encode_event_into_bytes(&mut self, ev: &Event, output: &mut BytesMut) -> Result<()> {
		self.encode_event(ev, output)
	}
}

#[cfg(test)]
mod tests_simple_namespaces {
	use super::*;

	fn ns1() -> Namespace<'static> {
		Namespace::try_from("uri:foo").unwrap()
	}

	fn ns2() -> Namespace<'static> {
		Namespace::try_from("uri:bar").unwrap()
	}

	fn ns3() -> Namespace<'static> {
		Namespace::try_from("uri:baz").unwrap()
	}

	fn mk() -> SimpleNamespaces {
		SimpleNamespaces::new()
	}

	#[test]
	fn prefers_setting_default_namespace() {
		let mut ns = mk();
		let (new, prefix) = ns.declare_auto(ns1());
		assert!(new);
		assert!(prefix.is_none());
	}

	#[test]
	fn unset_namespace_is_not_new_initially() {
		let mut ns = mk();
		let (new, prefix) = ns.declare_auto(Namespace::NONE);
		assert!(prefix.is_none());
		assert!(!new);
	}

	#[test]
	fn predefined_prefixes_can_be_looked_up() {
		let ns = mk();
		assert_eq!(ns.lookup_prefix(Some(PREFIX_XML)).unwrap(), XMLNS_XML);
		assert_eq!(ns.lookup_prefix(Some(PREFIX_XMLNS)).unwrap(), XMLNS_XMLNS);
	}

	#[test]
	fn hardcoded_xmlns_namespace_for_declare_auto() {
		let mut ns = mk();
		let (new, prefix) = ns.declare_auto(Namespace::XMLNS);
		assert!(!new);
		assert!(prefix.unwrap() == PREFIX_XMLNS);
	}

	#[test]
	fn hardcoded_xml_namespace_for_declare_auto() {
		let mut ns = mk();
		let (new, prefix) = ns.declare_auto(Namespace::XML);
		assert!(!new);
		assert!(prefix.unwrap() == PREFIX_XML);
	}

	#[test]
	fn hardcoded_xmlns_namespace_for_declare_with_auto_prefix() {
		let mut ns = mk();
		let (new, prefix) = ns.declare_with_auto_prefix(Namespace::XMLNS);
		assert!(!new);
		assert!(prefix == "xmlns");
	}

	#[test]
	fn hardcoded_xml_namespace_for_declare_with_auto_prefix() {
		let mut ns = mk();
		let (new, prefix) = ns.declare_with_auto_prefix(Namespace::XML);
		assert!(!new);
		assert!(prefix == "xml");
	}

	#[test]
	fn hardcoded_xmlns_namespace_for_declare_fixed() {
		let mut ns = mk();
		let new = ns.declare_fixed(Some(PREFIX_XMLNS), Namespace::XMLNS);
		assert!(!new);
	}

	#[test]
	fn hardcoded_xml_namespace_for_declare_fixed() {
		let mut ns = mk();
		let new = ns.declare_fixed(Some(PREFIX_XML), Namespace::XML);
		assert!(!new);
	}

	#[test]
	#[should_panic(expected = "xml is a reserved prefix")]
	fn reject_xml_as_fixed_prefix_for_random_namespace() {
		let mut ns = mk();
		ns.declare_fixed(Some(PREFIX_XML), ns1());
	}

	#[test]
	#[should_panic(expected = "xmlns is a reserved prefix")]
	fn reject_xmlns_fixed_prefix_for_random_namespace() {
		let mut ns = mk();
		ns.declare_fixed(Some(PREFIX_XMLNS), ns1());
	}

	#[test]
	#[should_panic(expected = "must be bound to xml prefix")]
	fn reject_xml_namespace_with_other_prefix() {
		let mut ns = mk();
		ns.declare_fixed(Some("foo".try_into().unwrap()), Namespace::XML);
	}

	#[test]
	#[should_panic(expected = "must be bound to xmlns prefix")]
	fn reject_xmlns_namespace_with_other_prefix() {
		let mut ns = mk();
		ns.declare_fixed(Some("foo".try_into().unwrap()), Namespace::XMLNS);
	}

	#[test]
	fn retrieve_default_namespace() {
		let mut ns = mk();
		ns.declare_auto(ns1());
		match ns.get_prefix_or_default(ns1()) {
			Ok(None) => {}
			other => panic!("unexpected get_prefix_or_default result: {:?}", other),
		};
		match ns.lookup_prefix(None) {
			Ok(name) => {
				assert_eq!(name, ns1());
			}
			other => panic!("unexpected lookup_prefix result: {:?}", other),
		}
	}

	#[test]
	fn declaration_causes_lookup_to_return_a_thing() {
		let mut ns = mk();
		match ns.lookup_prefix(None) {
			Err(PrefixError::Undeclared) => (),
			other => panic!("unexpected lookup_prefix result: {:?}", other),
		}
		ns.declare_auto(ns1());
		match ns.lookup_prefix(None) {
			Ok(name) => {
				assert_eq!(name, ns1());
			}
			other => panic!("unexpected lookup_prefix result: {:?}", other),
		}
	}

	#[test]
	fn reuses_existing_declaration_if_available() {
		let mut ns = mk();
		ns.declare_auto(ns1());
		let (new, prefix) = ns.declare_auto(ns1());
		assert!(!new);
		assert!(prefix.is_none());
	}

	#[test]
	fn allocates_prefix_if_default_is_already_set() {
		let mut ns = mk();
		ns.declare_auto(ns1());
		let (new, prefix) = ns.declare_auto(ns2());
		assert!(new);
		assert!(prefix.is_some());
	}

	#[test]
	fn retrieve_prefixed_namespace() {
		let mut ns = mk();
		let prefix = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
		match ns.get_prefix_or_default(ns1()) {
			Ok(Some(v)) => {
				assert_eq!(v, prefix);
			}
			other => panic!("unexpected get_prefix_or_default result: {:?}", other),
		};
	}

	#[test]
	fn declaration_causes_lookup_to_return_a_thing_for_a_prefix() {
		let mut ns = mk();
		match ns.lookup_prefix(None) {
			Err(PrefixError::Undeclared) => (),
			other => panic!("unexpected lookup_prefix result: {:?}", other),
		}
		let prefix = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
		match ns.lookup_prefix(Some(&prefix)) {
			Ok(name) => {
				assert_eq!(name, ns1());
			}
			other => panic!("unexpected lookup_prefix result: {:?}", other),
		}
		match ns.lookup_prefix(None) {
			Err(PrefixError::Undeclared) => (),
			other => panic!("unexpected lookup_prefix result: {:?}", other),
		}
	}

	#[test]
	fn reuses_auto_allocated_prefix_for_same_ns() {
		let mut ns = mk();
		let prefix1 = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
		let prefix2 = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
		assert_eq!(prefix1, prefix2);
	}

	#[test]
	fn auto_allocates_different_prefixes_for_different_ns() {
		let mut ns = mk();
		let prefix1 = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
		let prefix2 = ns.declare_with_auto_prefix(ns2()).1.to_ncname();
		assert_ne!(prefix1, prefix2);
	}

	#[test]
	fn preserves_default_ns_across_elements() {
		let mut ns = mk();
		ns.declare_auto(ns1());
		ns.get_prefix_or_default(ns1()).unwrap();
		ns.push();
		let (new, prefix) = ns.declare_auto(ns1());
		assert!(!new);
		assert!(prefix.is_none());
		assert!(ns.get_prefix_or_default(ns1()).unwrap().is_none());
		match ns.lookup_prefix(None) {
			Ok(name) => {
				assert_eq!(name, ns1());
			}
			other => panic!("unexpected lookup_prefix result: {:?}", other),
		}
	}

	#[test]
	fn declare_with_fixed_prefix() {
		let mut ns = mk();
		ns.declare_fixed(Some(NcNameStr::from_str("stream").unwrap()), ns1());
		match ns.get_prefix(ns1()) {
			Ok(v) => {
				assert_eq!(v, "stream");
			}
			other => panic!("unexpected get_prefix result: {:?}", other),
		}
		match ns.get_prefix_or_default(ns1()) {
			Ok(Some(v)) => {
				assert_eq!(v, "stream");
			}
			other => panic!("unexpected get_prefix result: {:?}", other),
		}
	}

	#[test]
	fn declare_fixed_prefixless() {
		let mut ns = mk();
		ns.declare_fixed(None, ns1());
		match ns.get_prefix_or_default(ns1()) {
			Ok(None) => (),
			other => panic!("unexpected get_prefix result: {:?}", other),
		}
		match ns.get_prefix(ns1()) {
			Err(PrefixError::Undeclared) => (),
			other => panic!("unexpected get_prefix result: {:?}", other),
		}
	}

	#[test]
	fn preserves_global_prefixed_across_elements() {
		let mut ns = mk();
		ns.declare_fixed(Some(NcNameStr::from_str("stream").unwrap()), ns1());
		ns.declare_fixed(None, ns2());
		ns.push();
		match ns.get_prefix(ns1()) {
			Ok(v) => {
				assert_eq!(v, "stream");
			}
			other => panic!("unexpected get_prefix result: {:?}", other),
		}
		match ns.get_prefix_or_default(ns1()) {
			Ok(Some(v)) => {
				assert_eq!(v, "stream");
			}
			other => panic!("unexpected get_prefix result: {:?}", other),
		}
		match ns.get_prefix_or_default(ns2()) {
			Ok(None) => (),
			other => panic!("unexpected get_prefix result: {:?}", other),
		}
		match ns.get_prefix(ns2()) {
			Err(PrefixError::Undeclared) => (),
			other => panic!("unexpected get_prefix result: {:?}", other),
		}
	}

	#[test]
	#[should_panic(expected = "conflict")]
	fn prohibits_overriding_global_prefix() {
		let mut ns = mk();
		ns.declare_fixed(Some(NcNameStr::from_str("stream").unwrap()), ns1());
		ns.declare_fixed(None, ns2());
		ns.push();
		ns.declare_fixed(Some(NcNameStr::from_str("stream").unwrap()), ns2());
	}

	#[test]
	fn auto_allocations_stay_global_and_do_not_cause_conflicts() {
		let mut ns = mk();
		let prefix1 = ns.declare_with_auto_prefix(ns1()).1.to_ncname();
		let prefix2 = ns.declare_with_auto_prefix(ns2()).1.to_ncname();
		ns.push();
		let (new, prefix3) = ns.declare_with_auto_prefix(ns3());
		assert!(new);
		let prefix3 = prefix3.to_ncname();
		assert_ne!(prefix1, prefix3);
		assert_ne!(prefix2, prefix3);
	}

	#[test]
	#[should_panic(expected = "conflict")]
	fn fixed_global_decl_can_conflict_with_auto_decl() {
		let mut ns = mk();
		ns.declare_fixed(Some(NcNameStr::from_str("tns0").unwrap()), ns1());
		ns.push();
		ns.declare_with_auto_prefix(ns3());
	}

	#[test]
	#[should_panic(expected = "conflict")]
	fn fixed_local_decl_can_conflict_with_auto_decl() {
		let mut ns = mk();
		ns.declare_fixed(Some(NcNameStr::from_str("tns0").unwrap()), ns1());
		ns.declare_with_auto_prefix(ns3());
	}
}

#[cfg(test)]
mod tests_encoder {
	use alloc::string::String;
	use std::io;
	use std::println;

	use super::*;

	use crate::parser::EventMetrics;

	fn mkencoder() -> Encoder<SimpleNamespaces> {
		Encoder::new()
	}

	fn ns1() -> Namespace<'static> {
		Namespace::try_from("uri:foo").unwrap()
	}

	fn ns2() -> Namespace<'static> {
		Namespace::try_from("uri:bar").unwrap()
	}

	fn ns3() -> Namespace<'static> {
		Namespace::try_from("uri:baz").unwrap()
	}

	fn parse(mut input: &[u8]) -> (Vec<Event>, io::Result<bool>) {
		let mut parser: crate::Reader<_> = crate::Reader::new(&mut input);
		let mut events = Vec::new();
		let result = crate::as_eof_flag(parser.read_all(|ev| events.push(ev)));
		(events, result)
	}

	fn encode_events(evs: &[Event]) -> Result<BytesMut> {
		let mut out = BytesMut::new();
		let mut encoder = mkencoder();
		for ev in evs {
			encoder.encode_event(&ev, &mut out)?;
		}
		Ok(out)
	}

	fn encode_events_via_into_bytes(evs: &[Event]) -> Result<BytesMut> {
		let mut out = BytesMut::new();
		let mut encoder = mkencoder();
		for ev in evs {
			encoder.encode_event_into_bytes(&ev, &mut out)?;
		}
		Ok(out)
	}

	fn collapse_cdata(evs: &mut Vec<Event>) {
		let mut buf = Vec::new();
		core::mem::swap(&mut buf, evs);
		let mut cdata_hold = None;
		for event in buf.drain(..) {
			match event {
				Event::Text(_, txt) => match cdata_hold.take() {
					None => cdata_hold = Some(txt),
					Some(existing) => cdata_hold = Some(existing + &*txt),
				},
				_ => {
					match cdata_hold.take() {
						Some(txt) => evs.push(Event::Text(EventMetrics::new(0), txt)),
						None => (),
					};
					evs.push(event);
				}
			}
		}
		match cdata_hold.take() {
			Some(txt) => evs.push(Event::Text(EventMetrics::new(0), txt)),
			None => (),
		};
	}

	fn assert_event_eq(a: &Event, b: &Event) {
		match (a, b) {
			(Event::XmlDeclaration(_, v1), Event::XmlDeclaration(_, v2)) => {
				assert_eq!(v1, v2);
			}
			(Event::StartElement(_, name1, attrs1), Event::StartElement(_, name2, attrs2)) => {
				assert_eq!(name1, name2);
				assert_eq!(attrs1, attrs2);
			}
			(Event::EndElement(_), Event::EndElement(_)) => {}
			(Event::Text(_, text1), Event::Text(_, text2)) => {
				assert_eq!(text1, text2);
			}
			// will always raise
			(a, b) => panic!("event types differ: {:?} != {:?}", a, b),
		}
	}

	fn assert_events_eq(initial: &[Event], reparsed: &[Event]) {
		for (a, b) in initial.iter().zip(reparsed.iter()) {
			assert_event_eq(a, b);
		}
		if initial.len() > reparsed.len() {
			panic!(
				"missing {} events in reparsed version",
				initial.len() - reparsed.len()
			)
		}
		if reparsed.len() > initial.len() {
			panic!(
				"{} additional events in reparsed version: {:?}",
				reparsed.len() - initial.len(),
				&reparsed[initial.len()..]
			)
		}
	}

	fn check_reserialized(
		input: &[u8],
		initial: &[Event],
		initial_eof: bool,
		reserialized: &[u8],
		via: &'static str,
	) {
		let (mut reparsed, reparsed_result) = parse(&reserialized[..]);
		collapse_cdata(&mut reparsed);
		let reparsed_eof = match reparsed_result {
			Ok(eof) => eof,
			Err(e) => {
				panic!(
					"reserialized (via {}) XML\n\n{:?}\n\n  of\n\n{:?}\n\nfails to parse: {}",
					via,
					String::from_utf8_lossy(&reserialized[..]),
					String::from_utf8_lossy(input),
					e
				)
			}
		};
		println!("checking (via {})", via);
		println!(
			"reserialized: {:?}",
			String::from_utf8_lossy(&reserialized[..])
		);
		assert_events_eq(initial, &reparsed);
		assert_eq!(initial_eof, reparsed_eof);
	}

	fn roundtrip_test(input: &[u8]) {
		// goal: test that a parsed thing can be serialized again and then parsed to the semantically equivalent series of events
		let (mut initial, initial_result) = parse(input);
		collapse_cdata(&mut initial);
		let initial_eof = initial_result.expect("test input must parse correctly");
		let reserialized_via_buf =
			encode_events(&initial[..]).expect("parsed input must be encodable");
		let reserialized_via_bytes =
			encode_events_via_into_bytes(&initial[..]).expect("parsed input must be encodable");
		check_reserialized(
			input,
			&initial,
			initial_eof,
			&reserialized_via_buf[..],
			"buf",
		);
		check_reserialized(
			input,
			&initial,
			initial_eof,
			&reserialized_via_bytes[..],
			"bytes",
		);
	}

	#[test]
	fn reject_duplicate_xml_declaration() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(Item::XmlDeclaration(XmlVersion::V1_0), &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::XmlDeclaration(XmlVersion::V1_0), &mut buf) {
			Err(Error::UnexpectedToken(_, "xml declaration", _)) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
	}

	#[test]
	fn reject_text_at_global_level() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(Item::Text("".try_into().unwrap()), &mut buf) {
			Err(Error::UnexpectedToken(_, "text", _)) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
	}

	#[test]
	fn reject_attribute_at_global_level() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(
			Item::Attribute(
				Namespace::NONE,
				"x".try_into().unwrap(),
				"".try_into().unwrap(),
			),
			&mut buf,
		) {
			Err(Error::UnexpectedToken(_, "attribute", _)) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
	}

	#[test]
	fn allow_element_before_decl() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(
			Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
	}

	#[test]
	fn reject_xml_decl_in_element() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(
			Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::XmlDeclaration(XmlVersion::V1_0), &mut buf) {
			Err(Error::UnexpectedToken(_, "xml declaration", _)) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
	}

	#[test]
	fn encode_self_closed_tag() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(
			Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		assert_eq!(&buf, &b"<x/>"[..]);
	}

	#[test]
	fn encode_root_prefix() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		enc.ns.declare_fixed(Some("foo".try_into().unwrap()), ns1());
		match enc.encode(
			Item::ElementHeadStart(ns1(), "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		assert_eq!(&buf, &b"<foo:x xmlns:foo='uri:foo'/>"[..]);
	}

	#[test]
	fn use_explicitly_set_at_root() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		enc.ns.declare_fixed(Some("foo".try_into().unwrap()), ns1());
		match enc.encode(
			Item::ElementHeadStart(ns2(), "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementHeadEnd, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(
			Item::ElementHeadStart(ns1(), "y".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		assert_eq!(
			&buf,
			&b"<x xmlns='uri:bar' xmlns:foo='uri:foo'><foo:y/></x>"[..]
		);
	}

	#[test]
	fn do_not_emit_duplicate_declarations() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		enc.ns.declare_fixed(Some("foo".try_into().unwrap()), ns1());
		match enc.encode(
			Item::ElementHeadStart(ns2(), "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(
			Item::Attribute(ns1(), "a1".try_into().unwrap(), "v1".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(
			Item::Attribute(ns1(), "a2".try_into().unwrap(), "v2".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(
			Item::Attribute(ns2(), "a3".try_into().unwrap(), "v3".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(
			Item::Attribute(ns2(), "a4".try_into().unwrap(), "v4".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(
			Item::Attribute(ns3(), "a5".try_into().unwrap(), "v5".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(
			Item::Attribute(ns3(), "a6".try_into().unwrap(), "v6".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(Item::ElementHeadEnd, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(
			Item::ElementHeadStart(ns1(), "y".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		assert_eq!(&buf, &b"<x xmlns='uri:bar' xmlns:foo='uri:foo' foo:a1='v1' foo:a2='v2' xmlns:tns0='uri:bar' tns0:a3='v3' tns0:a4='v4' xmlns:tns1='uri:baz' tns1:a5='v5' tns1:a6='v6'><foo:y/></x>"[..]);
	}

	#[test]
	fn reuses_existing_declaration_for_nested_namespaced_attributes_if_available() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		enc.ns.declare_fixed(Some("foo".try_into().unwrap()), ns1());
		match enc.encode(
			Item::ElementHeadStart(ns2(), "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(
			Item::Attribute(ns1(), "a1".try_into().unwrap(), "v1".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(
			Item::Attribute(ns1(), "a2".try_into().unwrap(), "v2".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(
			Item::Attribute(ns2(), "a3".try_into().unwrap(), "v3".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(
			Item::Attribute(ns2(), "a4".try_into().unwrap(), "v4".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(
			Item::Attribute(ns3(), "a5".try_into().unwrap(), "v5".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(
			Item::Attribute(ns3(), "a6".try_into().unwrap(), "v6".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(Item::ElementHeadEnd, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(
			Item::ElementHeadStart(ns1(), "y".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(
			Item::Attribute(ns1(), "a1".try_into().unwrap(), "v1".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		}
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		assert_eq!(&buf, &b"<x xmlns='uri:bar' xmlns:foo='uri:foo' foo:a1='v1' foo:a2='v2' xmlns:tns0='uri:bar' tns0:a3='v3' tns0:a4='v4' xmlns:tns1='uri:baz' tns1:a5='v5' tns1:a6='v6'><foo:y foo:a1='v1'/></x>"[..]);
	}

	#[test]
	fn self_closed_tag_encoding_ends_document_if_it_closes_the_root() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(
			Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::Text("".try_into().unwrap()), &mut buf) {
			Err(Error::UnexpectedToken(Some(ErrorContext::DocumentEnd), "text", _)) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
	}

	#[test]
	fn self_closed_tag_encoding_allows_content_thereafter() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(
			Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementHeadEnd, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(
			Item::ElementHeadStart(Namespace::NONE, "y".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::Text("foo".try_into().unwrap()), &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		assert_eq!(&buf[..], b"<x><y/>foo");
	}

	#[test]
	fn reject_element_after_end_of_document() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(
			Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementHeadEnd, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(
			Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
			&mut buf,
		) {
			Err(Error::UnexpectedToken(Some(ErrorContext::DocumentEnd), "element start", _)) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
	}

	#[test]
	fn reject_element_foot_before_start() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(Item::ElementFoot, &mut buf) {
			Err(Error::UnexpectedToken(_, "element foot", _)) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
	}

	#[test]
	fn reject_element_head_end_outside_element_header() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(Item::ElementHeadEnd, &mut buf) {
			Err(Error::UnexpectedToken(_, "element head end", _)) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(
			Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementHeadEnd, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementHeadEnd, &mut buf) {
			Err(Error::UnexpectedToken(_, "element head end", _)) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
	}

	#[test]
	fn reject_text_after_end_of_document() {
		let mut enc = mkencoder();
		let mut buf = BytesMut::new();
		match enc.encode(
			Item::ElementHeadStart(Namespace::NONE, "x".try_into().unwrap()),
			&mut buf,
		) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementHeadEnd, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::ElementFoot, &mut buf) {
			Ok(()) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
		match enc.encode(Item::Text("".try_into().unwrap()), &mut buf) {
			Err(Error::UnexpectedToken(Some(ErrorContext::DocumentEnd), "text", _)) => (),
			other => panic!("unexpected encode result: {:?}", other),
		};
	}

	#[test]
	fn single_element_roundtrip() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a/>")
	}

	#[test]
	fn nested_element_roundtrip() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a><b/></a>")
	}

	#[test]
	fn mixed_content_roundtrip() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a>foo<b>bar</b>baz</a>")
	}

	#[test]
	fn text_with_lt_roundtrip() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a>&lt;</a>")
	}

	#[test]
	fn text_with_gt_roundtrip() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a>&gt;</a>")
	}

	#[test]
	fn text_with_escaped_entity_roundtrip() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a>&amp;amp;</a>")
	}

	#[test]
	fn text_cdata_sequence_roundtrip() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a>]]&gt;</a>")
	}

	#[test]
	fn attribute_roundtrip() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a a1='foo' a2=\"bar\"/>")
	}

	#[test]
	fn attribute_whitespace_roundtrip() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a a1='&#xd;&#xa;&#x9; '/>")
	}

	#[test]
	fn attribute_quotes_roundtrip() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a a1='&quot;&apos;'/>")
	}

	#[test]
	fn namespace_roundtrip() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a xmlns='uri:foo'/>")
	}

	#[test]
	fn namespace_roundtrip_builtins() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a xml:lang='de'/>")
	}

	#[test]
	fn namespace_roundtrip_with_prefixes() {
		roundtrip_test(
			b"<?xml version='1.0'?>\n<a xmlns='uri:foo' xmlns:b='uri:bar'><b:b><c/></b:b></a>",
		)
	}

	#[test]
	fn roundtrip_escaped_crlf() {
		roundtrip_test(b"<?xml version='1.0'?>\n<a>\r\n&#xd;&#xa;</a>")
	}

	#[test]
	fn prefixed_attribute_roundtrip() {
		roundtrip_test(
			b"<?xml version='1.0'?>\n<a xmlns:b='uri:foo' b:a1='baz' a2='fnord' b:a3='foobar'/>",
		)
	}

	#[test]
	fn escape_text_rejects_non_xml_chars() {
		let mut buf = String::with_capacity(4);
		let mut sink = BytesMut::with_capacity(8);
		for cp in 0x0..=0x10ffffu32 {
			if let Some(ch) = char::from_u32(cp) {
				buf.clear();
				buf.push(ch);
				let validation = rxml_validation::validate_cdata(&buf);
				assert_eq!(
					{
						sink.clear();
						escape_text(&mut sink, &buf, false)
					},
					validation,
					"in non-attribute context",
				);
				assert_eq!(
					{
						sink.clear();
						escape_text(&mut sink, &buf, true)
					},
					validation,
					"in attribute context",
				);
			}
		}
	}
}
