import {Geom2d_Curve, CADTesselationParams, CadGeometryType, CadGC} from '../CadCommon';
import {BufferGeometry, Float32BufferAttribute, Vector2, MathUtils} from 'three';
import {BaseSopOperation} from '../../../../../engine/operations/sop/_Base';
import {cadMaterialLine} from '../CadConstant';
import {CadLoaderSync} from '../CadLoaderSync';
// import {withCadException} from '../CadExceptionHandler';
import {ObjectType} from '../../../Constant';
import {CadObject} from '../CadObject';
import {objectContentCopyProperties} from '../../../ObjectContent';
import {Color} from 'three';

export const CURVE_2D_TESSELATION_PARAMS: CADTesselationParams = {
	linearTolerance: 0.1,
	angularTolerance: 0.1,
	curveAbscissa: 0.1,
	curveTolerance: 0.1,
	wireframe: false,
	displayMeshes: false,
	displayEdges: true,
	meshesColor: new Color(),
	edgesColor: new Color(),
};

const STRIDE = 3;
export function cadGeom2dCurveToObject3D(
	cadObject: CadObject<CadGeometryType.CURVE_2D>,
	tesselationParams: CADTesselationParams
) {
	const geometry = cadGeom2dCurveToBufferGeometry(cadObject, tesselationParams);
	const object = BaseSopOperation.createObject(
		geometry,
		ObjectType.LINE_SEGMENTS,
		cadMaterialLine(tesselationParams.edgesColor)
	);
	objectContentCopyProperties(cadObject, object);
	return object;
}
export function cadGeom2dCurveToBufferGeometry(
	cadObject: CadObject<CadGeometryType.CURVE_2D>,
	tesselationParams: CADTesselationParams
) {
	const oc = CadLoaderSync.oc();

	return CadGC.withGC((r) => {
		const curve = cadObject.cadGeometry();
		const curveHandle = new oc.Handle_Geom2d_Curve_2(curve);
		const geom2Dadaptor = r(new oc.Geom2dAdaptor_Curve_2(curveHandle));

		const uniformAbscissa = r(
			new oc.GCPnts_UniformAbscissa_6(
				geom2Dadaptor,
				tesselationParams.curveAbscissa,
				tesselationParams.curveTolerance
			)
		);

		let positions: number[] | undefined;
		let indices: number[] | undefined;
		const point = CadLoaderSync.gp_Pnt2d;

		if (uniformAbscissa.IsDone()) {
			const pointsCount = uniformAbscissa.NbPoints();

			positions = new Array(pointsCount * 3).fill(0);
			indices = new Array(pointsCount);

			for (let i = 0; i < pointsCount; i++) {
				curve.D0(uniformAbscissa.Parameter(i + 1), point);
				const index = i * STRIDE;
				positions[index] = point.X();
				positions[index + 1] = point.Y();
				if (i > 0) {
					indices[(i - 1) * 2] = i - 1;
					indices[(i - 1) * 2 + 1] = i;
				}
			}
		}

		const geometry = new BufferGeometry();
		geometry.setAttribute('position', new Float32BufferAttribute(positions || [], 3));
		geometry.setIndex(indices || []);
		return geometry;
	});
}

// let _t: gp_Vec2d | undefined;
// let _pivot: gp_Pnt2d | undefined;
export function cadGeom2dCurveTransform(curve: Geom2d_Curve, t: Vector2, r: number, s: number, p: Vector2) {
	const _t = CadLoaderSync.gp_Vec2d;
	const _pivot = CadLoaderSync.gp_Pnt2d;
	_t.SetCoord_2(t.x, t.y);
	_pivot.SetCoord_2(p.x, p.y);
	curve.Translate_1(_t);
	curve.Rotate(_pivot, MathUtils.degToRad(r));
	curve.Scale(_pivot, s);
}
export function cadGeom2dCurveTranslate(curve: Geom2d_Curve, t: Vector2) {
	const _t = CadLoaderSync.gp_Vec2d;
	_t.SetCoord_2(t.x, t.y);
	curve.Translate_1(_t);
}

export function cadGeom2dCurveClone(src: Geom2d_Curve): Geom2d_Curve {
	// Not great, not terrible
	return src.Reversed().get().Reversed().get();
}
