Swiftgram/Postbox/ItemCollectionsView.swift
2016-11-02 03:14:12 +03:00

205 lines
11 KiB
Swift

import Foundation
public struct ItemCollectionViewEntryIndex: Comparable {
public let collectionIndex: Int32
public let collectionId: ItemCollectionId
public let itemIndex: ItemCollectionItemIndex
public static func ==(lhs: ItemCollectionViewEntryIndex, rhs: ItemCollectionViewEntryIndex) -> Bool {
return lhs.collectionIndex == rhs.collectionIndex && lhs.collectionId == rhs.collectionId && lhs.itemIndex == rhs.itemIndex
}
public static func <(lhs: ItemCollectionViewEntryIndex, rhs: ItemCollectionViewEntryIndex) -> Bool {
if lhs.collectionIndex == rhs.collectionIndex {
if lhs.itemIndex == rhs.itemIndex {
return lhs.collectionId < rhs.collectionId
} else {
return lhs.itemIndex < rhs.itemIndex
}
} else {
return lhs.collectionIndex < rhs.collectionIndex
}
}
public static func lowerBound(collectionIndex: Int32, collectionId: ItemCollectionId) -> ItemCollectionViewEntryIndex {
return ItemCollectionViewEntryIndex(collectionIndex: collectionIndex, collectionId: collectionId, itemIndex: ItemCollectionItemIndex(index: 0, id: 0))
}
}
public struct ItemCollectionViewEntry {
public let index: ItemCollectionViewEntryIndex
public let item: ItemCollectionItem
}
private func fetchLowerEntries(namespaces: [ItemCollectionId.Namespace], collectionId: ItemCollectionId, collectionIndex: Int32, itemIndex: ItemCollectionItemIndex, count: Int, lowerCollectionId: (_ namespaceList: [ItemCollectionId.Namespace], _ collectionId: ItemCollectionId, _ collectionIndex: Int32) -> (ItemCollectionId, Int32)?, lowerItems: (_ collectionId: ItemCollectionId, _ itemIndex: ItemCollectionItemIndex, _ count: Int) -> [ItemCollectionItem]) -> [ItemCollectionViewEntry] {
var entries: [ItemCollectionViewEntry] = []
var currentCollectionIndex = collectionIndex
var currentCollectionId = collectionId
var currentItemIndex = itemIndex
while true {
let remainingCount = count - entries.count
assert(remainingCount > 0)
var collectionItems = lowerItems(currentCollectionId, currentItemIndex, remainingCount)
for item in collectionItems {
entries.append(ItemCollectionViewEntry(index: ItemCollectionViewEntryIndex(collectionIndex: currentCollectionIndex, collectionId: currentCollectionId, itemIndex: item.index), item: item))
}
if entries.count >= count {
break
} else {
assert(collectionItems.count < remainingCount)
if let (previousCollectionId, previousCollectionIndex) = lowerCollectionId(namespaces, currentCollectionId, currentCollectionIndex) {
currentCollectionIndex = previousCollectionIndex
currentCollectionId = previousCollectionId
currentItemIndex = ItemCollectionItemIndex.upperBound
} else {
break
}
}
}
return entries
}
private func fetchHigherEntries(namespaces: [ItemCollectionId.Namespace], collectionId: ItemCollectionId, collectionIndex: Int32, itemIndex: ItemCollectionItemIndex, count: Int, higherCollectionId: (_ namespaceList: [ItemCollectionId.Namespace], _ collectionId: ItemCollectionId, _ collectionIndex: Int32) -> (ItemCollectionId, Int32)?, higherItems: (_ collectionId: ItemCollectionId, _ itemIndex: ItemCollectionItemIndex, _ count: Int) -> [ItemCollectionItem]) -> [ItemCollectionViewEntry] {
var entries: [ItemCollectionViewEntry] = []
var currentCollectionIndex = collectionIndex
var currentCollectionId = collectionId
var currentItemIndex = itemIndex
while true {
let remainingCount = count - entries.count
assert(remainingCount > 0)
var collectionItems = higherItems(currentCollectionId, currentItemIndex, remainingCount)
for item in collectionItems {
entries.append(ItemCollectionViewEntry(index: ItemCollectionViewEntryIndex(collectionIndex: currentCollectionIndex, collectionId: currentCollectionId, itemIndex: item.index), item: item))
}
if entries.count >= count {
break
} else {
assert(collectionItems.count < remainingCount)
if let (nextCollectionId, nextCollectionIndex) = higherCollectionId(namespaces, currentCollectionId, currentCollectionIndex) {
currentCollectionIndex = nextCollectionIndex
currentCollectionId = nextCollectionId
currentItemIndex = ItemCollectionItemIndex.lowerBound
} else {
break
}
}
}
return entries
}
private func aroundEntries(namespaces: [ItemCollectionId.Namespace],
collectionId: ItemCollectionId,
collectionIndex: Int32,
itemIndex: ItemCollectionItemIndex,
count: Int,
lowerCollectionId: (_ namespaceList: [ItemCollectionId.Namespace], _ collectionId: ItemCollectionId, _ collectionIndex: Int32) -> (ItemCollectionId, Int32)?,
fetchLowerItems: (_ collectionId: ItemCollectionId, _ itemIndex: ItemCollectionItemIndex, _ count: Int) -> [ItemCollectionItem],
higherCollectionId: (_ namespaceList: [ItemCollectionId.Namespace], _ collectionId: ItemCollectionId, _ collectionIndex: Int32) -> (ItemCollectionId, Int32)?,
fetchHigherItems: (_ collectionId: ItemCollectionId, _ itemIndex: ItemCollectionItemIndex, _ count: Int) -> [ItemCollectionItem]) -> ([ItemCollectionViewEntry], ItemCollectionViewEntry?, ItemCollectionViewEntry?) {
var lowerEntries: [ItemCollectionViewEntry] = []
var upperEntries: [ItemCollectionViewEntry] = []
var lower: ItemCollectionViewEntry?
var upper: ItemCollectionViewEntry?
lowerEntries.append(contentsOf: fetchLowerEntries(namespaces: namespaces, collectionId: collectionId, collectionIndex: collectionIndex, itemIndex: itemIndex, count: count / 2 + 1, lowerCollectionId: lowerCollectionId, lowerItems: fetchLowerItems))
let lowerIndices = lowerEntries.map { $0.index }
assert(lowerIndices.sorted() == lowerIndices.reversed())
if lowerEntries.count >= count / 2 + 1 {
lower = lowerEntries.last
lowerEntries.removeLast()
}
upperEntries.append(contentsOf: fetchHigherEntries(namespaces: namespaces, collectionId: collectionId, collectionIndex: collectionIndex, itemIndex: ItemCollectionItemIndex(index: itemIndex.index, id: max(0, itemIndex.id - 1)), count: count - lowerEntries.count + 1, higherCollectionId: higherCollectionId, higherItems: fetchHigherItems))
let upperIndices = upperEntries.map { $0.index }
assert(upperIndices.sorted() == upperIndices)
if upperEntries.count >= count - lowerEntries.count + 1 {
upper = upperEntries.last
upperEntries.removeLast()
}
if lowerEntries.count != 0 && lowerEntries.count + upperEntries.count < count {
var additionalLowerEntries: [ItemCollectionViewEntry] = fetchLowerEntries(namespaces: namespaces, collectionId: lowerEntries.last!.index.collectionId, collectionIndex: lowerEntries.last!.index.collectionIndex, itemIndex: lowerEntries.last!.index.itemIndex, count: count - lowerEntries.count - upperEntries.count + 1, lowerCollectionId: lowerCollectionId, lowerItems: fetchLowerItems)
if additionalLowerEntries.count >= count - lowerEntries.count + upperEntries.count + 1 {
lower = additionalLowerEntries.last
additionalLowerEntries.removeLast()
}
lowerEntries.append(contentsOf: additionalLowerEntries)
}
var entries: [ItemCollectionViewEntry] = []
entries.append(contentsOf: lowerEntries.reversed())
entries.append(contentsOf: upperEntries)
return (entries: entries, lower: lower, upper: upper)
}
final class MutableItemCollectionsView {
let namespaces: [ItemCollectionId.Namespace]
var collectionInfos: [(ItemCollectionId, ItemCollectionInfo, ItemCollectionItem?)]
var entries: [ItemCollectionViewEntry]
var lower: ItemCollectionViewEntry?
var higher: ItemCollectionViewEntry?
init(namespaces: [ItemCollectionId.Namespace], aroundIndex: ItemCollectionViewEntryIndex?, count: Int, getInfos: (_ namespace: ItemCollectionId.Namespace) -> [(Int, ItemCollectionId, ItemCollectionInfo)], lowerCollectionId: (_ namespaceList: [ItemCollectionId.Namespace], _ collectionId: ItemCollectionId, _ collectionIndex: Int32) -> (ItemCollectionId, Int32)?, lowerItems: (_ collectionId: ItemCollectionId, _ itemIndex: ItemCollectionItemIndex, _ count: Int) -> [ItemCollectionItem], higherCollectionId: (_ namespaceList: [ItemCollectionId.Namespace], _ collectionId: ItemCollectionId, _ collectionIndex: Int32) -> (ItemCollectionId, Int32)?, higherItems: (_ collectionId: ItemCollectionId, _ itemIndex: ItemCollectionItemIndex, _ count: Int) -> [ItemCollectionItem]) {
self.namespaces = namespaces
self.collectionInfos = []
for namespace in namespaces {
for (_, id, info) in getInfos(namespace) {
let item = higherItems(id, ItemCollectionItemIndex.lowerBound, 1).first
self.collectionInfos.append((id, info, item))
}
}
let selectedAroundIndex: ItemCollectionViewEntryIndex
if let aroundIndex = aroundIndex {
selectedAroundIndex = aroundIndex
} else {
selectedAroundIndex = ItemCollectionViewEntryIndex(collectionIndex: 0, collectionId: ItemCollectionId(namespace: namespaces[0], id: 0), itemIndex: ItemCollectionItemIndex.lowerBound)
}
let (entries, lower, higher) = aroundEntries(namespaces: namespaces,
collectionId: selectedAroundIndex.collectionId,
collectionIndex: selectedAroundIndex.collectionIndex,
itemIndex: selectedAroundIndex.itemIndex,
count: count,
lowerCollectionId: lowerCollectionId,
fetchLowerItems: lowerItems,
higherCollectionId: higherCollectionId,
fetchHigherItems: higherItems)
self.entries = entries
self.lower = lower
self.higher = higher
}
}
public final class ItemCollectionsView {
public let collectionInfos: [(ItemCollectionId, ItemCollectionInfo, ItemCollectionItem?)]
public let entries: [ItemCollectionViewEntry]
public let lower: ItemCollectionViewEntry?
public let higher: ItemCollectionViewEntry?
init(_ mutableView: MutableItemCollectionsView) {
self.collectionInfos = mutableView.collectionInfos
self.entries = mutableView.entries
self.lower = mutableView.lower
self.higher = mutableView.higher
}
}