//! Parses src/traits.rs and generates $OUT_DIR/generated_delegation.rs.
//! The generated code is include!'d at the end of lib.rs.
//!
//! src/traits.rs をパースして $OUT_DIR/generated_delegation.rs を生成する。
//! 生成コードは lib.rs 末尾で include! される。
//!
//! How it works / 仕組み:
//!   - "Struct" suffix (e.g. SolidStruct → crate::Solid): generates `impl crate::Solid { pub fn ... }`
//!   - "Module" suffix (e.g. IoModule → crate root): generates `pub fn ...` at crate root
//!     (module name is intentionally discarded — the suffix is a developer-facing
//!     organizational marker only, not part of the public API surface)
//!   - その他のサフィックス (e.g. Compound, Wire) はトップレベル委譲を生成しないが、
//!     "Struct" トレイトのスーパートレイトとして参照された場合は、その中の
//!     メソッドも inherent impl に取り込まれる
//!
//! パーサー制約（行ベースのテキスト処理）:
//!   - fn シグネチャは1行に収めること
//!   - #[cfg] は直前1行のみ認識
//!   - ライフタイム・where 句・default impl は本体を1行で書けばサポートされる
//!
//! Called from / 呼び出し元:
//!   build.rs: build_delegation(include_str!("src/traits.rs"), &out_dir)

use std::collections::HashSet;
use std::path::Path;

enum DelegationKind {
	/// FooStruct → impl crate::Foo { pub fn ... { <Self as FooStruct>::... } }
	InherentImpl { concrete_type: String },
	/// FooModule → pub fn ... at crate root { <crate::Foo as FooModule>::... }
	/// The `Foo` name is developer-facing only and does not leak into the user API.
	FreeFn { struct_path: String },
}

fn delegation_kind(trait_name: &str) -> Option<DelegationKind> {
	if trait_name.ends_with("Struct") {
		let type_name = &trait_name[..trait_name.len() - 6];
		Some(DelegationKind::InherentImpl { concrete_type: format!("crate::{}", type_name) })
	} else if trait_name.ends_with("Module") {
		let base = &trait_name[..trait_name.len() - 6];
		Some(DelegationKind::FreeFn { struct_path: format!("crate::{}", base) })
	} else {
		None // skip traits with unrecognized suffix (e.g. Compound, Wire) — only used as supertraits
	}
}

/// Type replacements: only associated types need resolving.
/// Short names (DVec3, Mesh, Error, Color) are already in scope via pub use in lib.rs.
///
/// `Self::Edge` / `Self::Face` (from `SolidStruct`) and `Self::Solid` (from
/// `IoModule`) become bare type names, which resolve to the active backend's
/// concrete types via `pub use` re-exports in `lib.rs`. This keeps `traits.rs`
/// itself free of backend-specific type references. `*Module` traits emit their
/// methods as free functions at the crate root.
const TYPE_MAP: &[(&str, &str)] = &[("Self::Face", "Face"), ("Self::Edge", "Edge"), ("Self::Solid", "Solid")];

struct Method {
	cfg: Option<String>,
	signature: String,
	name: String,
	args: Vec<String>,
	has_self: bool,
	/// Trait this method was originally declared in.
	/// Used to emit `<Self as crate::traits::ORIGIN>::method(...)`.
	origin_trait: String,
}

struct TraitDef {
	name: String,
	supertraits: Vec<String>,
	methods: Vec<Method>,
}

pub fn build_delegation(traits_src: &str, out_dir: &Path) {
	let mut output = String::new();
	output.push_str("// Auto-generated by build_delegation.rs — do not edit.\n\n");

	let traits = parse_traits(traits_src);

	for td in &traits {
		let kind = match delegation_kind(&td.name) {
			Some(k) => k,
			None => continue, // skip traits with unrecognized suffix
		};

		// Collect this trait's methods plus those from supertraits transitively.
		// Child methods override parent methods of the same name.
		let mut seen: HashSet<String> = HashSet::new();
		let mut collected: Vec<&Method> = Vec::new();
		for m in &td.methods {
			if seen.insert(m.name.clone()) {
				collected.push(m);
			}
		}
		collect_super_methods(&td.supertraits, &traits, &mut seen, &mut collected);

		match &kind {
			DelegationKind::InherentImpl { concrete_type } => {
				output.push_str(&format!("impl {} {{\n", concrete_type));
				for method in &collected {
					emit_method(&mut output, method, concrete_type, false);
				}
				output.push_str("}\n\n");
			}
			DelegationKind::FreeFn { struct_path } => {
				for method in &collected {
					emit_method(&mut output, method, struct_path, true);
				}
				output.push_str("\n");
			}
		}
	}

	let out_path = out_dir.join("generated_delegation.rs");
	std::fs::write(&out_path, output).expect("Failed to write generated_delegation.rs");
}

