//go:build kvdb_etcd
// +build kvdb_etcd

package etcd

// readWriteCursor holds a reference to the cursors bucket, the value
// prefix and the current key used while iterating.
type readWriteCursor struct {
	// bucket holds the reference to the parent bucket.
	bucket *readWriteBucket

	// prefix holds the value prefix which is in front of each
	// value key in the bucket.
	prefix string

	// currKey holds the current key of the cursor.
	currKey string
}

func newReadWriteCursor(bucket *readWriteBucket) *readWriteCursor {
	return &readWriteCursor{
		bucket: bucket,
		prefix: string(bucket.id),
	}
}

// First positions the cursor at the first key/value pair and returns
// the pair.
func (c *readWriteCursor) First() (key, value []byte) {
	// Get the first key with the value prefix.
	kv, err := c.bucket.tx.stm.First(c.prefix)
	if err != nil {
		// TODO: revise this once kvdb interface supports errors
		return nil, nil
	}

	if kv != nil {
		c.currKey = kv.key
		return getKeyVal(kv)
	}

	return nil, nil
}

// Last positions the cursor at the last key/value pair and returns the
// pair.
func (c *readWriteCursor) Last() (key, value []byte) {
	kv, err := c.bucket.tx.stm.Last(c.prefix)
	if err != nil {
		// TODO: revise this once kvdb interface supports errors
		return nil, nil
	}

	if kv != nil {
		c.currKey = kv.key
		return getKeyVal(kv)
	}

	return nil, nil
}

// Next moves the cursor one key/value pair forward and returns the new
// pair.
func (c *readWriteCursor) Next() (key, value []byte) {
	kv, err := c.bucket.tx.stm.Next(c.prefix, c.currKey)
	if err != nil {
		// TODO: revise this once kvdb interface supports errors
		return nil, nil
	}

	if kv != nil {
		c.currKey = kv.key
		return getKeyVal(kv)
	}

	return nil, nil
}

// Prev moves the cursor one key/value pair backward and returns the new
// pair.
func (c *readWriteCursor) Prev() (key, value []byte) {
	kv, err := c.bucket.tx.stm.Prev(c.prefix, c.currKey)
	if err != nil {
		// TODO: revise this once kvdb interface supports errors
		return nil, nil
	}

	if kv != nil {
		c.currKey = kv.key
		return getKeyVal(kv)
	}

	return nil, nil
}

// Seek positions the cursor at the passed seek key.  If the key does
// not exist, the cursor is moved to the next key after seek.  Returns
// the new pair.
func (c *readWriteCursor) Seek(seek []byte) (key, value []byte) {
	// Seek to the first key with prefix + seek. If that key is not present
	// STM will seek to the next matching key with prefix.
	kv, err := c.bucket.tx.stm.Seek(c.prefix, c.prefix+string(seek))
	if err != nil {
		// TODO: revise this once kvdb interface supports errors
		return nil, nil
	}

	if kv != nil {
		c.currKey = kv.key
		return getKeyVal(kv)
	}

	return nil, nil
}

// Delete removes the current key/value pair the cursor is at without
// invalidating the cursor.  Returns ErrIncompatibleValue if attempted
// when the cursor points to a nested bucket.
func (c *readWriteCursor) Delete() error {
	if isBucketKey(c.currKey) {
		c.bucket.DeleteNestedBucket(getKey(c.currKey))
	} else {
		c.bucket.Delete(getKey(c.currKey))
	}

	return nil
}
