package document
import (
protocol "wellquite.org/edist/protocol/go"
)
type WordManager struct {
root *protocol.Word
orderedWords []*protocol.Word
wordsById map[protocol.WordId]*protocol.Word
}
func NewWordManager() *WordManager {
root := &protocol.Word{
WordId: protocol.WordId{
ClientId: 0,
Counter: 1,
},
}
return &WordManager{
root: root,
orderedWords: []*protocol.Word{root},
wordsById: map[protocol.WordId]*protocol.Word{root.WordId: root},
}
}
type UpdatePair struct {
original *protocol.Word
updated *protocol.Word
isNewWord bool
}
func (self *WordManager) FilterUpdatesForNewer(updates []protocol.Word) []*UpdatePair {
filteredUpdates := make([]*UpdatePair, 0, len(updates))
for idx, word := range updates {
existing, found := self.wordsById[word.WordId]
if found {
if word.Version.GreaterThan(existing.Version) {
filteredUpdates = append(filteredUpdates, &UpdatePair{
original: existing,
updated: &updates[idx],
isNewWord: false,
})
}
} else {
filteredUpdates = append(filteredUpdates, &UpdatePair{
original: self.NewWord(word.WordId),
updated: &updates[idx],
isNewWord: true,
})
}
}
return filteredUpdates
}
func (self *WordManager) FilterUpdatesForKnown(updates []*UpdatePair) []protocol.Word {
words := make([]protocol.Word, 0, len(updates))
for _, update := range updates {
if _, found := self.wordsById[update.original.WordId]; found {
words = append(words, *update.original)
}
}
return words
}
func (self *WordManager) ApplyUpdates(updates []*UpdatePair) {
for _, update := range updates {
original, updated := update.original, update.updated
if updated.Version.GreaterThan(original.Version) {
original.Version = updated.Version
}
original.Letters = updated.Letters
original.Links = updated.Links
}
}
func (self *WordManager) NewWord(wordId protocol.WordId) *protocol.Word {
word := &protocol.Word{
WordId: wordId,
}
self.wordsById[wordId] = word
return word
}
func (self *WordManager) GC() {
if len(self.wordsById) < 2 {
return
}
wordsById := make(map[protocol.WordId]*protocol.Word, len(self.wordsById))
orderedWords := make([]*protocol.Word, 0, len(self.orderedWords))
for cur := self.root; cur != nil; {
orderedWords = append(orderedWords, cur)
wordsById[cur.WordId] = cur
if cur.Links.Next == nil {
break
}
cur = self.wordsById[*cur.Links.Next]
}
self.wordsById = wordsById
self.orderedWords = orderedWords
}
func (self *WordManager) CreateCheckpoint() *protocol.Message {
words := make([]protocol.Word, len(self.orderedWords))
for idx, word := range self.orderedWords {
words[idx] = *word
}
return &protocol.Message{
Updates: &words,
}
}
// How do we get from b to a.
func (a *WordManager) Delta(b *WordManager) *protocol.Message {
words := make([]protocol.Word, 0, len(a.orderedWords))
for _, wordA := range a.orderedWords {
wordB, foundB := b.wordsById[wordA.WordId]
if !foundB || wordA.Version.GreaterThan(wordB.Version) {
words = append(words, *wordA)
}
}
return &protocol.Message{
Updates: &words,
}
}
func (self *WordManager) String() string {
str := ""
for _, word := range self.orderedWords {
str += " " + word.Letters
}
if len(str) > 0 {
return str[1:]
}
return str
}