import 'package:worldhopper/providers/page_dimensions_provider.dart'; /// Represents a spread (one or two pages displayed together). class PageSpread { /// Page indices in display order (left to right on screen). final List pages; const PageSpread(this.pages); bool get isSingle => pages.length == 1; bool get isPair => pages.length == 2; /// The lowest page index in this spread (used for progress tracking). int get primaryPage => pages.reduce((a, b) => a < b ? a : b); /// The highest page index in this spread. int get lastPage => pages.reduce((a, b) => a > b ? a : b); } /// Computes page spreads based on known dimensions. /// /// Rules: /// - If [firstPageIsCover], page 0 is shown alone /// - Landscape images (aspect ratio >= threshold) shown alone /// - Two consecutive portrait pages paired side by side /// - Look-ahead: if current is portrait but next is landscape, show current alone /// - Last page shown alone if unpaired /// - [rtl]: pages within a pair are reversed (higher index on left) List computePageSpreads({ required int pageCount, required Map dimensions, required bool rtl, bool firstPageIsCover = true, }) { if (pageCount == 0) return []; final spreads = []; var i = 0; while (i < pageCount) { // Cover page shown alone if (i == 0 && firstPageIsCover) { spreads.add(const PageSpread([0])); i++; continue; } final currentDim = dimensions[i]; final isCurrentDouble = currentDim?.isDoublePage ?? false; // Landscape/double-page images shown alone if (isCurrentDouble) { spreads.add(PageSpread([i])); i++; continue; } // Last page with no partner if (i + 1 >= pageCount) { spreads.add(PageSpread([i])); i++; continue; } // Look ahead: if next page is landscape, show current alone final nextDim = dimensions[i + 1]; final isNextDouble = nextDim?.isDoublePage ?? false; if (isNextDouble) { spreads.add(PageSpread([i])); i++; continue; } // Pair two portrait pages if (rtl) { // RTL: higher-index page on the left spreads.add(PageSpread([i + 1, i])); } else { spreads.add(PageSpread([i, i + 1])); } i += 2; } return spreads; } /// Returns the spread index that contains the given page index. int spreadIndexForPage(List spreads, int pageIndex) { for (var i = 0; i < spreads.length; i++) { if (spreads[i].pages.contains(pageIndex)) { return i; } } // Fallback: return last spread return spreads.isEmpty ? 0 : spreads.length - 1; } /// Returns the primary (lowest) page index for a given spread index. int primaryPageForSpread(List spreads, int spreadIndex) { if (spreadIndex < 0 || spreadIndex >= spreads.length) return 0; return spreads[spreadIndex].primaryPage; } /// Returns the last (highest) page index for a given spread index. int lastPageInSpread(List spreads, int spreadIndex) { if (spreadIndex < 0 || spreadIndex >= spreads.length) return 0; return spreads[spreadIndex].lastPage; }