import { convertToScaledTime, roundToMilliseconds } from "./shared/PrecisionUtils.js";

//#region src/elements/EFMedia/SegmentIndex.ts
function createFragmentIndex(data, src) {
	const durationMs = Object.values(data).reduce((max, fragment) => Math.max(max, fragment.duration / fragment.timescale), 0) * 1e3;
	const audioTrack = Object.values(data).find((t) => t.type === "audio");
	const videoTrack = Object.values(data).find((t) => t.type === "video" && t.track !== void 0 && t.track > 0);
	const scrubTrack = data[-1];
	const tracks = {};
	if (videoTrack && videoTrack.track !== void 0) tracks.video = {
		role: "video",
		id: videoTrack.track,
		src,
		startTimeOffsetMs: videoTrack.startTimeOffsetMs
	};
	if (audioTrack && audioTrack.track !== void 0) tracks.audio = {
		role: "audio",
		id: audioTrack.track,
		src
	};
	if (scrubTrack && scrubTrack.track !== void 0) {
		const segmentDurationsMs = scrubTrack.segments.length > 0 ? scrubTrack.segments.map((s) => s.duration / scrubTrack.timescale * 1e3) : void 0;
		tracks.scrub = {
			role: "scrub",
			id: scrubTrack.track,
			src,
			segmentDurationMs: 3e4,
			segmentDurationsMs,
			startTimeOffsetMs: scrubTrack.startTimeOffsetMs
		};
	}
	return {
		durationMs,
		tracks,
		segmentAt(timeMs, track) {
			const trackId = typeof track.id === "number" ? track.id : Number.parseInt(track.id, 10);
			const trackData = data[trackId];
			if (!trackData) throw new Error(`Track ${trackId} not found`);
			const { timescale, segments } = trackData;
			const scaledSeekTime = convertToScaledTime(roundToMilliseconds(timeMs + (track.startTimeOffsetMs || 0)), timescale);
			for (let i = segments.length - 1; i >= 0; i--) {
				const segment = segments[i];
				const segmentEndTime = segment.cts + segment.duration;
				if (segment.cts <= scaledSeekTime && scaledSeekTime < segmentEndTime) return i;
			}
			let nearestSegmentIndex = 0;
			let nearestDistance = Number.MAX_SAFE_INTEGER;
			for (let i = 0; i < segments.length; i++) {
				const segment = segments[i];
				const segmentEndTime = segment.cts + segment.duration;
				let distance;
				if (scaledSeekTime < segment.cts) distance = segment.cts - scaledSeekTime;
				else if (scaledSeekTime >= segmentEndTime) distance = scaledSeekTime - segmentEndTime;
				else return i;
				if (distance < nearestDistance) {
					nearestDistance = distance;
					nearestSegmentIndex = i;
				}
			}
			return nearestSegmentIndex;
		},
		segmentsInRange(fromMs, toMs, track) {
			if (fromMs >= toMs) return [];
			const trackData = data[typeof track.id === "number" ? track.id : Number.parseInt(track.id, 10)];
			if (!trackData) return [];
			const { timescale, segments } = trackData;
			const ranges = [];
			for (let i = 0; i < segments.length; i++) {
				const segment = segments[i];
				const segmentStartMs = segment.cts / timescale * 1e3;
				const segmentEndMs = (segment.cts + segment.duration) / timescale * 1e3;
				if (segmentStartMs < toMs && segmentEndMs > fromMs) ranges.push({
					segmentId: i,
					startMs: segmentStartMs,
					endMs: segmentEndMs
				});
			}
			return ranges;
		}
	};
}
function createManifestIndex(manifest) {
	const durationMs = manifest.durationMs;
	const tracks = {};
	if (manifest.videoRenditions && manifest.videoRenditions.length > 0) {
		const r = manifest.videoRenditions[0];
		tracks.video = {
			role: "video",
			id: r.id,
			src: manifest.sourceUrl,
			segmentDurationMs: r.segmentDurationMs,
			segmentDurationsMs: r.segmentDurationsMs,
			startTimeOffsetMs: r.startTimeOffsetMs
		};
		const scrubRendition = manifest.videoRenditions.find((v) => v.id === "scrub");
		if (scrubRendition) tracks.scrub = {
			role: "scrub",
			id: scrubRendition.id,
			src: manifest.sourceUrl,
			segmentDurationMs: scrubRendition.segmentDurationMs,
			segmentDurationsMs: scrubRendition.segmentDurationsMs,
			startTimeOffsetMs: scrubRendition.startTimeOffsetMs
		};
	}
	if (manifest.audioRenditions && manifest.audioRenditions.length > 0) {
		const r = manifest.audioRenditions[0];
		tracks.audio = {
			role: "audio",
			id: r.id,
			src: manifest.sourceUrl,
			segmentDurationMs: r.segmentDurationMs,
			segmentDurationsMs: r.segmentDurationsMs,
			startTimeOffsetMs: r.startTimeOffsetMs
		};
	}
	function computeSegmentIdForTrack(desiredSeekTimeMs, track) {
		if (desiredSeekTimeMs > durationMs) return;
		if (track.segmentDurationsMs && track.segmentDurationsMs.length > 0) {
			let cumulativeTime = 0;
			for (let i = 0; i < track.segmentDurationsMs.length; i++) {
				const segmentDuration = track.segmentDurationsMs[i];
				if (segmentDuration === void 0) throw new Error("Segment duration is required for JIT metadata");
				const segmentStartMs = cumulativeTime;
				const segmentEndMs = cumulativeTime + segmentDuration;
				const includesEndTime = i === track.segmentDurationsMs.length - 1 && desiredSeekTimeMs === durationMs;
				if (desiredSeekTimeMs >= segmentStartMs && (desiredSeekTimeMs < segmentEndMs || includesEndTime)) return i + 1;
				cumulativeTime += segmentDuration;
				if (cumulativeTime >= durationMs) break;
			}
			return;
		}
		if (!track.segmentDurationMs) throw new Error("Segment duration is required for JIT metadata");
		const segmentIndex = Math.floor(desiredSeekTimeMs / track.segmentDurationMs);
		if (segmentIndex * track.segmentDurationMs >= durationMs) return;
		return segmentIndex + 1;
	}
	return {
		durationMs,
		tracks,
		segmentAt(timeMs, track) {
			return computeSegmentIdForTrack(timeMs, track);
		},
		segmentsInRange(fromMs, toMs, track) {
			if (fromMs >= toMs) return [];
			const segments = [];
			if (track.segmentDurationsMs && track.segmentDurationsMs.length > 0) {
				let cumulativeTime = 0;
				for (let i = 0; i < track.segmentDurationsMs.length; i++) {
					const segmentDuration = track.segmentDurationsMs[i];
					if (segmentDuration === void 0) continue;
					const segmentStartMs = cumulativeTime;
					const segmentEndMs = Math.min(cumulativeTime + segmentDuration, durationMs);
					if (segmentStartMs >= durationMs) break;
					if (segmentStartMs < toMs && segmentEndMs > fromMs) segments.push({
						segmentId: i + 1,
						startMs: segmentStartMs,
						endMs: segmentEndMs
					});
					cumulativeTime += segmentDuration;
					if (cumulativeTime >= durationMs) break;
				}
				return segments;
			}
			const segmentDurationMs = track.segmentDurationMs || 1e3;
			const startSegmentIndex = Math.floor(fromMs / segmentDurationMs);
			const endSegmentIndex = Math.floor(toMs / segmentDurationMs);
			for (let i = startSegmentIndex; i <= endSegmentIndex; i++) {
				const segmentId = i + 1;
				const segmentStartMs = i * segmentDurationMs;
				const segmentEndMs = Math.min((i + 1) * segmentDurationMs, durationMs);
				if (segmentStartMs >= durationMs) break;
				if (segmentStartMs < toMs && segmentEndMs > fromMs) segments.push({
					segmentId,
					startMs: segmentStartMs,
					endMs: segmentEndMs
				});
			}
			return segments;
		}
	};
}

//#endregion
export { createFragmentIndex, createManifestIndex };
//# sourceMappingURL=SegmentIndex.js.map