import {BaseCorePoint, CorePoint} from '../entities/point/CorePoint';
import {Float32BufferAttribute, Vector2, BufferGeometry, Mesh, TypedArray} from 'three';
import {PolyDictionary} from '../../../types/GlobalTypes';
import {CoreObjectType, ObjectContent, isObject3D} from '../ObjectContent';
import {pointsFromObject, pointAttributeNames, pointAttributeSizes} from '../entities/point/CorePointUtils';

const _points:CorePoint<CoreObjectType>[]=[]
export class CoreGeometryUtilCurve {
	static accumulatedCurvePointIndices(indices: TypedArray) {
		let curve_point_indices: number[] = [];
		const accumulated_curve_point_indices = [];
		let last_index_added: number | null = null;

		// indices.forEach((index, i) => {
		let index: number;
		for (let i = 0; i < indices.length; i++) {
			if (i % 2 === 1) {
				index = indices[i];
				const previous_index = indices[i - 1];

				// if the last added index, from the previous segment
				// is the same as the start of the current segment (indices[i-1])
				// then this is part of the same curve
				if (last_index_added == null || previous_index === last_index_added) {
					// add the first point
					if (curve_point_indices.length === 0) {
						curve_point_indices.push(previous_index);
					}

					curve_point_indices.push(index);
					last_index_added = index;
				} else {
					// otherwise we create a new curve
					accumulated_curve_point_indices.push(curve_point_indices);

					// and reset the array
					curve_point_indices = [previous_index, index];
					last_index_added = index;
				}
			}
		}

		// also create with the remaining ones
		accumulated_curve_point_indices.push(curve_point_indices);

		return accumulated_curve_point_indices;
	}

	static create_line_segment_geometry(
		points: BaseCorePoint[],
		indices: number[],
		attrib_names: string[],
		attrib_sizes_by_name: PolyDictionary<number>
	) {
		// let new_positions: number[] = []
		const new_indices: number[] = [];

		const new_attribute_values_by_name: PolyDictionary<number[]> = {};
		attrib_names.forEach((attrib_name) => {
			new_attribute_values_by_name[attrib_name] = [];
		});

		indices.forEach((index, i) => {
			const point = points[index];
			// const position = point.position();
			// new_positions.push(position.toArray());
			attrib_names.forEach((attrib_name) => {
				const attrib_value = point.attribValue(attrib_name);
				const attrib_size = attrib_sizes_by_name[attrib_name];
				let attrib_value_f: number[];
				if (attrib_size > 1) {
					attrib_value_f = (attrib_value as Vector2).toArray();
				} else {
					attrib_value_f = [attrib_value as number];
				}
				attrib_value_f.forEach((v: number) => {
					new_attribute_values_by_name[attrib_name].push(v);
				});
			});

			if (i > 0) {
				new_indices.push(i - 1);
				new_indices.push(i);
			}
		});

		const geometry = new BufferGeometry();

		attrib_names.forEach((attrib_name) => {
			const attrib_size = attrib_sizes_by_name[attrib_name];
			const values = new_attribute_values_by_name[attrib_name];
			geometry.setAttribute(attrib_name, new Float32BufferAttribute(values, attrib_size));
		});

		geometry.setIndex(new_indices);
		return geometry;
	}

	static line_segment_to_geometries<T extends CoreObjectType>(object: ObjectContent<T>) {
		const geometries: BufferGeometry[] = [];
		const attribNames = pointAttributeNames(object);
		pointsFromObject(object,_points);

		if (!isObject3D(object)) {
			return;
		}
		const geometry = (object as Mesh).geometry;
		if (!geometry) {
			return;
		}

		const indices = (geometry.getIndex()?.array ) || new Uint32Array(0);

		const accumulated_curve_point_indices = this.accumulatedCurvePointIndices(indices);

		if (accumulated_curve_point_indices.length > 0) {
			const attributeSizesByName = pointAttributeSizes(object);

			accumulated_curve_point_indices.forEach((curvePointIndices, i) => {
				const newGeometry = this.create_line_segment_geometry(
					_points,
					curvePointIndices,
					attribNames,
					attributeSizesByName
				);
				geometries.push(newGeometry);
			});
		}

		return geometries;
	}
}
