import type { EntityObject } from '@ChronoManager/models/entity' import type { CalendarOccurrence, CalendarOccurrenceIndex, } from '../types/occurrence' import type { VisibleDateRange } from '../types' import { shiftLocalDays, startOfLocalDay } from './dateRanges' import { expandEntityOccurrences } from './recurrence' function appendToDayBuckets(index: CalendarOccurrenceIndex, occurrence: CalendarOccurrence) { const lastOccupiedDayMs = startOfLocalDay(new Date(occurrence.endMs - 1)).getTime() let day = new Date(occurrence.dayStartMs) while (day.getTime() <= lastOccupiedDayMs) { const dayKey = day.getTime() const bucket = index.byDay.get(dayKey) if (bucket) bucket.push(occurrence) else index.byDay.set(dayKey, [occurrence]) day = startOfLocalDay(shiftLocalDays(day, 1)) } } function sortOccurrences(occurrences: CalendarOccurrence[]) { occurrences.sort((left, right) => ( left.startMs - right.startMs || right.durationMs - left.durationMs || left.key.localeCompare(right.key) )) } export function buildOccurrenceIndex( entities: EntityObject[], range?: VisibleDateRange, ): CalendarOccurrenceIndex { const index: CalendarOccurrenceIndex = { byDay: new Map(), byEntity: new Map(), sorted: [], } for (const entity of entities) { const entityOccurrences = expandEntityOccurrences(entity, range) if (entityOccurrences.length === 0) continue index.byEntity.set(String(entity.identifier), entityOccurrences) for (const occurrence of entityOccurrences) { index.sorted.push(occurrence) appendToDayBuckets(index, occurrence) } } sortOccurrences(index.sorted) for (const bucket of index.byDay.values()) sortOccurrences(bucket) return index } export function occurrencesForDay( index: CalendarOccurrenceIndex, date: Date | number, ): CalendarOccurrence[] { const dayKey = startOfLocalDay(new Date(date)).getTime() return index.byDay.get(dayKey) ?? [] } export function occurrencesForRange( index: CalendarOccurrenceIndex, range: VisibleDateRange, ): CalendarOccurrence[] { if (range.endMs <= range.startMs) return [] const matches = new Map() let day = startOfLocalDay(new Date(range.startMs)) while (day.getTime() < range.endMs) { for (const occurrence of index.byDay.get(day.getTime()) ?? []) { if (occurrence.startMs < range.endMs && occurrence.endMs > range.startMs) { matches.set(occurrence.key, occurrence) } } day = startOfLocalDay(shiftLocalDays(day, 1)) } const occurrences = Array.from(matches.values()) sortOccurrences(occurrences) return occurrences }