import {BufferGeometry, BufferAttribute, Matrix4, MathUtils} from 'three';
import type {maths, geometries} from '@jscad/modeling';
// import {PolyDictionary} from '../../../../types/GlobalTypes';
import {ObjectType} from '../../../Constant';
import {BaseSopOperation} from '../../../../../engine/operations/sop/_Base';
import {csgMaterialMesh} from '../CsgConstant';
import {CSGTesselationParams} from '../CsgCommon';
import {toCreasedNormals} from 'three/examples/jsm/utils/BufferGeometryUtils';

// interface Geom3ToObject3DOptions {
// 	facet?: {
// 		angle: number;
// 	};
// }
interface jscadVertexWithIndex extends maths.vec3.Vec3 {
	// positionAsString: string;
	index: number;
}
// interface Normal {
// 	index: number;
// 	normal: Vector3;
// }

// interface NormalAttributes {
// 	normals: Normal[];
// }

export function geom3ToObject3D(csg: geometries.geom3.Geom3, options: CSGTesselationParams) {
	const geometry = geom3ToBufferGeometry(csg, options);
	return BaseSopOperation.createObject(
		geometry,
		ObjectType.MESH,
		csgMaterialMesh(options.meshesColor, options.wireframe)
	);
}

export function geom3ToBufferGeometry(csg: geometries.geom3.Geom3, options: CSGTesselationParams) {
	const positions: number[] = [];
	// const colors: number[] = [];
	const indices: number[] = [];
	const polygons = csg.polygons;
	let currentIndex = 0;
	// const color = csg.color;
	const indexByPosition: Map<string, number> = new Map();
	for (const polygon of polygons) {
		const polygonjsCount = polygon.vertices.length;
		const polygonVertices = polygon.vertices as jscadVertexWithIndex[];
		// console.log(polygonVertices.map((v) => v.index));
		for (const vertex of polygonVertices) {
			const positionAsString = `${vertex[0]},${vertex[1]},${vertex[2]}`;
			let index = indexByPosition.get(positionAsString);
			if (index == null) {
				index = currentIndex;
				indexByPosition.set(positionAsString, index);
				positions.push(vertex[0], vertex[1], vertex[2]);
				// if (color) {
				// 	colors.push(color[0], color[1], color[2]);
				// } else {
				// 	colors.push(1, 1, 1);
				// }
				currentIndex++;
			}
			vertex.index = index;
		}
		const first = (polygonVertices[0] as jscadVertexWithIndex).index;
		for (let i = 2; i < polygonjsCount; i++) {
			const second = (polygon.vertices[i - 1] as jscadVertexWithIndex).index;
			const third = (polygon.vertices[i] as jscadVertexWithIndex).index;
			indices.push(first, second, third);
		}
	}
	// let idx = 0;
	// console.log(indexByPosition);
	// for (let polygon of polygons) {
	// 	const polygonVertices = polygon.vertices as jscadVertexWithIndex[];
	// 	// console.log(polygonVertices.map((v) => v.index));
	// 	for (let vertex of polygonVertices) {
	// 		const str = `${vertex[0]},${vertex[1]},${vertex[2]}`;
	// 		if (!indexByPosition.get(str)) {
	// 			indexByPosition.set(str, newIndex++);
	// 		}
	// 		vertex.index = idx;
	// 		vertices.push(vertex[0], vertex[1], vertex[2]);
	// 		if (polygon.color) {
	// 			colors.push(polygon.color[0], polygon.color[1], polygon.color[2]);
	// 		} else {
	// 			colors.push(1, 1, 1);
	// 		}
	// 		idx++;
	// 	}
	// 	const first = (polygonVertices[0] as jscadVertexWithIndex).index;
	// 	for (let i = 2; i < polygon.vertices.length; i++) {
	// 		const second = (polygon.vertices[i - 1] as jscadVertexWithIndex).index;
	// 		const third = (polygon.vertices[i] as jscadVertexWithIndex).index;
	// 		indices.push(first, second, third);
	// 	}
	// }

	const geo = new BufferGeometry();
	geo.setAttribute('position', new BufferAttribute(new Float32Array(positions), 3));
	// geo.setAttribute('color', new BufferAttribute(new Float32Array(colors), 3));
	geo.setIndex(indices);
	if (csg.transforms) {
		const transforms = new Matrix4();
		transforms.set(...csg.transforms).transpose();
		geo.applyMatrix4(transforms);
	}
	geo.computeVertexNormals();

	// if (options && options.facetAngle!=null) {
	return toCreasedNormals(geo, MathUtils.degToRad(options.facetAngle));
	// } else {
	// return geo;
	// }

	// const positions: PolyDictionary<NormalAttributes> = {};
	// for (let i = 0; i < geo.attributes.position.count; i++) {
	// 	const pArray = geo.attributes.position.array;
	// 	const x = Math.round(pArray[i * 3] * 100);
	// 	const y = Math.round(pArray[i * 3 + 1] * 100);
	// 	const z = Math.round(pArray[i * 3 + 2] * 100);
	// 	const position = `${x},${y},${z}`;
	// 	if (!positions[position]) {
	// 		positions[position] = {normals: []};
	// 	}
	// 	const nArray = geo.attributes.normal.array;
	// 	const nx = nArray[i * 3];
	// 	const ny = nArray[i * 3 + 1];
	// 	const nz = nArray[i * 3 + 2];
	// 	const normal = new Vector3(nx, ny, nz);
	// 	positions[position].normals.push({index: i, normal: normal});
	// }

	// const toAverage: Map<number, Vector3> = new Map();
	// const toAverageIndices: number[] = [];
	// for (let p in positions) {
	// 	const currentPosition = positions[p];
	// 	const nl = currentPosition.normals.length;
	// 	for (let i = 0; i < nl - 1; i += 1) {
	// 		for (let j = i + 1; j < nl; j += 1) {
	// 			const n1 = currentPosition.normals[i].normal;
	// 			const n2 = currentPosition.normals[j].normal;
	// 			if (n1.angleTo(n2) < Math.PI * 0.5 && n1.angleTo(n2) !== 0) {
	// 				const i0 = currentPosition.normals[i].index;
	// 				const i1 = currentPosition.normals[j].index;
	// 				toAverage.set(i0, currentPosition.normals[i].normal);
	// 				toAverage.set(i1, currentPosition.normals[j].normal);
	// 				toAverageIndices.push(i0, i1);
	// 			}
	// 		}
	// 	}
	// 	const averageNormal = new Vector3();
	// 	for (let index of toAverageIndices) {
	// 		const normal = toAverage.get(index);
	// 		if (normal) {
	// 			averageNormal.add(normal);
	// 			averageNormal.normalize();
	// 		}
	// 	}
	// 	for (let index of toAverageIndices) {
	// 		(geo.attributes.normal.array as number[])[index * 3] = averageNormal.x;
	// 		(geo.attributes.normal.array as number[])[index * 3 + 1] = averageNormal.y;
	// 		(geo.attributes.normal.array as number[])[index * 3 + 2] = averageNormal.z;
	// 	}

	// 	toAverage.clear();
	// 	toAverageIndices.splice(0, toAverageIndices.length);
	// }

	// geo.attributes.normal.needsUpdate = true;

	// matrix.elements = csg.transforms;
	// geo.applyMatrix4(matrix);

	// return geo;
}

// export function geom3Positions(csg: geometries.geom3.Geom3): Vector3[] {
// 	const bufferGeometry = geom3ToBufferGeometry(csg);
// 	const positionAttribute = bufferGeometry.getAttribute('position') as BufferAttribute;
// 	const positionsArray = positionAttribute.array;
// 	const pointsCount = positionAttribute.itemSize;
// 	const vectors: Vector3[] = new Array(pointsCount);
// 	for (let i = 0; i < pointsCount; i++) {
// 		const vec = new Vector3().fromArray(positionsArray, i * 3);
// 		vectors[i] = vec;
// 		i++;
// 	}
// 	return vectors;
// }