/// Walk the supertrait list transitively, collecting methods that haven't already
/// been seen (i.e., not overridden by a child trait).
fn collect_super_methods<'a>(supers: &[String], all: &'a [TraitDef], seen: &mut HashSet<String>, out: &mut Vec<&'a Method>) {
	for super_name in supers {
		let parent = match all.iter().find(|t| &t.name == super_name) {
			Some(p) => p,
			None => continue, // not user-defined (e.g. Sized, Clone) — skip silently
		};
		for m in &parent.methods {
			if seen.insert(m.name.clone()) {
				out.push(m);
			}
		}
		collect_super_methods(&parent.supertraits, all, seen, out);
	}
}

fn emit_method(output: &mut String, method: &Method, concrete_type: &str, is_module: bool) {
	if let Some(cfg) = &method.cfg {
		output.push_str(&format!("    {}\n", cfg));
	}
	let sig = resolve_types(&method.signature, concrete_type);
	output.push_str(&format!("    pub {}", sig));
	output.push_str(" {\n");
	let trait_path = format!("crate::traits::{}", method.origin_trait);
	let args_str = method.args.join(", ");
	if is_module {
		output.push_str(&format!("        <{} as {}>::{}({})\n", concrete_type, trait_path, method.name, args_str));
	} else if method.has_self {
		output.push_str(&format!("        <Self as {}>::{}(self, {})\n", trait_path, method.name, args_str));
	} else {
		output.push_str(&format!("        <Self as {}>::{}({})\n", trait_path, method.name, args_str));
	}
	output.push_str("    }\n");
}

/// Replace short type names and `Self` with fully-qualified paths.
///
/// Order matters: `Self::Elem` / `Self::Face` / `Self::Edge` (associated types) MUST
/// be replaced before bare `Self`, otherwise `Self` would be substituted first and
/// produce `crate::Solid::Elem` etc.
fn resolve_types(sig: &str, concrete_type: &str) -> String {
	let mut s = sig.to_string();
	// Self::Elem is per-impl, so replace dynamically with the concrete type.
	s = replace_type_word(&s, "Self::Elem", concrete_type);
	for (from, to) in TYPE_MAP {
		s = replace_type_word(&s, from, to);
	}
	s = replace_self_type(&s, concrete_type);
	s
}

/// Replace whole-word occurrences of `from` with `to` in type positions.
fn replace_type_word(sig: &str, from: &str, to: &str) -> String {
	let mut result = String::new();
	let bytes = sig.as_bytes();
	let from_bytes = from.as_bytes();
	let from_len = from_bytes.len();
	let mut i = 0;
	while i < bytes.len() {
		if i + from_len <= bytes.len() && &sig[i..i + from_len] == from {
			let before_ok = i == 0 || !is_ident_char(bytes[i - 1]);
			let after_ok = i + from_len >= bytes.len() || !is_ident_char(bytes[i + from_len]);
			if before_ok && after_ok {
				result.push_str(to);
				i += from_len;
				continue;
			}
		}
		result.push(bytes[i] as char);
		i += 1;
	}
	result
}

fn is_ident_char(b: u8) -> bool {
	b.is_ascii_alphanumeric() || b == b'_'
}

/// Replace `Self` as a type (not `self` as a parameter, not `Self::Foo` as an
/// associated type) with the concrete type.
fn replace_self_type(sig: &str, concrete_type: &str) -> String {
	let mut result = String::new();
	let bytes = sig.as_bytes();
	let mut i = 0;
	while i < bytes.len() {
		if i + 4 <= bytes.len() && &sig[i..i + 4] == "Self" {
			let before_ok = i == 0 || !bytes[i - 1].is_ascii_alphanumeric();
			// Reject if followed by `:` (associated type path like `Self::Elem`) or
			// by an identifier character (e.g. `SelfRef`). Associated types must be
			// resolved by an earlier pass before reaching here.
			let next = if i + 4 < bytes.len() { Some(bytes[i + 4]) } else { None };
			let after_ok = match next {
				None => true,
				Some(b) => !b.is_ascii_alphanumeric() && b != b':',
			};
			if before_ok && after_ok {
				result.push_str(concrete_type);
				i += 4;
				continue;
			}
		}
		result.push(bytes[i] as char);
		i += 1;
	}
	result
}

