//! Backend-independent trait definitions.
//!
//! Trait hierarchy:
//!
//! ```text
//! Transform  ─┬─  SolidStruct  (pub(crate))  ──  Solid
//!             └─  EdgeStruct   (pub(crate))  ──  Edge
//!                 FaceStruct   (pub(crate))  ──  Face
//! ```
//!
//! 各トレイトは **単体の形状** (`Solid` / `Edge` / `Face`) にのみ実装される。形状の
//! 集合は `Vec<T>` や `[T; N]` などのコレクション型としてそのまま扱い、変換・集約は
//! 標準のイテレータ慣用句で行う（専用のコレクション・トレイトは持たない）:
//!   - 変換: `vec.into_iter().map(|s| s.translate(v)).collect::<Vec<_>>()` /
//!     配列は `arr.map(|s| s.translate(v))`
//!   - 集約: `solids.iter().map(|s| s.volume()).sum::<f64>()`、重心や慣性テンソルは
//!     `a.inertia() + b.inertia()` のように呼び出し側で合成する
//!
//! sweep / loft / extrude / boolean / I/O はコレクション型を直接受けず
//! `impl IntoIterator<Item = &Edge>` / `&Solid` を取る。順序付きの `Vec<Edge>` が
//! そのままワイヤ（開/閉ポリライン）の役割を果たす — 専用 `Wire` 型は無い。
//!
//! - `Transform` (crate-internal): spatial ops (translate/rotate/scale/mirror).
//!   Geometry-agnostic. Implemented for the single shapes (`Solid`, `Edge`) only.
//!   Not re-exported from `lib.rs`, so external users cannot name it — they reach the
//!   same methods as inherent methods on `Solid` / `Edge` (generated by
//!   examples/codegen.rs walking the supertrait chain).
//!
//! - `SolidStruct: Sized + Clone + Transform` (pub(crate)): backend implementation
//!   trait holding every Solid operation — constructors, topology accessors, queries
//!   (volume/area/center/inertia/bounding_box/contains), color, boolean primitives,
//!   I/O. examples/codegen.rs parses this and generates pub inherent methods on Solid,
//!   walking the supertrait chain so `Transform` methods are exposed inherently too.
//!   Trait name follows `<Type>Struct` convention (SolidStruct → Solid).
//!
//! - `EdgeStruct: Sized + Clone + Transform` (pub(crate)): single-edge constructors
//!   plus per-edge queries (start_point/end_point/tangents/is_closed/project/
//!   approximation_segments), exposed inherently on `Edge`.
//!
//! ## 関連型による型ヒエラルキー（バックエンド非依存ルール）
//!
//! このファイルはバックエンド（occt / pure）の具象型を一切名指ししない。具象型への
//! 参照はすべて **関連型** 経由にすること。`use crate::{Edge, Face, Solid};` を
//! このファイルに書いてはいけない（書くと、両バックエンドが同時に存在する将来構成で
//! どちらの型を指すか曖昧になる）。
//!
//! ### 階層と関連型の向き
//!
//! 型同士の依存に **一方向の階層** を導入し、上位が下位を関連型として参照する：
//!
//! ```text
//!   EdgeStruct       ← 単独。下位を一切知らない
//!   FaceStruct       ← type Edge: EdgeStruct;
//!   SolidStruct      ← type Edge: EdgeStruct;  type Face: FaceStruct;
//!                       I/O メソッド (read_step / write_step / mesh など) も SolidStruct に同居
//! ```
//!
//! 下位（Edge/Face）→ 上位（Solid）への参照は持たせない。例えば「Edge を sweep して
//! Solid を作る」操作は `EdgeStruct::sweep` ではなく `SolidStruct::sweep(profile, spine)`
//! として上位側に置き、ヒエラルキーを保つ。逆向き参照を導入する瞬間に associated type
//! の循環や Backend バンドルトレイトが必要になり、examples/codegen.rs のテキスト処理が
//! 追従できなくなる。
//!
//! ### 命名と codegen の対応
//!
//! - `SolidStruct` の `type Edge` / `type Face` という名前は examples/codegen.rs の
//!   `TYPE_MAP` と一致させること。`Self::Edge` / `Self::Face` は生成時に
//!   バックエンドの具象型名（`Edge` / `Face`）へ置換され、`lib.rs` の
//!   `pub use occt::{Solid, Edge, Face};` により実体に解決される。
//! - 戻り型・引数型は `Vec<Self::Edge>`、`impl IntoIterator<Item = &'a Self>` の
//!   ように常に関連型 / Self 経由で書く（具象型名を直接書かない）。
//! - associated type 宣言（`type Foo: Bound;`）はパーサーが行頭でスキップするので、
//!   メソッドと同じインデントで 1 行に収めること。
//!
//! パーサー挙動と制約（examples/codegen.rs — 行ベースのテキスト処理）:
//!
//! トレイトヘッダ:
//! - `pub trait Foo: A + B + C {` から名前と supertrait リスト（`+` 区切り）を抽出する
//! - `Foo` が `Struct` サフィックスを持つトレイトの supertrait に出現した場合、
//!   `Foo` のメソッドも親側の inherent impl に取り込まれる（再帰的に祖先まで辿る）
//! - 解析対象トレイト一覧に存在しない名前（`Sized`, `Clone`, ライフタイム束縛 `'a` 等）は
//!   黙って無視される
//! - 同名メソッドは子トレイト優先で重複排除される（親のオーバーライド）
//! - ヘッダ行は1行に収めること（`where` 句を改行して書くと検出されない）
//! - `where` 節は `: A + B + C where ...` の形で同一行に書いてよい。パーサーは
//!   ` where ` 以降を supertrait リストから切り落とすため `Add + Sub` のような
//!   `+` を含む bound が where 句にあっても supertrait 抽出を汚染しない
//!
//! メソッドシグネチャ:
//! - fn シグネチャは1行に収めること（`where` 句・ライフタイム・ジェネリクス引数も同じ行）
//! - default impl はサポート。本体が1行に収まる場合はそのまま、複数行の場合も
//!   `{...}` ブロックを brace 深さでスキップする
//! - ライフタイム引数 `<'a, 'b>` および `where Self: 'a` のような句はそのまま保持される。
//!   `Self` は inherent impl 文脈では具象型と等価なので置換せず残す（`Self::Elem` のような
//!   関連型のみ事前に concrete type へ置換される）
//! - `Self::Elem` は impl 対象の具象型へ置換される。`Self::Face` / `Self::Edge` /
//!   `Self::Solid` はそれぞれ `Face` / `Edge` / `Solid` へ置換され、`lib.rs` の
//!   バックエンド再エクスポートで解決される
//!
//! その他:
//! - `#[cfg(...)]` は直前1行のみ認識し、続く fn に付与される
//! - `type Foo;` などの associated type 宣言は無視される（メソッド生成対象外）


