//! A [markdown_it] plugin for parsing footnotes
//!
//! ```
//! let parser = &mut markdown_it::MarkdownIt::new();
//! md_footnote::add(parser);
//! let node = parser.parse("[^note]\n\n[^note]: A footnote\n");
//! ```
use std::collections::HashMap;

use markdown_it::{MarkdownIt, parser::extset::RootExt};

pub mod back_refs;
pub mod collect;
pub mod definitions;
pub mod inline;
pub mod references;

// Silence lints
#[cfg(test)]
use md_dev as _;

#[cfg(test)]
use testing as _;

/// Add the full footnote plugin to the parser
pub fn add(md: &mut MarkdownIt) {
	definitions::add(md);
	references::add(md);
	inline::add(md);
	collect::add(md);
	back_refs::add(md);
}

#[derive(Debug, Default)]
/// The set of parsed footnote definition labels,
/// stored in the root node.
pub struct FootnoteMap {
	def_counter: usize,
	ref_counter: usize,
	label_to_def: HashMap<String, usize>,
	def_to_refs: HashMap<usize, Vec<usize>>,
}
impl RootExt for FootnoteMap {}
impl FootnoteMap {
	/// Create an ID for the definition,
	/// or return None if a definition already exists for the label
	pub fn add_def(&mut self, label: &str) -> Option<usize> {
		if self.label_to_def.contains_key(label) {
			return None;
		}
		self.def_counter += 1;
		self.label_to_def
			.insert(String::from(label), self.def_counter);
		Some(self.def_counter)
	}
	/// Create an ID for the reference and return (def_id, ref_id),
	/// or return None if no definition exists for the label
	pub fn add_ref(&mut self, label: &str) -> Option<(usize, usize)> {
		match self.label_to_def.get(label) {
			Some(def_id) => {
				self.ref_counter += 1;
				// self.def_to_refs.get_mut(&def_id).unwrap().push(self.ref_counter);
				match self.def_to_refs.get_mut(def_id) {
					Some(refs) => refs.push(self.ref_counter),
					None => {
						self.def_to_refs.insert(*def_id, vec![self.ref_counter]);
					}
				}
				Some((*def_id, self.ref_counter))
			}
			None => None,
		}
	}
	/// Add an inline definition and return (def_id, ref_id)
	pub fn add_inline_def(&mut self) -> (usize, usize) {
		self.def_counter += 1;
		self.ref_counter += 1;
		self.def_to_refs
			.insert(self.def_counter, vec![self.ref_counter]);
		(self.def_counter, self.ref_counter)
	}
	/// return the IDs of all references to the given definition ID
	pub fn referenced_by(&self, def_id: usize) -> Vec<usize> {
		match self.def_to_refs.get(&def_id) {
			Some(ids) => ids.clone(),
			None => Vec::new(),
		}
	}
}