/// Parse trait definitions from traits.rs source.
fn parse_traits(src: &str) -> Vec<TraitDef> {
	let mut traits = Vec::new();
	let lines: Vec<&str> = src.lines().collect();
	let mut i = 0;

	while i < lines.len() {
		let line = lines[i].trim();

		// Look for trait definition. Skip comment lines so example snippets in
		// doc-comments (e.g. `//! - \`pub trait Foo: ...{\``) aren't parsed as
		// real trait definitions.
		if !line.starts_with("//") && line.contains("trait ") && line.contains('{') {
			if let Some((name, supertraits)) = extract_trait_header(line) {
				let mut methods = Vec::new();
				i += 1;

				let mut pending_cfg: Option<String> = None;
				while i < lines.len() {
					let line = lines[i].trim();

					if line == "}" {
						break;
					}

					// Collect #[cfg(...)] attributes
					if line.starts_with("#[cfg(") {
						pending_cfg = Some(line.to_string());
						i += 1;
						continue;
					}

					// Skip associated type declarations
					if line.starts_with("type ") {
						i += 1;
						continue;
					}

					// Skip comments and empty lines
					if line.starts_with("//") || line.is_empty() {
						i += 1;
						continue;
					}

					// Parse fn signature
					if line.starts_with("fn ") {
						if let Some(method) = parse_method(line, pending_cfg.take(), name.clone()) {
							methods.push(method);
						}
						// Skip multi-line default impl body: if line ends with '{', skip until matching '}'
						if line.ends_with('{') {
							let mut depth = 1usize;
							while depth > 0 && i + 1 < lines.len() {
								i += 1;
								let body_line = lines[i].trim();
								depth += body_line.matches('{').count();
								depth -= body_line.matches('}').count();
							}
						}
					} else {
						pending_cfg = None;
					}

					i += 1;
				}

				traits.push(TraitDef { name, supertraits, methods });
			}
		}
		i += 1;
	}
	traits
}

/// Extract `(trait_name, supertraits)` from a line like
/// `pub trait SolidStruct: Sized + Clone + Compound {`.
///
/// Lifetime bounds (e.g. `'static`) are filtered out. Whether a returned name
/// refers to a user-defined trait is decided later by lookup.
fn extract_trait_header(line: &str) -> Option<(String, Vec<String>)> {
	let idx = line.find("trait ")?;
	let rest = &line[idx + 6..];
	let name_end = rest.find(|c: char| c == ':' || c == '{' || c == ' ')?;
	let name = rest[..name_end].trim().to_string();

	let mut supertraits = Vec::new();
	if let Some(colon) = rest.find(':') {
		if let Some(brace) = rest.find('{') {
			if colon < brace {
				let bounds_str = &rest[colon + 1..brace];
				for s in bounds_str.split('+') {
					let s = s.trim();
					if s.is_empty() || s.starts_with('\'') {
						continue;
					}
					supertraits.push(s.to_string());
				}
			}
		}
	}
	Some((name, supertraits))
}

fn parse_method(line: &str, cfg: Option<String>, origin_trait: String) -> Option<Method> {
	// "fn name(args) -> RetType;" or "fn name(args) -> RetType { default_impl }"
	let line = line.trim().trim_end_matches(';');
	// Strip default impl body: "-> Self { ... }" → "-> Self"
	let line = if let Some(brace) = line.find('{') { line[..brace].trim() } else { line };

	let fn_idx = line.find("fn ")?;
	let rest = &line[fn_idx + 3..];

	let paren_open = rest.find('(')?;
	let name_with_generics = rest[..paren_open].trim();
	let name = if let Some(angle) = name_with_generics.find('<') { name_with_generics[..angle].trim().to_string() } else { name_with_generics.to_string() };

	let paren_close = rest.rfind(')')?;
	let args_str = &rest[paren_open + 1..paren_close];

	// Determine if method has self parameter, and collect non-self arg names
	let mut has_self = false;
	let mut args = Vec::new();

	for arg in split_args(args_str) {
		let arg = arg.trim();
		if arg.is_empty() {
			continue;
		}
		if arg == "self" || arg == "&self" || arg == "mut self" || arg == "&mut self" {
			has_self = true;
			continue;
		}
		// "name: Type" -> extract name
		if let Some(colon) = arg.find(':') {
			let arg_name = arg[..colon].trim().to_string();
			args.push(arg_name);
		}
	}

	// Reconstruct signature: "fn name(self/&self, args) -> RetType [where ...]"
	let signature = line[fn_idx..].to_string();

	Some(Method { cfg, signature, name, args, has_self, origin_trait })
}

/// Split arguments by ',' while respecting `<>` and `()` nesting.
/// `()` tracking is required for `impl Fn(A, B) -> C` style parameters.
fn split_args(s: &str) -> Vec<&str> {
	let mut result = Vec::new();
	let mut angle = 0usize;
	let mut paren = 0usize;
	let mut start = 0;
	for (i, b) in s.bytes().enumerate() {
		match b {
			b'<' => angle += 1,
			b'>' => {
				if angle > 0 {
					angle -= 1;
				}
			}
			b'(' => paren += 1,
			b')' => {
				if paren > 0 {
					paren -= 1;
				}
			}
			b',' if angle == 0 && paren == 0 => {
				result.push(&s[start..i]);
				start = i + 1;
			}
			_ => {}
		}
	}
	result.push(&s[start..]);
	result
}
