// // ReadingPlanSchedule.swift // Bible-Week // // Plano "Bíblia em 180 Dias": três trilhas paralelas por dia // (História, Sabedoria e Profetas, Novo Testamento), distribuindo os // capítulos de cada trilha uniformemente pelos 180 dias. // import Foundation /// As três trilhas de leitura diária do plano. enum PlanTrack: String, CaseIterable, Codable, Sendable { case history = "history" case wisdom = "wisdom" case newTestament = "nt" var displayName: String { switch self { case .history: "História" case .wisdom: "Sabedoria e Profetas" case .newTestament: "Novo Testamento" } } /// Livros da trilha, em ordem canônica. var books: [Book] { switch self { case .history: BibleCanon.books(in: 1...17) // Gênesis...Ester case .wisdom: BibleCanon.books(in: 18...39) // Jó...Malaquias case .newTestament: BibleCanon.books(in: 40...66) // Mateus...Apocalipse } } } /// Referência a um capítulo específico de um livro. struct ChapterRef: Hashable, Sendable { let bookCode: String let bookName: String let chapter: Int } /// Os capítulos de uma trilha em um dia do plano. struct TrackReading: Hashable, Sendable, Identifiable { let track: PlanTrack let chapters: [ChapterRef] var id: PlanTrack { track } /// Rótulo legível, ex.: "Gênesis 34–36" ou "Obadias 1 e Jonas 1–2". var label: String { // Agrupa capítulos consecutivos do mesmo livro em intervalos. var runs: [(name: String, first: Int, last: Int)] = [] for ref in chapters { if var last = runs.last, last.name == ref.bookName, last.last == ref.chapter - 1 { last.last = ref.chapter runs[runs.count - 1] = last } else { runs.append((ref.bookName, ref.chapter, ref.chapter)) } } let parts = runs.map { run in run.first == run.last ? "\(run.name) \(run.first)" : "\(run.name) \(run.first)–\(run.last)" } return parts.joined(separator: " e ") } } /// A leitura completa de um dia (as três trilhas). struct DayReading: Sendable { let day: Int let readings: [TrackReading] func reading(for track: PlanTrack) -> TrackReading? { readings.first { $0.track == track } } } /// Cronograma fixo e determinístico do plano de 180 dias. enum PlanSchedule { static let totalDays = 180 static let days: [DayReading] = generate() /// Leitura do dia `n` (1-based, limitado a 1...180). static func day(_ n: Int) -> DayReading { days[max(1, min(n, totalDays)) - 1] } private static func generate() -> [DayReading] { let trackChapters: [PlanTrack: [ChapterRef]] = Dictionary( uniqueKeysWithValues: PlanTrack.allCases.map { track in let refs = track.books.flatMap { book in (1...book.chapterCount).map { ChapterRef(bookCode: book.code, bookName: book.name, chapter: $0) } } return (track, refs) } ) return (0.. TrackReading in let all = trackChapters[track]! // Partição uniforme: o dia i recebe as posições // [i*T/180, (i+1)*T/180), cobrindo tudo sem sobreposição. let start = index * all.count / totalDays let end = (index + 1) * all.count / totalDays return TrackReading(track: track, chapters: Array(all[start..