package reads

import (
	"bytes"
	"strings"

	"github.com/influxdata/influxdb/v2/models"
)

// tagsKeyMerger is responsible for determining a merged set of tag keys
type KeyMerger struct {
	i    int
	tmp  [][]byte
	keys [2][][]byte
}

func (km *KeyMerger) Clear() {
	km.i = 0
	km.keys[0] = km.keys[0][:0]
	if km.tmp != nil {
		tmp := km.tmp[:cap(km.tmp)]
		for i := range tmp {
			tmp[i] = nil
		}
	}
}

func (km *KeyMerger) Get() [][]byte { return km.keys[km.i&1] }

func (km *KeyMerger) String() string {
	var s []string
	for _, k := range km.Get() {
		s = append(s, string(k))
	}
	return strings.Join(s, ",")
}

func (km *KeyMerger) MergeTagKeys(tags models.Tags) {
	if cap(km.tmp) < len(tags) {
		km.tmp = make([][]byte, len(tags))
	} else {
		km.tmp = km.tmp[:len(tags)]
	}

	for i := range tags {
		km.tmp[i] = tags[i].Key
	}

	km.MergeKeys(km.tmp)
}

func (km *KeyMerger) MergeKeys(in [][]byte) {
	keys := km.keys[km.i&1]
	i, j := 0, 0
	for i < len(keys) && j < len(in) && bytes.Equal(keys[i], in[j]) {
		i++
		j++
	}

	if j == len(in) {
		// no new tags
		return
	}

	km.i = (km.i + 1) & 1
	l := len(keys) + len(in)
	if cap(km.keys[km.i]) < l {
		km.keys[km.i] = make([][]byte, l)
	} else {
		km.keys[km.i] = km.keys[km.i][:l]
	}

	keya := km.keys[km.i]

	// back up the pointers
	if i > 0 {
		i--
		j--
	}

	k := i
	copy(keya[:k], keys[:k])

	for i < len(keys) && j < len(in) {
		cmp := bytes.Compare(keys[i], in[j])
		if cmp < 0 {
			keya[k] = keys[i]
			i++
		} else if cmp > 0 {
			keya[k] = in[j]
			j++
		} else {
			keya[k] = keys[i]
			i++
			j++
		}
		k++
	}

	if i < len(keys) {
		k += copy(keya[k:], keys[i:])
	}

	if j < len(in) {
		k += copy(keya[k:], in[j:])
	}

	km.keys[km.i] = keya[:k]
}
