use source_map::SourceId;
use crate::{
	ArrayDestructuringField, Expression, JSXElement, ObjectDestructuringField, PropertyKey,
	StatementOrDeclaration, WithComment,
};
pub use temporary_annex::Annex;
pub use ast::*;
pub use structures::*;
pub use visitors::*;
pub use visitors_mut::*;
mod ast {
	use temporary_annex::Annex;
	use crate::block::{BlockLike, BlockLikeMut};
	use super::*;
	pub struct VisitSettings {
		pub reverse_statements: bool,
		pub visit_function_bodies: bool,
	}
	impl Default for VisitSettings {
		fn default() -> Self {
			Self { reverse_statements: false, visit_function_bodies: true }
		}
	}
	macro_rules! mark_items {
        (impl $trait:ident for $($t:ty),*) => {
            $(
                impl $trait for $t {}
            )*
        }
    }
	pub trait SelfVisitable {}
	pub trait SelfVisitableMut {}
	mark_items! {
		impl SelfVisitable for Expression, StatementOrDeclarationRef<'_>, BlockLike<'_>, JSXElement, ImmutableVariableOrPropertyPart<'_>
	}
	mark_items! {
		impl SelfVisitableMut for Expression, StatementOrDeclarationMut<'_>, BlockLikeMut<'_>, JSXElement, MutableVariablePart<'_>
	}
	pub trait Visitable {
		fn visit<TData>(
			&self,
			visitors: &mut (impl VisitorReceiver<TData> + ?Sized),
			data: &mut TData,
			settings: &VisitSettings,
			chain: &mut Annex<Chain>,
		);
		fn visit_mut<TData>(
			&mut self,
			visitors: &mut (impl VisitorMutReceiver<TData> + ?Sized),
			data: &mut TData,
			settings: &VisitSettings,
			chain: &mut Annex<Chain>,
		);
	}
	impl<T: Visitable> Visitable for Box<T> {
		fn visit<TData>(
			&self,
			v: &mut (impl VisitorReceiver<TData> + ?Sized),
			d: &mut TData,
			s: &VisitSettings,
			c: &mut Annex<Chain>,
		) {
			Visitable::visit(&**self, v, d, s, c)
		}
		fn visit_mut<TData>(
			&mut self,
			v: &mut (impl VisitorMutReceiver<TData> + ?Sized),
			d: &mut TData,
			s: &VisitSettings,
			c: &mut Annex<Chain>,
		) {
			Visitable::visit_mut(&mut **self, v, d, s, c)
		}
	}
	impl<T: Visitable> Visitable for Vec<T> {
		fn visit<TData>(
			&self,
			v: &mut (impl VisitorReceiver<TData> + ?Sized),
			d: &mut TData,
			s: &VisitSettings,
			c: &mut Annex<Chain>,
		) {
			self.iter().for_each(|item| item.visit(v, d, s, c));
		}
		fn visit_mut<TData>(
			&mut self,
			v: &mut (impl VisitorMutReceiver<TData> + ?Sized),
			d: &mut TData,
			s: &VisitSettings,
			c: &mut Annex<Chain>,
		) {
			self.iter_mut().for_each(|item| item.visit_mut(v, d, s, c));
		}
	}
	impl<T: Visitable> Visitable for Option<T> {
		fn visit<TData>(
			&self,
			v: &mut (impl VisitorReceiver<TData> + ?Sized),
			d: &mut TData,
			s: &VisitSettings,
			c: &mut Annex<Chain>,
		) {
			if let Some(item) = self {
				item.visit(v, d, s, c);
			}
		}
		fn visit_mut<TData>(
			&mut self,
			v: &mut (impl VisitorMutReceiver<TData> + ?Sized),
			d: &mut TData,
			s: &VisitSettings,
			c: &mut Annex<Chain>,
		) {
			if let Some(item) = self {
				item.visit_mut(v, d, s, c);
			}
		}
	}
	impl<T: Visitable, U: Visitable> Visitable for (T, U) {
		fn visit<TData>(
			&self,
			v: &mut (impl VisitorReceiver<TData> + ?Sized),
			d: &mut TData,
			s: &VisitSettings,
			c: &mut Annex<Chain>,
		) {
			self.0.visit(v, d, s, c);
			self.1.visit(v, d, s, c);
		}
		fn visit_mut<TData>(
			&mut self,
			v: &mut (impl VisitorMutReceiver<TData> + ?Sized),
			d: &mut TData,
			s: &VisitSettings,
			c: &mut Annex<Chain>,
		) {
			self.0.visit_mut(v, d, s, c);
			self.1.visit_mut(v, d, s, c);
		}
	}
	macro_rules! create_blank_visiting_implementations {
        ($($T:ty),*) => {
            $(
                impl Visitable for $T {
                    fn visit<TData>(
                        &self,
                        _visitors: &mut (impl VisitorReceiver<TData> + ?Sized),
                        _data: &mut TData,
                        _settings: &VisitSettings,
                        _chain: &mut Annex<Chain>,
                    ) {}
                    fn visit_mut<TData>(
                        &mut self,
                        _visitors: &mut (impl VisitorMutReceiver<TData> + ?Sized),
                        _data: &mut TData,
                        _settings: &VisitSettings,
                        _chain: &mut Annex<Chain>,
                    ) {}
                }
            )*
        }
    }
	create_blank_visiting_implementations![
		(),
		bool,
		isize,
		usize,
		i8,
		u8,
		i16,
		u16,
		i32,
		u32,
		i64,
		u64,
		i128,
		u128,
		f32,
		f64,
		char,
		String,
		Box<str>,
		std::rc::Rc<str>,
		std::path::Path,
		std::path::PathBuf,
		source_map::Span,
		crate::TypeAnnotation,
		crate::NumberRepresentation,
		crate::operators::BinaryOperator,
		crate::operators::BinaryAssignmentOperator,
		crate::operators::UnaryOperator,
		crate::operators::UnaryPrefixAssignmentOperator,
		crate::operators::UnaryPostfixAssignmentOperator,
		crate::types::InterfaceDeclaration,
		crate::types::type_alias::TypeAlias,
		crate::types::declares::DeclareFunctionDeclaration,
		crate::types::declares::DeclareVariableDeclaration,
		crate::VariableIdentifier,
		crate::PropertyReference,
		crate::Quoted,
		crate::PropertyKey<crate::property_key::AlwaysPublic>,
		crate::PropertyKey<crate::property_key::PublicOrPrivate>
	];
}
mod structures {
	use crate::{
		property_key::{AlwaysPublic, PublicOrPrivate},
		Declaration, Statement, VariableFieldInSourceCode,
	};
	use super::*;
	use source_map::Span;
	use temporary_annex::{Annex, Annexable};
	#[derive(Debug, Clone)]
	pub enum ChainVariable {
		Module(SourceId),
		Function(Span),
		Block(Span),
	}
	#[derive(Debug, Clone)]
	pub struct Chain(Vec<ChainVariable>);
	impl Chain {
		pub fn new() -> Self {
			Self(Vec::with_capacity(10))
		}
		pub fn new_with_initial(initial: ChainVariable) -> Self {
			let mut buf = Vec::with_capacity(10);
			buf.push(initial);
			Self(buf)
		}
		pub fn len(&self) -> usize {
			self.0.len()
		}
		pub fn truncate(&mut self, to_size: usize) {
			self.0.truncate(to_size)
		}
		pub fn get_chain(&self) -> &[ChainVariable] {
			&self.0
		}
		pub fn get_module(&self) -> SourceId {
			if let ChainVariable::Module(source) = self.0.first().unwrap() {
				source.to_owned()
			} else {
				SourceId::NULL
			}
		}
		}
	impl Annexable for Chain {
		type NewItem = ChainVariable;
		fn push_annex(&mut self, item: Self::NewItem) -> Annex<Self>
		where
			Self: Sized,
		{
			self.0.push(item);
			Annex::new(self)
		}
		fn revert_annex(&mut self) {
			self.0.pop();
		}
	}
	#[derive(Debug)]
	pub enum MutableVariablePart<'a> {
		VariableFieldName(&'a mut String),
		ArrayDestructuringMember(&'a mut ArrayDestructuringField<VariableFieldInSourceCode>),
		ObjectDestructuringMember(
			&'a mut WithComment<ObjectDestructuringField<VariableFieldInSourceCode>>,
		),
		ClassName(Option<&'a mut String>),
		FunctionName(&'a mut String),
		ClassPropertyKey(&'a mut WithComment<PropertyKey<PublicOrPrivate>>),
		ObjectPropertyKey(&'a mut WithComment<PropertyKey<AlwaysPublic>>),
	}
	#[derive(Debug)]
	pub enum ImmutableVariableOrPropertyPart<'a> {
		VariableFieldName(&'a str, &'a Span),
		ArrayDestructuringMember(&'a ArrayDestructuringField<VariableFieldInSourceCode>),
		ObjectDestructuringMember(
			&'a WithComment<ObjectDestructuringField<VariableFieldInSourceCode>>,
		),
		ClassName(Option<&'a str>, &'a Span),
		FunctionName(&'a str, &'a Span),
		ClassPropertyKey(&'a WithComment<PropertyKey<PublicOrPrivate>>),
		ObjectPropertyKey(&'a WithComment<PropertyKey<AlwaysPublic>>),
	}
	impl<'a> ImmutableVariableOrPropertyPart<'a> {
		pub fn get_name(&self) -> Option<&'a str> {
			match self {
				ImmutableVariableOrPropertyPart::FunctionName(name, _)
				| ImmutableVariableOrPropertyPart::VariableFieldName(name, _) => Some(name),
				ImmutableVariableOrPropertyPart::ArrayDestructuringMember(_)
				| ImmutableVariableOrPropertyPart::ObjectDestructuringMember(_) => None,
				ImmutableVariableOrPropertyPart::ClassName(name, _) => *name,
				ImmutableVariableOrPropertyPart::ObjectPropertyKey(property) => {
					match property.get_ast_ref() {
						PropertyKey::Ident(ident, _, _) | PropertyKey::StringLiteral(ident, _) => {
							Some(ident.as_str())
						}
						PropertyKey::NumberLiteral(_, _) | PropertyKey::Computed(_, _) => None,
					}
				}
				ImmutableVariableOrPropertyPart::ClassPropertyKey(property) => {
					match property.get_ast_ref() {
						PropertyKey::Ident(ident, _, _) | PropertyKey::StringLiteral(ident, _) => {
							Some(ident.as_str())
						}
						PropertyKey::NumberLiteral(_, _) | PropertyKey::Computed(_, _) => None,
					}
				}
			}
		}
		pub fn get_position(&self) -> &Span {
			use crate::ASTNode;
			match self {
				ImmutableVariableOrPropertyPart::FunctionName(_, pos)
				| ImmutableVariableOrPropertyPart::ClassName(_, pos)
				| ImmutableVariableOrPropertyPart::VariableFieldName(_, pos) => pos,
				ImmutableVariableOrPropertyPart::ArrayDestructuringMember(member) => {
					member.get_position()
				}
				ImmutableVariableOrPropertyPart::ObjectDestructuringMember(member) => {
					member.get_position()
				}
				ImmutableVariableOrPropertyPart::ClassPropertyKey(property_key) => {
					property_key.get_position()
				}
				ImmutableVariableOrPropertyPart::ObjectPropertyKey(property_key) => {
					property_key.get_position()
				}
			}
		}
	}
	pub enum StatementOrDeclarationRef<'a> {
		Statement(&'a crate::Statement),
		Declaration(&'a crate::Declaration),
	}
	impl<'a> From<&'a StatementOrDeclaration> for StatementOrDeclarationRef<'a> {
		fn from(value: &'a StatementOrDeclaration) -> Self {
			match value {
				StatementOrDeclaration::Statement(item) => {
					StatementOrDeclarationRef::Statement(item)
				}
				StatementOrDeclaration::Declaration(item) => {
					StatementOrDeclarationRef::Declaration(item)
				}
			}
		}
	}
	impl<'a> From<&'a Statement> for StatementOrDeclarationRef<'a> {
		fn from(item: &'a Statement) -> Self {
			StatementOrDeclarationRef::Statement(item)
		}
	}
	impl<'a> From<&'a Declaration> for StatementOrDeclarationRef<'a> {
		fn from(item: &'a Declaration) -> Self {
			StatementOrDeclarationRef::Declaration(item)
		}
	}
	pub enum StatementOrDeclarationMut<'a> {
		Statement(&'a mut crate::Statement),
		Declaration(&'a mut crate::Declaration),
	}
	impl<'a> From<&'a mut StatementOrDeclaration> for StatementOrDeclarationMut<'a> {
		fn from(value: &'a mut StatementOrDeclaration) -> Self {
			match value {
				StatementOrDeclaration::Statement(item) => {
					StatementOrDeclarationMut::Statement(item)
				}
				StatementOrDeclaration::Declaration(item) => {
					StatementOrDeclarationMut::Declaration(item)
				}
			}
		}
	}
	impl<'a> From<&'a mut Statement> for StatementOrDeclarationMut<'a> {
		fn from(item: &'a mut Statement) -> Self {
			StatementOrDeclarationMut::Statement(item)
		}
	}
	impl<'a> From<&'a mut Declaration> for StatementOrDeclarationMut<'a> {
		fn from(item: &'a mut Declaration) -> Self {
			StatementOrDeclarationMut::Declaration(item)
		}
	}
}
mod visitors {
	use super::*;
	use crate::{block::BlockLike, TSXKeyword};
	use source_map::Span;
	pub trait Visitor<Item: SelfVisitable, Data> {
		fn visit(&mut self, item: &Item, data: &mut Data, chain: &Chain);
	}
	#[allow(unused_variables)]
	pub trait VisitorReceiver<T> {
		fn visit_expression(&mut self, expression: &Expression, data: &mut T, chain: &Chain) {}
		fn visit_statement(
			&mut self,
			statement: StatementOrDeclarationRef,
			data: &mut T,
			chain: &Chain,
		) {
		}
		fn visit_jsx_element(&mut self, element: &JSXElement, data: &mut T, chain: &Chain) {}
		fn visit_variable(
			&mut self,
			variable: &ImmutableVariableOrPropertyPart,
			data: &mut T,
			chain: &Chain,
		) {
		}
		fn visit_block(&mut self, block: &BlockLike, data: &mut T, chain: &Chain) {}
		fn visit_keyword(&mut self, keyword: &(TSXKeyword, &Span), data: &mut T, chain: &Chain) {}
	}
	impl<T> VisitorReceiver<T> for Visitors<T> {
		fn visit_expression(&mut self, expression: &Expression, data: &mut T, chain: &Chain) {
			self.expression_visitors.iter_mut().for_each(|vis| vis.visit(expression, data, chain));
		}
		fn visit_statement(
			&mut self,
			statement: StatementOrDeclarationRef,
			data: &mut T,
			chain: &Chain,
		) {
			self.statement_visitors.iter_mut().for_each(|vis| vis.visit(&statement, data, chain));
		}
		fn visit_jsx_element(&mut self, jsx_element: &JSXElement, data: &mut T, chain: &Chain) {
			self.jsx_element_visitors
				.iter_mut()
				.for_each(|vis| vis.visit(jsx_element, data, chain));
		}
		fn visit_variable(
			&mut self,
			variable: &ImmutableVariableOrPropertyPart,
			data: &mut T,
			chain: &Chain,
		) {
			self.variable_visitors.iter_mut().for_each(|vis| vis.visit(variable, data, chain))
		}
		fn visit_block(&mut self, block: &BlockLike, data: &mut T, chain: &Chain) {
			self.block_visitors.iter_mut().for_each(|vis| vis.visit(block, data, chain))
		}
		fn visit_keyword(&mut self, _keyword: &(TSXKeyword, &Span), _data: &mut T, _chain: &Chain) {
		}
	}
	#[derive(Default)]
	pub struct Visitors<T> {
		pub expression_visitors: Vec<Box<dyn Visitor<Expression, T>>>,
		pub statement_visitors: Vec<Box<dyn for<'a> Visitor<StatementOrDeclarationRef<'a>, T>>>,
		pub jsx_element_visitors: Vec<Box<dyn Visitor<JSXElement, T>>>,
		pub variable_visitors:
			Vec<Box<dyn for<'a> Visitor<ImmutableVariableOrPropertyPart<'a>, T>>>,
		pub block_visitors: Vec<Box<dyn for<'a> Visitor<BlockLike<'a>, T>>>,
	}
	}
mod visitors_mut {
	use crate::block::BlockLikeMut;
	use super::*;
	pub trait VisitorMut<Item: SelfVisitableMut, Data> {
		fn visit_mut(&mut self, item: &mut Item, data: &mut Data, chain: &Chain);
	}
	#[allow(unused_variables)]
	pub trait VisitorMutReceiver<T> {
		fn visit_expression_mut(
			&mut self,
			expression: &mut Expression,
			data: &mut T,
			chain: &Chain,
		) {
		}
		fn visit_statement_mut(
			&mut self,
			statement: StatementOrDeclarationMut,
			data: &mut T,
			chain: &Chain,
		) {
		}
		fn visit_jsx_element_mut(&mut self, element: &mut JSXElement, data: &mut T, chain: &Chain) {
		}
		fn visit_variable_mut(
			&mut self,
			variable: &mut MutableVariablePart,
			data: &mut T,
			chain: &Chain,
		) {
		}
		fn visit_block_mut(&mut self, block: &mut BlockLikeMut, data: &mut T, chain: &Chain) {}
	}
	pub struct VisitorsMut<T> {
		pub expression_visitors_mut: Vec<Box<dyn VisitorMut<Expression, T>>>,
		pub statement_visitors_mut:
			Vec<Box<dyn for<'a> VisitorMut<StatementOrDeclarationMut<'a>, T>>>,
		pub jsx_element_visitors_mut: Vec<Box<dyn VisitorMut<JSXElement, T>>>,
		pub variable_visitors_mut: Vec<Box<dyn for<'a> VisitorMut<MutableVariablePart<'a>, T>>>,
		pub block_visitors_mut: Vec<Box<dyn for<'a> VisitorMut<BlockLikeMut<'a>, T>>>,
	}
	impl<T> Default for VisitorsMut<T> {
		fn default() -> Self {
			Self {
				expression_visitors_mut: Default::default(),
				statement_visitors_mut: Default::default(),
				jsx_element_visitors_mut: Default::default(),
				variable_visitors_mut: Default::default(),
				block_visitors_mut: Default::default(),
			}
		}
	}
	impl<T> VisitorMutReceiver<T> for VisitorsMut<T> {
		fn visit_expression_mut(
			&mut self,
			expression: &mut Expression,
			data: &mut T,
			chain: &Chain,
		) {
			self.expression_visitors_mut
				.iter_mut()
				.for_each(|vis| vis.visit_mut(expression, data, chain));
		}
		fn visit_statement_mut(
			&mut self,
			mut statement: StatementOrDeclarationMut,
			data: &mut T,
			chain: &Chain,
		) {
			self.statement_visitors_mut
				.iter_mut()
				.for_each(|vis| vis.visit_mut(&mut statement, data, chain));
		}
		fn visit_jsx_element_mut(&mut self, element: &mut JSXElement, data: &mut T, chain: &Chain) {
			self.jsx_element_visitors_mut
				.iter_mut()
				.for_each(|vis| vis.visit_mut(element, data, chain));
		}
		fn visit_variable_mut(
			&mut self,
			variable: &mut MutableVariablePart,
			data: &mut T,
			chain: &Chain,
		) {
			self.variable_visitors_mut
				.iter_mut()
				.for_each(|vis| vis.visit_mut(variable, data, chain));
		}
		fn visit_block_mut(&mut self, block: &mut BlockLikeMut, data: &mut T, chain: &Chain) {
			self.block_visitors_mut.iter_mut().for_each(|vis| vis.visit_mut(block, data, chain));
		}
	}
	}