use super::stream::{rust_reader_read, rust_writer_write};
use super::stream::{RustReader, RustWriter};

#[cxx::bridge(namespace = "cadrum")]
mod ffi_bridge {
	// Shared struct for mesh data returned from C++
	struct MeshData {
		vertices: Vec<f64>, // flat xyz
		uvs: Vec<f64>,      // flat uv
		normals: Vec<f64>,  // flat xyz
		indices: Vec<u32>,
		face_tshape_ids: Vec<u64>, // per-triangle TShape* address
		success: bool,
	}

	// Expose Rust stream types to C++ for streambuf callbacks
	extern "Rust" {
		type RustReader;
		type RustWriter;

		fn rust_reader_read(reader: &mut RustReader, buf: &mut [u8]) -> usize;
		fn rust_writer_write(writer: &mut RustWriter, buf: &[u8]) -> usize;
	}

	unsafe extern "C++" {
		include!("cadrum/cpp/wrapper.h");

		// Opaque C++ types (accessed as cadrum::TopoDS_Shape etc. via using aliases)
		type TopoDS_Shape;
		type TopoDS_Face;
		type TopoDS_Edge;

		// ==================== Shape I/O (streambuf callback) ====================

		// Plain STEP I/O — used only without `color` feature.
		// With color, STEP goes through XCAF (`read_step_color_stream` etc.).
		#[cfg(not(feature = "color"))]
		fn read_step_stream(reader: &mut RustReader) -> UniquePtr<TopoDS_Shape>;
		#[cfg(not(feature = "color"))]
		fn write_step_stream(shape: &TopoDS_Shape, writer: &mut RustWriter) -> bool;
		fn read_brep_bin_stream(reader: &mut RustReader) -> UniquePtr<TopoDS_Shape>;
		fn write_brep_bin_stream(shape: &TopoDS_Shape, writer: &mut RustWriter) -> bool;
		fn read_brep_text_stream(reader: &mut RustReader) -> UniquePtr<TopoDS_Shape>;
		fn write_brep_text_stream(shape: &TopoDS_Shape, writer: &mut RustWriter) -> bool;

		// ==================== Shape Constructors ====================

		fn make_half_space(ox: f64, oy: f64, oz: f64, nx: f64, ny: f64, nz: f64) -> UniquePtr<TopoDS_Shape>;

		fn make_box(x1: f64, y1: f64, z1: f64, x2: f64, y2: f64, z2: f64) -> UniquePtr<TopoDS_Shape>;

		fn make_cylinder(px: f64, py: f64, pz: f64, dx: f64, dy: f64, dz: f64, radius: f64, height: f64) -> UniquePtr<TopoDS_Shape>;

		fn make_sphere(cx: f64, cy: f64, cz: f64, radius: f64) -> UniquePtr<TopoDS_Shape>;

		fn make_cone(px: f64, py: f64, pz: f64, dx: f64, dy: f64, dz: f64, r1: f64, r2: f64, height: f64) -> UniquePtr<TopoDS_Shape>;

		fn make_torus(px: f64, py: f64, pz: f64, dx: f64, dy: f64, dz: f64, r1: f64, r2: f64) -> UniquePtr<TopoDS_Shape>;

		fn make_empty() -> UniquePtr<TopoDS_Shape>;

		fn deep_copy(shape: &TopoDS_Shape) -> UniquePtr<TopoDS_Shape>;

		// ==================== Colored STEP I/O (color feature only) ====================

		#[cfg(feature = "color")]
		fn read_step_color_stream(reader: &mut RustReader, out_ids: &mut Vec<u64>, out_rgb: &mut Vec<f32>) -> UniquePtr<TopoDS_Shape>;

		#[cfg(feature = "color")]
		fn write_step_color_stream(shape: &TopoDS_Shape, ids: &[u64], rgb: &[f32], writer: &mut RustWriter) -> bool;

		// ==================== Builders (solid → solid with history) ====================

		// Unified boolean op. `op_kind`: 0 = fuse(union), 1 = cut(a − b), 2 = common(intersect).
		// `out_history` is appended with flat [post_id, src_id, ...] pairs covering both inputs.
		fn builder_boolean(a: &TopoDS_Shape, b: &TopoDS_Shape, op_kind: u32, out_history: &mut Vec<u64>) -> UniquePtr<TopoDS_Shape>;

		// Unify shared faces. `out_history` receives flat [new_id, old_id, ...]
		// pairs (same layout as `builder_boolean`), used by Solid::clean to populate
		// `Solid::history` and remap the colormap when color is enabled.
		fn builder_clean(shape: &TopoDS_Shape, out_history: &mut Vec<u64>) -> UniquePtr<TopoDS_Shape>;

		// TODO: builder_thick_solid / builder_fillet / builder_chamfer should
		// also gain `out_history` populated via OCCT's Modified()/Generated()
		// — currently no history (Rust side stores Default::default()).
		fn builder_thick_solid(solid: &TopoDS_Shape, open_faces: &CxxVector<TopoDS_Face>, thickness: f64) -> UniquePtr<TopoDS_Shape>;
		fn builder_fillet(solid: &TopoDS_Shape, edges: &CxxVector<TopoDS_Edge>, radius: f64) -> UniquePtr<TopoDS_Shape>;
		fn builder_chamfer(solid: &TopoDS_Shape, edges: &CxxVector<TopoDS_Edge>, distance: f64) -> UniquePtr<TopoDS_Shape>;

		// ==================== Transforms (solid → solid, no history) ====================

		fn transform_translate(shape: &TopoDS_Shape, tx: f64, ty: f64, tz: f64) -> UniquePtr<TopoDS_Shape>;

		fn transform_rotate(shape: &TopoDS_Shape, ox: f64, oy: f64, oz: f64, dx: f64, dy: f64, dz: f64, angle: f64) -> UniquePtr<TopoDS_Shape>;

		fn transform_scale(shape: &TopoDS_Shape, cx: f64, cy: f64, cz: f64, factor: f64) -> UniquePtr<TopoDS_Shape>;

		fn transform_mirror(shape: &TopoDS_Shape, ox: f64, oy: f64, oz: f64, nx: f64, ny: f64, nz: f64) -> UniquePtr<TopoDS_Shape>;

		// ==================== Shape Queries ====================

		fn shape_is_null(shape: &TopoDS_Shape) -> bool;
		fn shape_is_solid(shape: &TopoDS_Shape) -> bool;
		fn shape_volume(shape: &TopoDS_Shape) -> f64;
		fn shape_surface_area(shape: &TopoDS_Shape) -> f64;
		fn shape_center_of_mass(shape: &TopoDS_Shape, x: &mut f64, y: &mut f64, z: &mut f64);
		fn shape_inertia_tensor(shape: &TopoDS_Shape, m00: &mut f64, m01: &mut f64, m02: &mut f64, m10: &mut f64, m11: &mut f64, m12: &mut f64, m20: &mut f64, m21: &mut f64, m22: &mut f64);
		fn shape_contains_point(shape: &TopoDS_Shape, x: f64, y: f64, z: f64) -> bool;
		fn shape_bounding_box(shape: &TopoDS_Shape, xmin: &mut f64, ymin: &mut f64, zmin: &mut f64, xmax: &mut f64, ymax: &mut f64, zmax: &mut f64);

		// ==================== Compound Decompose/Compose ====================

		fn decompose_into_solids(shape: &TopoDS_Shape) -> UniquePtr<CxxVector<TopoDS_Shape>>;
		fn compound_add(compound: Pin<&mut TopoDS_Shape>, child: &TopoDS_Shape);

		// ==================== Meshing ====================

		fn mesh_shape(shape: &TopoDS_Shape, tolerance: f64) -> MeshData;

		// ==================== Topology enumeration ====================

		fn shape_edges(shape: &TopoDS_Shape) -> UniquePtr<CxxVector<TopoDS_Edge>>;
		fn shape_faces(shape: &TopoDS_Shape) -> UniquePtr<CxxVector<TopoDS_Face>>;

		fn clone_shape_handle(shape: &TopoDS_Shape) -> UniquePtr<TopoDS_Shape>;
		fn clone_edge_handle(edge: &TopoDS_Edge) -> UniquePtr<TopoDS_Edge>;
		fn clone_face_handle(face: &TopoDS_Face) -> UniquePtr<TopoDS_Face>;

		// ==================== Face Methods ====================

		fn face_tshape_id(face: &TopoDS_Face) -> u64;
		fn shape_tshape_id(shape: &TopoDS_Shape) -> u64;

		fn face_project_point(face: &TopoDS_Face, px: f64, py: f64, pz: f64, cpx: &mut f64, cpy: &mut f64, cpz: &mut f64, nx: &mut f64, ny: &mut f64, nz: &mut f64) -> bool;

		// ==================== Edge Methods ====================

		fn edge_approximation_segments(edge: &TopoDS_Edge, angular: f64, chord: f64) -> Vec<f64>;

		fn make_helix_edge(ax: f64, ay: f64, az: f64, xrx: f64, xry: f64, xrz: f64, radius: f64, pitch: f64, height: f64) -> UniquePtr<TopoDS_Edge>;
		fn make_polygon_edges(coords: &[f64]) -> UniquePtr<CxxVector<TopoDS_Edge>>;
		fn make_circle_edge(ax: f64, ay: f64, az: f64, radius: f64) -> UniquePtr<TopoDS_Edge>;
		fn make_line_edge(ax: f64, ay: f64, az: f64, bx: f64, by: f64, bz: f64) -> UniquePtr<TopoDS_Edge>;
		fn make_arc_edge(sx: f64, sy: f64, sz: f64, mx: f64, my: f64, mz: f64, ex: f64, ey: f64, ez: f64) -> UniquePtr<TopoDS_Edge>;
		fn make_bspline_edge(coords: &[f64], end_kind: u32, sx: f64, sy: f64, sz: f64, ex: f64, ey: f64, ez: f64) -> UniquePtr<TopoDS_Edge>;

		fn edge_endpoints(edge: &TopoDS_Edge, sx: &mut f64, sy: &mut f64, sz: &mut f64, ex: &mut f64, ey: &mut f64, ez: &mut f64);
		fn edge_tangents(edge: &TopoDS_Edge, sx: &mut f64, sy: &mut f64, sz: &mut f64, ex: &mut f64, ey: &mut f64, ez: &mut f64);
		fn edge_is_closed(edge: &TopoDS_Edge) -> bool;
		fn edge_project_point(edge: &TopoDS_Edge, px: f64, py: f64, pz: f64, cpx: &mut f64, cpy: &mut f64, cpz: &mut f64, tx: &mut f64, ty: &mut f64, tz: &mut f64) -> bool;

		fn deep_copy_edge(edge: &TopoDS_Edge) -> UniquePtr<TopoDS_Edge>;

		fn translate_edge(edge: &TopoDS_Edge, tx: f64, ty: f64, tz: f64) -> UniquePtr<TopoDS_Edge>;
		fn rotate_edge(edge: &TopoDS_Edge, ox: f64, oy: f64, oz: f64, dx: f64, dy: f64, dz: f64, angle: f64) -> UniquePtr<TopoDS_Edge>;
		fn scale_edge(edge: &TopoDS_Edge, cx: f64, cy: f64, cz: f64, factor: f64) -> UniquePtr<TopoDS_Edge>;
		fn mirror_edge(edge: &TopoDS_Edge, ox: f64, oy: f64, oz: f64, nx: f64, ny: f64, nz: f64) -> UniquePtr<TopoDS_Edge>;

		fn make_extrude(profile_edges: &CxxVector<TopoDS_Edge>, dx: f64, dy: f64, dz: f64) -> UniquePtr<TopoDS_Shape>;
		fn make_pipe_shell(all_edges: &CxxVector<TopoDS_Edge>, spine_edges: &CxxVector<TopoDS_Edge>, orient: u32, ux: f64, uy: f64, uz: f64, aux_spine_edges: &CxxVector<TopoDS_Edge>) -> UniquePtr<TopoDS_Shape>;
		fn make_loft(all_edges: &CxxVector<TopoDS_Edge>) -> UniquePtr<TopoDS_Shape>;
		fn make_bspline_solid(coords: &[f64], nu: u32, nv: u32, u_periodic: bool) -> UniquePtr<TopoDS_Shape>;

		fn edge_vec_new() -> UniquePtr<CxxVector<TopoDS_Edge>>;
		fn edge_vec_push(v: Pin<&mut CxxVector<TopoDS_Edge>>, e: &TopoDS_Edge);
		fn edge_vec_push_null(v: Pin<&mut CxxVector<TopoDS_Edge>>);

		fn face_vec_new() -> UniquePtr<CxxVector<TopoDS_Face>>;
		fn face_vec_push(v: Pin<&mut CxxVector<TopoDS_Face>>, f: &TopoDS_Face);

	}
}

// Re-export all bridge items so other modules can use `ffi::TopoDS_Shape` etc.
pub use ffi_bridge::*;

// cxx opaque types default to `!Send + !Sync`. We mark them `Send` here so
// that `UniquePtr<TopoDS_Shape>` (and friends) become `Send`, which in turn
// makes our wrapper types (`Shape`, `Solid`, `Face`, `Edge`) auto-Send.
//
// Safety rationale:
//   - `UniquePtr` gives exclusive ownership — no aliasing is possible.
//   - These values are never shared across threads simultaneously; they are
//     only *moved* to another thread, which is what `Send` permits.
//   - `Sync` is intentionally NOT implemented: OCC's `Handle<Geom_XXX>`
//     reference counts are non-atomic, so concurrent `&T` access across
//     threads would be unsound.
unsafe impl Send for TopoDS_Shape {}
unsafe impl Send for TopoDS_Face {}
unsafe impl Send for TopoDS_Edge {}
