use gramatika::{SpannedError, Token as _};
use crate::{
	common::{AttributeList, TypeDecl},
	decl::{
		legacy::{ExtensionDecl, ModuleDecl},
		Decl, FieldDecl, FunctionDecl, ImportDecl, ImportPath, ImportPathBlock, ImportPathDecl,
		NamespacedImportPath, ParamDecl, StructDecl, TypeAliasDecl, VarDecl,
	},
	expr::{
		BinaryExpr, BitcastExpr, Expr, FnCallExpr, GroupExpr, IdentExpr, PostfixExpr, PrimaryExpr,
		TypeCtorExpr, UnaryPostExpr, UnaryPreExpr,
	},
	stmt::{
		BlockStmt, CaseStmt, ContinuingStmt, ElseStmt, ExprStmt, ForStmt, IfStmt, LoopStmt,
		ReturnStmt, Stmt, SwitchStmt,
	},
	SyntaxTree, Token,
};
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
pub enum FlowControl {
	Continue,
	Break,
}
#[allow(unused_variables)]
#[rustfmt::skip]
pub trait Visitor {
	#[must_use] fn visit_decl(&mut self, decl: &Decl) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_stmt(&mut self, stmt: &Stmt) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_expr(&mut self, expr: &Expr) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_type(&mut self, decl: &TypeDecl) -> FlowControl { FlowControl::Continue }
	fn visit_attributes(&mut self, attr: &AttributeList) {}
	#[must_use] fn visit_var_decl(&mut self, decl: &VarDecl) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_type_alias_decl(&mut self, decl: &TypeAliasDecl) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_struct_decl(&mut self, decl: &StructDecl) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_field_decl(&mut self, decl: &FieldDecl) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_func_decl(&mut self, decl: &FunctionDecl) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_param_decl(&mut self, decl: &ParamDecl) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_import_path_decl(&mut self, decl: &ImportPathDecl) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_import_decl(&mut self, decl: &ImportDecl) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_import_path(&mut self, path: &ImportPath) -> FlowControl { FlowControl::Continue }
	fn visit_extension_decl(&mut self, decl: &ExtensionDecl) {}
	fn visit_module_decl(&mut self, decl: &ModuleDecl) {}
	#[must_use] fn visit_return_stmt(&mut self, stmt: &ReturnStmt) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_if_stmt(&mut self, stmt: &IfStmt) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_else_stmt(&mut self, stmt: &ElseStmt) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_switch_stmt(&mut self, stmt: &SwitchStmt) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_case_stmt(&mut self, stmt: &CaseStmt) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_loop_stmt(&mut self, stmt: &LoopStmt) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_continuing_stmt(&mut self, stmt: &ContinuingStmt) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_for_stmt(&mut self, stmt: &ForStmt) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_expr_stmt(&mut self, stmt: &ExprStmt) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_unary_pre_expr(&mut self, expr: &UnaryPreExpr) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_unary_post_expr(&mut self, expr: &UnaryPostExpr) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_binary_expr(&mut self, expr: &BinaryExpr) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_assignment_expr(&mut self, expr: &BinaryExpr) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_fn_call_expr(&mut self, expr: &FnCallExpr) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_type_ctor_expr(&mut self, expr: &TypeCtorExpr) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_group_expr(&mut self, expr: &GroupExpr) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_bitcast_expr(&mut self, expr: &BitcastExpr) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_primary_expr(&mut self, expr: &PrimaryExpr) -> FlowControl { FlowControl::Continue }
	#[must_use] fn visit_postfix_expr(&mut self, expr: &PostfixExpr) -> FlowControl { FlowControl::Continue }
	fn visit_literal_expr(&mut self, expr: &Token) {}
	fn visit_ident_expr(&mut self, expr: &IdentExpr) {}
	fn visit_error(&mut self, expr: &SpannedError) {}
}
pub trait Walk {
	fn walk(&self, visitor: &mut dyn Visitor);
}
impl Walk for SyntaxTree {
	fn walk(&self, visitor: &mut dyn Visitor) {
		for decl in self.inner.iter() {
			decl.walk(visitor);
		}
	}
}
impl Walk for Decl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		use Decl::*;
		if visitor.visit_decl(self) == FlowControl::Continue {
			match self {
				Var(decl) | Const(decl) => decl.walk(visitor),
				TypeAlias(decl) => decl.walk(visitor),
				Struct(decl) => decl.walk(visitor),
				Field(decl) => decl.walk(visitor),
				Function(decl) => decl.walk(visitor),
				Param(decl) => decl.walk(visitor),
				Extension(decl) => decl.walk(visitor),
				ImportPath(decl) => decl.walk(visitor),
				Import(decl) => decl.walk(visitor),
				Module(decl) => decl.walk(visitor),
				Error(err) => visitor.visit_error(err),
			}
		}
	}
}
impl Walk for VarDecl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_var_decl(self) == FlowControl::Continue {
			if let Some(ref attributes) = self.attributes {
				visitor.visit_attributes(attributes);
			}
			if let Some(ref ty) = self.ty {
				ty.walk(visitor);
			}
			if let Some(ref expr) = self.assignment {
				expr.walk(visitor);
			}
		}
	}
}
impl Walk for TypeAliasDecl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_type_alias_decl(self) == FlowControl::Continue {
			self.value.walk(visitor);
		}
	}
}
impl Walk for StructDecl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_struct_decl(self) == FlowControl::Continue {
			for field in self.body.fields.iter() {
				field.walk(visitor);
			}
		}
	}
}
impl Walk for FieldDecl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_field_decl(self) == FlowControl::Continue {
			self.ty.walk(visitor);
		}
	}
}
impl Walk for FunctionDecl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_func_decl(self) == FlowControl::Continue {
			for param in self.params.iter() {
				param.walk(visitor);
			}
			if let Some(ref ty) = self.return_ty {
				ty.walk(visitor);
			}
			self.body.walk(visitor);
		}
	}
}
impl Walk for ParamDecl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_param_decl(self) == FlowControl::Continue {
			self.ty.walk(visitor);
		}
	}
}
impl Walk for ExtensionDecl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		visitor.visit_extension_decl(self);
	}
}
impl Walk for ImportPathDecl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_import_path_decl(self) == FlowControl::Continue {
			self.path.walk(visitor);
		}
	}
}
impl Walk for ImportDecl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_import_decl(self) == FlowControl::Continue {
			self.path.walk(visitor);
		}
	}
}
impl Walk for ImportPath {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_import_path(self) == FlowControl::Continue {
			match self {
				ImportPath::Namespaced(NamespacedImportPath { path, .. }) => {
					path.walk(visitor);
				}
				ImportPath::Block(ImportPathBlock { paths, .. }) => {
					for path in paths.iter() {
						path.walk(visitor);
					}
				}
				ImportPath::Leaf(_) => {}
			}
		}
	}
}
impl Walk for ModuleDecl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		visitor.visit_module_decl(self);
	}
}
impl Walk for Stmt {
	fn walk(&self, visitor: &mut dyn Visitor) {
		use Stmt::*;
		if visitor.visit_stmt(self) == FlowControl::Continue {
			match self {
				Block(stmt) => stmt.walk(visitor),
				Return(stmt) => stmt.walk(visitor),
				If(stmt) => stmt.walk(visitor),
				Switch(stmt) => stmt.walk(visitor),
				Loop(stmt) => stmt.walk(visitor),
				Continuing(stmt) => stmt.walk(visitor),
				For(stmt) => stmt.walk(visitor),
				Var(decl) => decl.walk(visitor),
				Expr(stmt) => stmt.walk(visitor),
				Error(err) => visitor.visit_error(err),
				_ => {}
			}
		}
	}
}
impl Walk for BlockStmt {
	fn walk(&self, visitor: &mut dyn Visitor) {
		for stmt in self.stmts.iter() {
			stmt.walk(visitor);
		}
	}
}
impl Walk for ReturnStmt {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_return_stmt(self) == FlowControl::Continue {
			if let Some(ref expr) = self.value {
				expr.walk(visitor);
			}
		}
	}
}
impl Walk for IfStmt {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_if_stmt(self) == FlowControl::Continue {
			self.condition.walk(visitor);
			self.then_branch.walk(visitor);
			if let Some(ref stmt) = self.else_branch {
				stmt.walk(visitor);
			}
		}
	}
}
impl Walk for ElseStmt {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_else_stmt(self) == FlowControl::Continue {
			self.body.walk(visitor);
		}
	}
}
impl Walk for SwitchStmt {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_switch_stmt(self) == FlowControl::Continue {
			self.subject.walk(visitor);
			for stmt in self.body.cases.iter() {
				stmt.walk(visitor);
			}
		}
	}
}
impl Walk for CaseStmt {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_case_stmt(self) == FlowControl::Continue {
			self.body.walk(visitor);
		}
	}
}
impl Walk for LoopStmt {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_loop_stmt(self) == FlowControl::Continue {
			self.body.walk(visitor);
		}
	}
}
impl Walk for ContinuingStmt {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_continuing_stmt(self) == FlowControl::Continue {
			self.body.walk(visitor);
		}
	}
}
impl Walk for ForStmt {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_for_stmt(self) == FlowControl::Continue {
			if let Some(ref stmt) = self.initializer {
				stmt.walk(visitor);
			}
			if let Some(ref expr) = self.condition {
				expr.walk(visitor);
			}
			if let Some(ref expr) = self.increment {
				expr.walk(visitor);
			}
			self.body.walk(visitor);
		}
	}
}
impl Walk for ExprStmt {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_expr_stmt(self) == FlowControl::Continue {
			self.expr.walk(visitor);
		}
	}
}
impl Walk for Expr {
	fn walk(&self, visitor: &mut dyn Visitor) {
		use Expr::*;
		if visitor.visit_expr(self) == FlowControl::Continue {
			match self {
				UnaryPre(expr) => expr.walk(visitor),
				UnaryPost(expr) => expr.walk(visitor),
				Binary(expr) => expr.walk(visitor),
				Assignment(expr) => expr.walk(visitor),
				FnCall(expr) => expr.walk(visitor),
				TypeCtor(expr) => expr.walk(visitor),
				Group(expr) => expr.walk(visitor),
				Bitcast(expr) => expr.walk(visitor),
				Literal(expr) => visitor.visit_literal_expr(expr),
				Ident(expr) => visitor.visit_ident_expr(expr),
				Primary(expr) => expr.walk(visitor),
				Error(err) => visitor.visit_error(err),
			};
		}
	}
}
impl Walk for UnaryPreExpr {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_unary_pre_expr(self) == FlowControl::Continue {
			self.expr.walk(visitor);
		}
	}
}
impl Walk for UnaryPostExpr {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_unary_post_expr(self) == FlowControl::Continue {
			self.expr.walk(visitor);
		}
	}
}
impl Walk for BinaryExpr {
	fn walk(&self, visitor: &mut dyn Visitor) {
		let visit_method = match self.op.lexeme().as_str() {
			"=" => Visitor::visit_assignment_expr,
			_ => Visitor::visit_binary_expr,
		};
		if visit_method(visitor, self) == FlowControl::Continue {
			self.lhs.walk(visitor);
			self.rhs.walk(visitor);
		}
	}
}
impl Walk for FnCallExpr {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_fn_call_expr(self) == FlowControl::Continue {
			visitor.visit_ident_expr(&self.ident);
			for arg in self.arguments.arguments.iter() {
				arg.walk(visitor);
			}
		}
	}
}
impl Walk for TypeCtorExpr {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_type_ctor_expr(self) == FlowControl::Continue {
			self.ty.walk(visitor);
			for arg in self.arguments.arguments.iter() {
				arg.walk(visitor);
			}
		}
	}
}
impl Walk for GroupExpr {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_group_expr(self) == FlowControl::Continue {
			self.expr.walk(visitor);
		}
	}
}
impl Walk for BitcastExpr {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_bitcast_expr(self) == FlowControl::Continue {
			self.ty.walk(visitor);
			self.expr.walk(visitor);
		}
	}
}
impl Walk for PrimaryExpr {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_primary_expr(self) == FlowControl::Continue {
			self.expr.walk(visitor);
			if let Some(ref expr) = self.postfix {
				expr.walk(visitor);
			}
		}
	}
}
impl Walk for PostfixExpr {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_postfix_expr(self) == FlowControl::Continue {
			self.expr.walk(visitor);
			if let Some(ref expr) = self.postfix {
				expr.walk(visitor);
			}
		}
	}
}
impl Walk for TypeDecl {
	fn walk(&self, visitor: &mut dyn Visitor) {
		if visitor.visit_type(self) == FlowControl::Continue {
			if let Some(ref attributes) = self.attributes {
				visitor.visit_attributes(attributes);
			}
			if let Some(ref ty) = self.child_ty {
				ty.walk(visitor);
			}
			visitor.visit_ident_expr(&self.name);
		}
	}
}