package fzf

import “fmt”

// EmptyMerger is a Merger with no data var EmptyMerger = NewMerger(nil, [][]Result{}, false, false)

// Merger holds a set of locally sorted lists of items and provides the view of // a single, globally-sorted list type Merger struct {

pattern *Pattern
lists   [][]Result
merged  []Result
chunks  *[]*Chunk
cursors []int
sorted  bool
tac     bool
final   bool
count   int

}

// PassMerger returns a new Merger that simply returns the items in the // original order func PassMerger(chunks *[]*Chunk, tac bool) *Merger {

mg := Merger{
        pattern: nil,
        chunks:  chunks,
        tac:     tac,
        count:   0}

for _, chunk := range *mg.chunks {
        mg.count += chunk.count
}
return &mg

}

// NewMerger returns a new Merger func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool) *Merger {

mg := Merger{
        pattern: pattern,
        lists:   lists,
        merged:  []Result{},
        chunks:  nil,
        cursors: make([]int, len(lists)),
        sorted:  sorted,
        tac:     tac,
        final:   false,
        count:   0}

for _, list := range mg.lists {
        mg.count += len(list)
}
return &mg

}

// Length returns the number of items func (mg *Merger) Length() int {

return mg.count

}

// Get returns the pointer to the Result object indexed by the given integer func (mg *Merger) Get(idx int) Result {

if mg.chunks != nil {
        if mg.tac {
                idx = mg.count - idx - 1
        }
        chunk := (*mg.chunks)[idx/chunkSize]
        return Result{item: &chunk.items[idx%chunkSize]}
}

if mg.sorted {
        return mg.mergedGet(idx)
}

if mg.tac {
        idx = mg.count - idx - 1
}
for _, list := range mg.lists {
        numItems := len(list)
        if idx < numItems {
                return list[idx]
        }
        idx -= numItems
}
panic(fmt.Sprintf("Index out of bounds (unsorted, %d/%d)", idx, mg.count))

}

func (mg *Merger) cacheable() bool {

return mg.count < mergerCacheMax

}

func (mg *Merger) mergedGet(idx int) Result {

for i := len(mg.merged); i <= idx; i++ {
        minRank := minRank()
        minIdx := -1
        for listIdx, list := range mg.lists {
                cursor := mg.cursors[listIdx]
                if cursor < 0 || cursor == len(list) {
                        mg.cursors[listIdx] = -1
                        continue
                }
                if cursor >= 0 {
                        rank := list[cursor]
                        if minIdx < 0 || compareRanks(rank, minRank, mg.tac) {
                                minRank = rank
                                minIdx = listIdx
                        }
                }
        }

        if minIdx >= 0 {
                chosen := mg.lists[minIdx]
                mg.merged = append(mg.merged, chosen[mg.cursors[minIdx]])
                mg.cursors[minIdx]++
        } else {
                panic(fmt.Sprintf("Index out of bounds (sorted, %d/%d)", i, mg.count))
        }
}
return mg.merged[idx]

}