internal/dateprogression.go (view raw)
1package bible
2
3import (
4 "fmt"
5 "time"
6)
7
8// DateProgression handles calculating Bible position based on date
9type DateProgression struct {
10 bible *Bible
11}
12
13// NewDateProgression creates a new date progression calculator
14func NewDateProgression(bible *Bible) *DateProgression {
15 return &DateProgression{bible: bible}
16}
17
18// GetPositionForDate calculates the verse range for a given date
19func (dp *DateProgression) GetPositionForDate(date time.Time) (*Bookmark, error) {
20 // Calculate day of year (1-366)
21 dayOfYear := date.YearDay()
22
23 // Each day shows 12 verses
24 targetVerseOffset := (dayOfYear - 1) * 12 // Zero-indexed
25
26 // Walk through Gospels to find the position
27 return dp.findPositionForOffset(targetVerseOffset)
28}
29
30// findPositionForOffset finds the bookmark for a given cumulative verse offset
31func (dp *DateProgression) findPositionForOffset(offset int) (*Bookmark, error) {
32 // Iterate through books 40-43 (Matthew, Mark, Luke, John)
33 for book := 40; book <= 43; book++ {
34 // Find last chapter in this book
35 maxChapter := 0
36 for chapter := 1; ; chapter++ {
37 if dp.bible.CountVersesInChapter(book, chapter) == 0 {
38 maxChapter = chapter - 1
39 break
40 }
41 }
42
43 // Iterate through chapters
44 for chapter := 1; chapter <= maxChapter; chapter++ {
45 versesInChapter := dp.bible.CountVersesInChapter(book, chapter)
46
47 // Check if offset falls within this chapter
48 if offset < versesInChapter {
49 // Offset is within this chapter
50 firstVerse := offset + 1 // Convert from 0-indexed to 1-indexed
51
52 // Create initial bookmark (like AdvanceBookmark does)
53 secondVerse := min(firstVerse+11, versesInChapter)
54 bookmark := &Bookmark{
55 Book: BookIndex(book),
56 Chapter: chapter,
57 FirstVerse: firstVerse,
58 SecondVerse: secondVerse,
59 }
60
61 // Apply lookahead rule (like AdjustForLookahead)
62 return dp.applyLookahead(bookmark), nil
63 }
64
65 // Move to next chapter, subtracting this chapter's verses
66 offset -= versesInChapter
67 }
68 }
69
70 // If we've gone through all Gospels and offset is still positive,
71 // wrap around to beginning (start over)
72 // Calculate modulo offset within total Gospel verses
73 totalVerses := dp.GetTotalGospelVerses()
74 if offset >= 0 {
75 adjustedOffset := offset % totalVerses
76 // Recursively find position for adjusted offset
77 return dp.findPositionForOffset(adjustedOffset)
78 }
79
80 return nil, fmt.Errorf("could not find position for offset %d", offset)
81}
82
83// applyLookahead applies the lookahead rule (same as BookmarkManager.AdjustForLookahead)
84func (dp *DateProgression) applyLookahead(bookmark *Bookmark) *Bookmark {
85 versesInChapter := dp.bible.CountVersesInChapter(int(bookmark.Book), bookmark.Chapter)
86 versesRemaining := versesInChapter - bookmark.SecondVerse
87
88 // If we're not at the end of the chapter and less than 12 verses remain for NEXT snippet
89 if versesRemaining > 0 && versesRemaining < 12 {
90 return &Bookmark{
91 Book: bookmark.Book,
92 Chapter: bookmark.Chapter,
93 FirstVerse: bookmark.FirstVerse,
94 SecondVerse: versesInChapter,
95 }
96 }
97
98 // Otherwise return the original bookmark
99 return bookmark
100}
101
102// GetTotalGospelVerses returns the total number of verses in all four Gospels
103func (dp *DateProgression) GetTotalGospelVerses() int {
104 total := 0
105 for book := 40; book <= 43; book++ {
106 for chapter := 1; ; chapter++ {
107 verses := dp.bible.CountVersesInChapter(book, chapter)
108 if verses == 0 {
109 break
110 }
111 total += verses
112 }
113 }
114 return total
115}