#pragma once

#include "../Eigen/Dense"
#include <cmath>

namespace OpticsFEM {

// SBC face normal for 3D first-order Nedelec scatter (FemType=4).
// Matches MATLAB assembly_inc.m / assembly_out.m:
//   inc  -> [0,0,1]
//   out, domain==3 -> [0,0,-1]
//   other out -> normalize([sum(x)/3, sum(y)/3, 0]) from tet vertices
// Fallback when the reference formula is degenerate:
//   1) mesh NormOfFace(domain) if non-zero
//   2) geometric normal from the boundary triangle
inline Eigen::Vector3d computeScatterSBCNormal(
	bool isInc,
	int boundaryDomain,
	const double tetX[4],
	const double tetY[4],
	const Eigen::Vector3d& faceP0,
	const Eigen::Vector3d& faceP1,
	const Eigen::Vector3d& faceP2,
	const Eigen::Vector3d* meshNormFallback = nullptr)
{
	using Eigen::Vector3d;

	if (isInc)
		return Vector3d(0.0, 0.0, 1.0);

	// Prefer mesh NormOfFace (matches MATLAB assembly_out / assembly_inc).
	if (meshNormFallback != nullptr && meshNormFallback->norm() >= 1e-30)
		return (*meshNormFallback) / meshNormFallback->norm();

	if (boundaryDomain == 3)
		return Vector3d(0.0, 0.0, -1.0);

	Vector3d n(
		(tetX[0] + tetX[1] + tetX[2] + tetX[3]) / 3.0,
		(tetY[0] + tetY[1] + tetY[2] + tetY[3]) / 3.0,
		0.0);
	const double len = n.norm();
	if (len >= 1e-30)
		return n / len;

	const Vector3d geo = (faceP1 - faceP0).cross(faceP2 - faceP0);
	if (geo.norm() >= 1e-30)
		return geo.normalized();

	return Vector3d(0.0, 0.0, -1.0);
}

} // namespace OpticsFEM