#[cfg(feature = "color")]
use crate::common::color::Color;
use crate::common::boolean::Boolean;
use crate::common::error::Error;
use crate::common::mesh::Mesh;
use glam::{DMat3, DQuat, DVec3};

/// Tessellation parameters for `Solid::mesh` and `Edge::approximation_segments`.
///
/// Use `Tessellation::default()` for a scale-independent default that suits
/// most shapes; override individual fields with struct update syntax, e.g.
/// `Tessellation { deflection_linear: 0.1, relative_linear: false, ..Default::default() }`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Tessellation {
	/// Linear (chord) deflection: max distance between the facet/segment and the
	/// true surface/curve. When `relative_linear` is true this is a fraction of
	/// the edge's bounding-box size rather than an absolute distance.
	pub deflection_linear: f64,
	/// Angular deflection in radians: max angle between adjacent facet/segment
	/// directions. Controls smoothness of curves independently of scale.
	pub deflection_angular: f64,
	/// Interpret `deflection_linear` as relative to the local feature size
	/// (each edge's bounding-box max dimension) instead of an absolute distance.
	pub relative_linear: bool,
}

impl Default for Tessellation {
	fn default() -> Self {
		// Relative 0.2% linear + 0.5 rad angular: scale-independent and "just
		// right" for most shapes without over-tessellating.
		Self { deflection_linear: 0.004, deflection_angular: 0.5, relative_linear: true }
	}
}

// ==================== Transform ====================

/// Spatial-transform operations: translate / rotate / scale / mirror.
///
/// Orthogonal to any specific geometry kind. Implemented for the individual
/// shapes (`Solid`, `Edge`) only; collections of shapes are transformed with
/// standard iterator idioms — `vec.into_iter().map(|s| s.translate(v)).collect()`
/// or, for arrays, `arr.map(|s| s.translate(v))`.
///
/// **Visibility**: this trait is declared `pub` but the enclosing `traits`
/// module is `pub(crate)` in `lib.rs`, and `Transform` is intentionally NOT
/// re-exported at the crate root. External users cannot name it; they reach the
/// same methods as inherent methods on `Solid` / `Edge`, which
/// `examples/codegen.rs` emits by walking the supertrait chain.
pub trait Transform: Sized {
	fn translate(self, translation: DVec3) -> Self;
	fn rotate(self, axis_origin: DVec3, axis_direction: DVec3, angle: f64) -> Self;
	fn rotate_x(self, angle: f64) -> Self { self.rotate(DVec3::ZERO, DVec3::X, angle) }
	fn rotate_y(self, angle: f64) -> Self { self.rotate(DVec3::ZERO, DVec3::Y, angle) }
	fn rotate_z(self, angle: f64) -> Self { self.rotate(DVec3::ZERO, DVec3::Z, angle) }
	fn scale(self, center: DVec3, factor: f64) -> Self;
	fn mirror(self, plane_origin: DVec3, plane_normal: DVec3) -> Self;
	/// Rotate so that local +X axis aligns with `new_x`, with local +Y projected toward `y_hint`.
	/// Rotation is around the world origin. Panics on degenerate input (zero or parallel vectors).
	fn align_x(self, new_x: DVec3, y_hint: DVec3) -> Self {
		let x = new_x.try_normalize().expect("align_x: new_x is zero");
		let z = x.cross(y_hint).try_normalize().expect("align_x: y_hint parallel to new_x");
		let (axis, angle) = DQuat::from_mat3(&DMat3::from_cols(x, z.cross(x), z)).to_axis_angle();
		self.rotate(DVec3::ZERO, axis, angle)
	}
	/// Rotate so that local +Y axis aligns with `new_y`, with local +Z projected toward `z_hint`.
	/// Rotation is around the world origin. Panics on degenerate input (zero or parallel vectors).
	fn align_y(self, new_y: DVec3, z_hint: DVec3) -> Self {
		let y = new_y.try_normalize().expect("align_y: new_y is zero");
		let x = y.cross(z_hint).try_normalize().expect("align_y: z_hint parallel to new_y");
		let (axis, angle) = DQuat::from_mat3(&DMat3::from_cols(x, y, x.cross(y))).to_axis_angle();
		self.rotate(DVec3::ZERO, axis, angle)
	}
	/// Rotate so that local +Z axis aligns with `new_z`, with local +X projected toward `x_hint`.
	/// Rotation is around the world origin. Panics on degenerate input (zero or parallel vectors).
	fn align_z(self, new_z: DVec3, x_hint: DVec3) -> Self {
		let z = new_z.try_normalize().expect("align_z: new_z is zero");
		let y = z.cross(x_hint).try_normalize().expect("align_z: x_hint parallel to new_z");
		let (axis, angle) = DQuat::from_mat3(&DMat3::from_cols(y.cross(z), y, z)).to_axis_angle();
		self.rotate(DVec3::ZERO, axis, angle)
	}
}

// ==================== ProfileOrient ====================

/// Controls how the cross-section profile is oriented as it travels along the
/// spine in [`SolidStruct::sweep`] and [`SolidStruct::sweep_sections`].
///
/// **どれを選ぶか:**
///
/// | やりたいこと | 選ぶ variant |
/// |---|---|
/// | 直線押し出し / profile を回したくない | [`Fixed`](Self::Fixed) |
/// | ねじ・バネ・つる (helix 系) | [`Torsion`](Self::Torsion) または [`Up`](Self::Up)`(axis)` |
/// | 道路・線路・パイプ (重力方向を保ちたい) | [`Up`](Self::Up)`(DVec3::Z)` |
/// | 上記に当てはまらない 3D 自由曲線 | [`Torsion`](Self::Torsion) |
/// | 任意の捻り制御 (メビウスの輪等) | [`Auxiliary`](Self::Auxiliary)`(&aux_spine)` |
///
/// **`Torsion` と `Up(axis)` の関係**: helix のような定曲率・定 torsion 曲線では、
/// この 2 つは数学的に等価なトリヘドロンを生成します。`Torsion` は曲線の主法線
/// (= `d²C/dt²` の T 直交成分) に profile を貼り付け、`Up` はユーザが渡した
/// 方向を T 直交平面に射影して binormal にする — helix 上ではこの 2 つが
/// 同じ axis を指すため、結果が一致します。helix 以外の曲線では一致しません。
#[derive(Clone, Copy)]
pub enum ProfileOrient<'a> {
	/// Profile is parallel-transported along the spine **without rotating**.
	/// All cross-sections stay parallel to the starting orientation.
	///
	/// - **適**: 直線 spine (押し出し)
	/// - **不適**: 曲がる spine (profile が tangent と直交しなくなり、見た目が壊れる)
	Fixed,

	/// Profile rotates following the spine's principal normal direction
	/// (= the T-perpendicular component of `d²C/dt²`). Equivalent to OCCT's
	/// raw Frenet–Serret frame.
	///
	/// - **適**: helix, spring, screw thread, twisted ribbon — 定曲率・
	///   定 torsion な曲線、および 3D 自由曲線で「曲線の自然な捻れ」を
	///   profile に反映させたいケース
	/// - **不適**: 変曲点 (curvature → 0) を含む 2D / 3D スプライン。
	///   変曲点で N が不定になり profile が 180° flip しうる。その場合は
	///   `Up` を使う
	Torsion,

	/// Profile keeps the given direction as its "up" axis (binormal).
	///
	/// - **適**: 道路 (`up = DVec3::Z`), 線路, パイプ, 運河 — 重力方向を
	///   保ちたい sweep 全般
	/// - **不適**: 任意の点で `up` が tangent と平行になる spine
	Up(DVec3),

	/// Profile orientation is controlled by an auxiliary spine curve.
	/// The profile's X axis tracks the direction toward the auxiliary spine.
	///
	/// - **適**: メビウスの輪、ステラレーターの断面回転、任意の捻り制御
	Auxiliary(&'a [crate::Edge]),
}

// ==================== BSplineEnd ====================

/// End-condition selector for [`EdgeStruct::bspline`].
///
/// A cubic B-spline interpolating N data points has 4(N−1) coefficient
/// degrees of freedom. The interpolation conditions plus C¹/C² continuity
/// at internal knots fix all but **2** of those. This enum chooses how
/// the remaining 2 degrees are determined.
///
/// **どれを選ぶか:**
///
/// | やりたいこと | 選ぶ variant |
/// |---|---|
/// | 閉じた断面プロファイル (プラズマ poloidal section, 自由曲線リング) | [`Periodic`](Self::Periodic) |
/// | 開いた自由曲線で端点接線が分からない | [`NotAKnot`](Self::NotAKnot) |
/// | 開いた自由曲線で端点接線が物理的に決まっている | [`Clamped`](Self::Clamped) |
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BSplineEnd {
	/// Build a periodic curve. Start and end coincide with **C² continuity**
	/// (position + tangent + curvature all match at the wrap-around).
	/// The first data point must NOT be repeated at the end — periodicity
	/// is encoded in the basis function structure. Passing a duplicated
	/// endpoint yields [`Error::InvalidEdge`].
	///
	/// Requires ≥ 3 distinct points. The most common choice for closed
	/// profile curves (plasma poloidal sections, screw threads, gear teeth)
	/// where the start/end seam should be invisible.
	Periodic,

	/// Open curve, end conditions chosen so that the cubics on the first
	/// two intervals collapse into a single cubic (and likewise at the
	/// other end). The 2nd and (N−1)th data points behave as plain
	/// interpolation targets that do not act as real knots.
	///
	/// This is the default in MATLAB, SciPy, and OCCT itself. Best when
	/// nothing is known about end behavior — gives the most "natural"
	/// looking boundary because the boundary cubic is fit to 3 data
	/// points instead of being constrained by an artificial derivative
	/// condition. Requires ≥ 2 points.
	NotAKnot,

	/// Open curve with explicit start/end tangent vectors. The magnitude
	/// of each vector controls how strongly the curve is pulled along
	/// that direction near the boundary — a unit vector gives a gentle
	/// hint, a longer vector pulls more aggressively. Requires ≥ 2 points.
	Clamped {
		start: DVec3,
		end: DVec3,
	},
}

// ==================== EdgeStruct ====================

/// Backend-independent edge trait (pub(crate) — not exposed to users).
///
/// Single-edge constructors plus per-edge queries (endpoints / tangents /
/// is_closed / approximation_segments / project). Spatial transforms come from
/// the `Transform` supertrait. There is no dedicated wire type or collection
/// trait: an ordered `Vec<Edge>` *is* a wire, and callers aggregate over it with
/// plain iterator idioms (e.g. `edges.first().unwrap().start_point()`,
/// `edges.iter().any(|e| e.is_closed())`).
///
/// All constructors return `Result<..., Error>`. Invalid inputs (degenerate
/// geometry, zero/negative radius, collinear arc points, etc.) yield
/// `Error::InvalidEdge(String)` with a message that identifies the failing
/// constructor and the offending parameters.
pub trait EdgeStruct: Sized + Clone + Transform {
	/// Stable, backend-defined identity for this edge. Two `Edge` values
	/// returning the same `id()` refer to the same topology element.
	/// Use to compare edges across `Solid::iter_edge()` / `Face::iter_edge()`
	/// (e.g. `face.iter_edge().any(|e| e.id() == edge.id())`).
	fn id(&self) -> u64;

	// --- Per-edge queries ---
	/// Start point of the edge (evaluated at the curve's first parameter).
	fn start_point(&self) -> DVec3;
	/// End point of the edge (evaluated at the curve's last parameter).
	fn end_point(&self) -> DVec3;
	/// Unit tangent at the start point, in the curve's native parameter direction.
	fn start_tangent(&self) -> DVec3;
	/// Unit tangent at the end point, in the curve's native parameter direction.
	fn end_tangent(&self) -> DVec3;
	/// Whether the edge's underlying geometry is closed (e.g. a full circle).
	fn is_closed(&self) -> bool;
	/// Polyline approximation of the edge within `tolerance`, as ordered points.
	fn approximation_segments(&self, tessellation: Tessellation) -> Vec<DVec3>;
	/// Project `p` onto the edge and return `(closest_point, unit_tangent)`.
	/// The tangent follows the curve's native parameter direction. Panics only
	/// on an edge with no 3D geometric curve (an FFI-level bug, which
	/// cadrum-built edges never produce), not on degenerate user input.
	fn project(&self, p: DVec3) -> (DVec3, DVec3);

	/// Construct a single helical edge on a cylindrical surface centered at
	/// the world origin.
	///
	/// - `radius`: cylinder radius
	/// - `pitch`: rise per full revolution
	/// - `height`: total rise (number of turns = `height / pitch`)
	/// - `axis`: cylinder axis direction (must be non-zero)
	/// - `x_ref`: reference direction that anchors the local +X axis of the
	///   cylindrical frame. The helix start point is
	///   `radius * normalize(component of x_ref orthogonal to axis)`.
	///   `x_ref` must not be parallel to `axis`.
	///
	/// Making `x_ref` explicit guarantees the start point is deterministic
	/// rather than depending on whatever orthogonal direction OCCT picks
	/// from `axis` alone.
	fn helix(radius: f64, pitch: f64, height: f64, axis: DVec3, x_ref: DVec3) -> Result<Self, Error>;

	/// Build a closed polygon from a sequence of points and return its
	/// constituent edges in order. The polygon is **always closed**: the
	/// last point is automatically connected back to the first.
	// 非平面の点列も受理する (検証しない) — `Solid::sweep` で face 化に失敗
	// したとき `Error::SweepFailed` で気付ける想定なので、入力側での事前検査は省略。
	fn polygon<'a>(points: impl IntoIterator<Item = &'a DVec3>) -> Result<Vec<Self>, Error>;

	/// Closed circle of radius `r` centered at the world origin, lying in
	/// the plane normal to `axis`. Returns a single edge (one Geom_Circle
	/// curve — not a polygon approximation).
	///
	/// The circle's start/end point (at which `start_point()` /
	/// `start_tangent()` are evaluated) is chosen by the backend from an
	/// arbitrary orthogonal direction to `axis`. Callers that need a
	/// deterministic start point should translate/rotate the resulting
	/// edge into place rather than relying on the implicit choice.
	fn circle(radius: f64, axis: DVec3) -> Result<Self, Error>;

	/// Straight line segment from `a` to `b`. Fails with `InvalidEdge` if
	/// `a == b` (zero-length segment).
	fn line(a: DVec3, b: DVec3) -> Result<Self, Error>;

	/// Circular arc through three points: start, mid, end. The unique circle
	/// passing through the three points defines the arc; `mid` disambiguates
	/// which of the two possible arcs is returned (the one passing through
	/// `mid`). Fails with `InvalidEdge` if `mid` is collinear with `start`
	/// and `end`, or if any pair of points coincides.
	fn arc_3pts(start: DVec3, mid: DVec3, end: DVec3) -> Result<Self, Error>;

	/// Cubic B-spline curve interpolating the given data points.
	///
	/// **The points are interpolation targets, not control points.** OCCT's
	/// `GeomAPI_Interpolate` solves a linear system so the resulting curve
	/// passes through every input point exactly. The internal control points
	/// and knots are computed automatically and not exposed.
	///
	/// - Degree: 3 (cubic)
	/// - Parameterization: chord-length
	/// - End behavior: chosen by `end` (see [`BSplineEnd`])
	///
	/// Returns one `Edge` wrapping a single `Geom_BSplineCurve`. Use as a
	/// sweep/loft section by wrapping in `vec![...]` or `&[edge]`.
	///
	/// # Errors
	///
	/// Returns [`Error::InvalidEdge`] if:
	/// - point count is below the minimum (≥3 for `Periodic`, ≥2 otherwise)
	/// - `BSplineEnd::Periodic` is requested but the first and last points
	///   coincide (periodicity is encoded in the basis; do not duplicate)
	/// - OCCT's interpolation fails (degenerate point distribution, etc.)
	fn bspline<'a>(points: impl IntoIterator<Item = &'a DVec3>, end: BSplineEnd) -> Result<Self, Error>;
}

/// Backend-independent face trait (pub(crate) — not exposed to users).
///
/// `Face` is a query handle for surfaces in a solid. Used to read identity
/// (for colormap / boolean history matching) and to project external 3D
/// points onto the face for snap-to-surface workflows.
///
/// examples/codegen.rs generates `impl Face { pub fn ... }` from this trait
/// so callers reach the methods inherently as `face.id()` / `face.project(p)`.
pub trait FaceStruct: Sized {
	type Edge: EdgeStruct;

	/// Stable, backend-defined identity for this face. Two `Face` values
	/// returning the same `id()` refer to the same topology element. Used
	/// to look up entries in `Solid::colormap` or to match faces against
	/// boolean / clean operation history. The numeric value itself has no
	/// meaning beyond equality / hash use.
	fn id(&self) -> u64;

	/// Project a 3D point onto this face. Returns `(closest_point,
	/// outward_normal)`. Sister of `Wire::project` which returns `(closest,
	/// tangent)` on a 1D curve.
	///
	/// The closest hit respects the face's trim — projection lands on the
	/// actual face area, not its underlying infinite surface. To project
	/// onto a full solid, iterate `Solid::iter_face()` and call `project`
	/// on each face; the caller picks the smallest-distance face and keeps
	/// the face object for follow-up queries (e.g. `face.id()` for
	/// colormap lookup).
	///
	/// `outward_normal` is the zero vector when the surface evaluator
	/// cannot define a normal at the closest hit (degenerate surface
	/// point); callers can detect this case via `normal.length() == 0`.
	fn project(&self, p: DVec3) -> (DVec3, DVec3);

	/// Iterate this face's boundary edges (outer wire and any inner wires).
	/// Each edge appears once even when shared between wires. Backends may
	/// cache the result internally; re-calls are expected to be cheap.
	///
	/// Use with `Edge::id()` to test face/edge incidence:
	/// `face.iter_edge().any(|e| e.id() == edge.id())`.
	fn iter_edge(&self) -> impl Iterator<Item = &Self::Edge> + '_;
}

/// Backend-independent solid trait (pub(crate) — not exposed to users).
///
/// Holds every `Solid` operation: constructors, topology accessors, queries
/// (volume / area / center / inertia / bounding_box / contains), color, editing,
/// boolean primitives and I/O. Spatial transforms come from the `Transform`
/// supertrait. Collections of solids (`Vec<Solid>` / `[Solid; N]`) are handled
/// with iterator idioms — e.g. `solids.iter().map(|s| s.volume()).sum::<f64>()`,
/// `a.inertia() + b.inertia()` — not a dedicated collection trait.
///
/// examples/codegen.rs generates `impl Solid { pub fn ... }` from this trait
/// and walks the supertrait chain to expose `Transform` methods inherently as well.
///
/// Associated types `Edge`/`Face` keep this trait backend-independent: each
/// backend (occt / pure) binds them to its own concrete types in the impl.
pub trait SolidStruct: Sized + Clone + Transform{
	type Edge: EdgeStruct;
	type Face: FaceStruct;

	// --- Identity ---
	/// Stable, backend-defined identity for this solid. Two `Solid` values
	/// returning the same `id()` refer to the same topology element.
	/// translate / rotate / color preserve this id; scale / mirror / Clone
	/// rebuild topology and produce a fresh id. Distinct from the ids of
	/// the solid's contained faces / edges (each sub-shape has its own).
	fn id(&self) -> u64;

	// --- Constructors ---
	fn cube(corner0: DVec3, corner1: DVec3) -> Self;
	fn sphere(radius: f64) -> Self;
	fn cylinder(r: f64, height: DVec3) -> Self;
	fn cone(r1: f64, r2: f64, height: DVec3) -> Self;
	fn torus(r1: f64, r2: f64, axis: DVec3) -> Self;
	fn half_space(plane_origin: DVec3, plane_normal: DVec3) -> Self;

	// --- Topology iteration ---
	/// Iterate this solid's unique edges. Each OCCT edge appears once even
	/// when shared between faces. Backends may cache the result internally;
	/// re-calls are expected to be cheap.
	fn iter_edge(&self) -> impl Iterator<Item = &Self::Edge> + '_;
	/// Iterate this solid's faces. Backends may cache the result internally.
	fn iter_face(&self) -> impl Iterator<Item = &Self::Face> + '_;
	/// Iterate face-derivation pairs `[post_id, src_id]` from the most recent
	/// boolean operation that produced this Solid (or its source chain, while
	/// it stays through translate/rotate/color). Empty after primitive/builder
	/// construction, I/O read, scale/mirror, or Clone.
	fn iter_history(&self) -> impl Iterator<Item = [u64; 2]> + '_;

	// --- Queries ---
	/// Volume of the solid (uniform density).
	fn volume(&self) -> f64;
	/// Total surface area of the solid.
	fn area(&self) -> f64;
	/// Center of mass (uniform density).
	fn center(&self) -> DVec3;
	/// Inertia tensor about the **world origin** (uniform density).
	fn inertia(&self) -> DMat3;
	/// Whether `point` lies inside (or on) the solid.
	fn contains(&self, point: DVec3) -> bool;
	/// Axis-aligned bounding box as `[min, max]`.
	fn bounding_box(&self) -> [DVec3; 2];

	// --- Color ---
	/// Paint every face of the solid with `color` (stored in the colormap and
	/// propagated through STEP / BRep / STL / SVG I/O).
	#[cfg(feature = "color")]
	fn color(self, color: impl Into<Color>) -> Self;
	/// Drop all per-face color from this solid.
	#[cfg(feature = "color")]
	fn color_clear(self) -> Self;

	/// Heal/regularize this solid (fuse coplanar faces, drop micro-edges,
	/// repair small inconsistencies). Wraps `ShapeUpgrade_UnifySameDomain`
	/// + cleanup. Failure is reported as `Error::CleanFailed`.
	fn clean(&self) -> Result<Self, Error>;
	/// Extrude a closed profile wire along a direction vector to form a solid.
	///
	/// Internally builds a face from the wire and uses `BRepPrimAPI_MakePrism`.
	/// Fails if the profile is empty, not closed, or the direction is zero-length.
	fn extrude<'a>(profile: impl IntoIterator<Item = &'a Self::Edge>, dir: DVec3) -> Result<Self, Error> where Self::Edge: 'a;

	/// Hollow this solid into a thin-walled shell by removing `open_faces`
	/// (they become openings) and building a wall of signed `thickness` along
	/// each remaining face. Wraps OCCT's `BRepOffsetAPI_MakeThickSolid`.
	///
	/// `thickness` is the wall thickness with direction encoded in its sign:
	/// negative → wall grows inward (carve cavity inside the original volume),
	/// positive → wall grows outward (shell sits outside the original surface,
	/// enclosing the original as its inner boundary).
	///
	/// `open_faces` must be faces of `self` (e.g. selected via `self.iter_face()`).
	/// When `open_faces` is empty, `BRepOffsetAPI_MakeThickSolid` degenerates to
	/// a plain offset shape (no cavity) because it needs at least one removed
	/// face to build the inner wall. The wrapper detects this and falls back to
	/// `BRepOffsetAPI_MakeOffsetShape` + `BRepBuilderAPI_MakeSolid`, assembling
	/// an outer shell and a reversed inner shell into a sealed multi-shell
	/// solid with an internal void (the void is inaccessible from outside).
	/// Fails on OCCT rejection (self-intersecting offset at sharp corners, etc).
	fn shell<'a>(&self, thickness: f64, open_faces: impl IntoIterator<Item = &'a Self::Face>) -> Result<Self, Error> where Self::Face: 'a;

	/// Round the given edges of `self` with a uniform radius. Edges are
	/// typically selected via `self.iter_edge().filter(...)`.
	///
	/// Wraps `BRepFilletAPI_MakeFillet`. Fails (`Error::FilletFailed`) if
	/// the radius is too large for the local geometry, if tangent
	/// discontinuity prevents OCCT from building the fillet surface, or
	/// if an edge not belonging to `self` is passed.
	///
	/// Empty `edges` is a no-op and returns a clone of `self` — handy when
	/// a selector chain legitimately yields zero edges.
	fn fillet_edges<'a>(&self, radius: f64, edges: impl IntoIterator<Item = &'a Self::Edge>) -> Result<Self, Error> where Self::Edge: 'a;

	/// Chamfer (bevel) the given edges of `self` with a uniform distance.
	/// Edges are typically selected via `self.iter_edge().filter(...)`.
	///
	/// Wraps `BRepFilletAPI_MakeChamfer`. The chamfer plane is symmetric —
	/// the same `distance` is taken off along each of the two faces
	/// adjacent to the edge. Fails (`Error::ChamferFailed`) under the same
	/// conditions as `fillet_edges`.
	///
	/// Empty `edges` is a no-op and returns a clone of `self`.
	fn chamfer_edges<'a>(&self, distance: f64, edges: impl IntoIterator<Item = &'a Self::Edge>) -> Result<Self, Error> where Self::Edge: 'a;

	// --- Sweep ---
	/// Sweep a closed profile wire (= ordered edge list) along a spine wire
	/// to create a solid. Both inputs are accepted as `IntoIterator` of edge
	/// references so a single `&Edge` (via `std::slice::from_ref`) and a
	/// `&Vec<Edge>` work uniformly.
	///
	/// The profile must be closed; otherwise the underlying pipe operation
	/// produces a shell rather than a solid and an error is returned.
	///
	/// `orient` selects how the profile is oriented along the spine. See
	/// [`ProfileOrient`] for the trade-offs between [`Fixed`](ProfileOrient::Fixed),
	/// [`Torsion`](ProfileOrient::Torsion), and [`Up`](ProfileOrient::Up).
	// 戻り型は単一 `Self` 固定。MakePipeShell が compound を返すことは closed
	// face 入力に対しては実質起きないため、`Vec<Self>` に拡張する手間を省いた。
	// 想定外ケースに当たったら `Solid::new` の debug_assert で気付ける。
	fn sweep<'a, 'b, 'c>(profile: impl IntoIterator<Item = &'a Self::Edge>, spine: impl IntoIterator<Item = &'b Self::Edge>, orient: ProfileOrient<'c>) -> Result<Self, Error> where Self::Edge: 'a + 'b;

	/// Loft (skin) a solid through a sequence of cross-section wires.
	///
	/// Each `section` is an ordered list of edges forming a closed wire (a
	/// "rib"); ≥2 sections are required. The lofter builds a surface through
	/// all sections in order, then caps the first/last sections with planar
	/// faces to form a closed `Solid` (the standard "trunk" / "frustum" shape).
	///
	/// **`ruled` でどちらを選ぶか:**
	///
	/// | `ruled` | surface | 適 |
	/// |---|---|---|
	/// | `false` | 全 section を **正確に通る** C² B-spline 補間面 | 翼 (NACA 断面列 → 正確な NURBS 翼)、船体、滑らかな遷移形状 |
	/// | `true`  | 隣接 section 間を直線で結ぶ ruled パネル (section 間 C⁰) | 板金・テーパー柱・展開可能面、断面間の膨らみを許さない形状 |
	///
	/// Either way the surface contains every section wire exactly — `ruled`
	/// only changes how the surface behaves *between* sections (interpolating
	/// spline vs straight lines).
	///
	/// Internally uses `BRepOffsetAPI_ThruSections(isSolid=true, isRuled=ruled)`.
	fn loft<'a, S, I>(sections: S, ruled: bool) -> Result<Self, Error> where S: IntoIterator<Item = I>, I: IntoIterator<Item = &'a Self::Edge>, Self::Edge: 'a;

	/// Sew (stitch) faces into a closed solid.
	///
	/// `faces` are face handles — typically harvested from existing solids
	/// via `iter_face()`, or read from a surface-only exchange file — whose
	/// boundary edges coincide pairwise within `tolerance`. Wraps OCCT's
	/// `BRepBuilderAPI_Sewing`; the sewn shell must be **exactly one closed
	/// shell**, which is then upgraded to a `Solid` (orientation is fixed
	/// automatically so the enclosed volume is positive regardless of the
	/// input faces' orientations).
	///
	/// Fails with [`Error::SewFailed`] when the faces leave gaps wider than
	/// `tolerance`, overlap, form multiple disconnected shells, or include
	/// stray faces that belong to no closed shell.
	fn sew<'a>(faces: impl IntoIterator<Item = &'a Self::Face>, tolerance: f64) -> Result<Self, Error> where Self::Face: 'a;

	/// Offset every face of this solid by a constant signed distance,
	/// producing a new solid whose boundary is parallel to the original
	/// surface. `offset > 0` grows outward, `offset < 0` shrinks inward —
	/// the mold/tooling primitive (cavity = `part.offset_surface(t, tol)`).
	///
	/// Wraps `BRepOffsetAPI_MakeOffsetShape` (`PerformByJoin`, mode=Skin,
	/// join=Arc): convex edges/corners are blended with cylindrical/spherical
	/// arcs of radius `offset`; concave ones are extended and intersected.
	/// `tolerance` is the offset algorithm's working precision (`1e-6` is a
	/// reasonable default at millimeter scale).
	///
	/// **Known failure mode — thin features**: an inward offset whose
	/// magnitude reaches half the local wall thickness makes opposing offset
	/// faces cross, and an outward offset pinches shut inside narrow concave
	/// slots ≤ `2·offset` wide. OCCT rejects the self-intersecting result and
	/// this method returns [`Error::OffsetFailed`] — reduce `offset` or
	/// remove/split the thin feature first.
	fn offset_surface(&self, offset: f64, tolerance: f64) -> Result<Self, Error>;

	/// Build a B-spline surface solid from a 2D control-point grid.
	///
	/// `grid[i][j]` — index `i` (0..M) runs along the longitudinal (U) direction,
	/// index `j` (0..N) runs along the cross-section (V) direction. V is always
	/// periodic (the cross-section is a closed loop); U is periodic iff
	/// `periodic=true`, producing a torus. When `periodic=false` the U-ends are
	/// capped with planar faces, producing a pipe.
	///
	/// Internally builds a `Geom_BSplineSurface` via tensor-product periodic
	/// curve interpolation: per-V-column then per-U-row `GeomAPI_Interpolate`
	/// with explicit uniform parameters. Yields C^(degree-1) continuity at
	/// both seams. The closure `point(i, j)` is called for `i ∈ 0..u`,
	/// `j ∈ 0..v` to produce the M×N control grid, where `u` is the
	/// toroidal direction (closed iff `u_periodic`) and `v` is the
	/// cross-section direction (always closed).
	fn bspline(u: usize, v: usize, u_periodic: bool, point: impl Fn(usize, usize) -> DVec3) -> Result<Self, Error>;

	// --- Boolean primitive (FFI への唯一の通路) ---
	// Per-result-Solid face derivation history is attached to each Solid via
	// `Solid::iter_history()`; no separate metadata channel.
	//
	// ユーザーは `Boolean<S>` (= Solid に対する `+`/`-`/`*` 演算子で構築) を
	// `.build()` / `.build_vec()` に渡す経路を使う。下記 2 メソッドが Boolean<S>
	// と FFI を繋ぐ通路:
	//
	// - `boolean(solids, clauses)` — `&Self` を shallow copy しつつ Boolean を構築。
	//   バックエンドは TShape identity を保つ複製 (OCCT なら clone_shape_handle) を行う。
	//   common::boolean 内のすべての solid 複製はこの経路を通る。
	// - `boolean_build(&Boolean)` — DIMACS-flat DNF の `clauses` を FFI に渡して評価。
	fn boolean<'a>(solids: impl IntoIterator<Item = &'a Self>, clauses: impl IntoIterator<Item = i64>) -> Boolean<Self> where Self: 'a;
	fn boolean_build(b: &Boolean<Self>) -> Result<Vec<Self>, Error>;

	// --- I/O ---
	// Co-located with constructors: STEP / BRep readers return `Vec<Self>` (a
	// build path symmetrical with `Solid::cube` etc.), writers/`mesh` consume
	// solids. Putting them on Solid concentrates the type's surface and keeps
	// the crate root free of generic names like `mesh` / `write_step`.
	fn read_step<R: std::io::Read>(reader: &mut R) -> Result<Vec<Self>, Error>;
	fn read_brep_binary<R: std::io::Read>(reader: &mut R) -> Result<Vec<Self>, Error>;
	fn read_brep_text<R: std::io::Read>(reader: &mut R) -> Result<Vec<Self>, Error>;
	fn write_step<'a, W: std::io::Write>(solids: impl IntoIterator<Item = &'a Self>, writer: &mut W) -> Result<(), Error> where Self: 'a;
	fn write_brep_binary<'a, W: std::io::Write>(solids: impl IntoIterator<Item = &'a Self>, writer: &mut W) -> Result<(), Error> where Self: 'a;
	fn write_brep_text<'a, W: std::io::Write>(solids: impl IntoIterator<Item = &'a Self>, writer: &mut W) -> Result<(), Error> where Self: 'a;
	fn mesh<'a>(solids: impl IntoIterator<Item = &'a Self>, options: Tessellation) -> Result<Mesh, Error> where Self: 'a;

	/// Write a fixed 4-view multiview PNG (1024×1024) of this solid to `writer`.
	///
	/// 引数なしの「LLM 用固定プロトコル」: ISO + 軸 cyclic 順 (+X / +Y / +Z) の 4 視点を
	/// 同一スケールで並べ、各パネルに world-axis gnomon、中央境界線上に単位なし
	/// scale bar を 2 本埋め込む。視点・解像度・shading/hidden line 設定はすべて内部で
	/// 固定。tolerance は `bbox.diag * 0.001` を内部算出。寸法線が要らない設計確認に向く。
	///
	/// 微調整したい場合は `Solid::mesh(&[self], tol)?.scene(view, up, ...).write_png(...)`
	/// を直接呼ぶこと。この関数はあえて拡張点を持たない。
	#[cfg(feature = "png")]
	fn write_multiview_png<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
		let bbox = <Self as SolidStruct>::bounding_box(self);
		let diag = (bbox[1] - bbox[0]).length();
		let tol = if diag > 0.0 { diag * 0.001 } else { 0.001 };
		let mesh = Self::mesh(std::iter::once(self), Tessellation { deflection_linear: tol, relative_linear: false, ..Default::default() })?;
		mesh.write_multiview_png(writer)
	}
}

// Collections of solids/edges (`Vec<T>`, `[T; N]`, slices, iterators) are NOT
// special-cased with a dedicated trait. Transform them with iterator idioms
// (`vec.into_iter().map(|s| s.translate(v)).collect::<Vec<_>>()`), and aggregate
// queries the same way (`solids.iter().map(|s| s.volume()).sum::<f64>()`,
// `a.inertia() + b.inertia()`). sweep / loft / extrude / boolean / I/O accept
// any `impl IntoIterator<Item = &Edge>` / `&Solid` directly.

