// Copyright (C) 2014 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package db

import (
	"encoding/binary"
	"time"

	"github.com/syndtr/goleveldb/leveldb"
)

// NamespacedKV is a simple key-value store using a specific namespace within
// a leveldb.
type NamespacedKV struct {
	db     *leveldb.DB
	prefix []byte
}

// NewNamespacedKV returns a new NamespacedKV that lives in the namespace
// specified by the prefix.
func NewNamespacedKV(db *leveldb.DB, prefix string) *NamespacedKV {
	return &NamespacedKV{
		db:     db,
		prefix: []byte(prefix),
	}
}

// PutInt64 stores a new int64. Any existing value (even if of another type)
// is overwritten.
func (n *NamespacedKV) PutInt64(key string, val int64) {
	keyBs := append(n.prefix, []byte(key)...)
	var valBs [8]byte
	binary.BigEndian.PutUint64(valBs[:], uint64(val))
	n.db.Put(keyBs, valBs[:], nil)
}

// Int64 returns the stored value interpreted as an int64 and a boolean that
// is false if no value was stored at the key.
func (n *NamespacedKV) Int64(key string) (int64, bool) {
	keyBs := append(n.prefix, []byte(key)...)
	valBs, err := n.db.Get(keyBs, nil)
	if err != nil {
		return 0, false
	}
	val := binary.BigEndian.Uint64(valBs)
	return int64(val), true
}

// PutTime stores a new time.Time. Any existing value (even if of another
// type) is overwritten.
func (n *NamespacedKV) PutTime(key string, val time.Time) {
	keyBs := append(n.prefix, []byte(key)...)
	valBs, _ := val.MarshalBinary() // never returns an error
	n.db.Put(keyBs, valBs, nil)
}

// Time returns the stored value interpreted as a time.Time and a boolean
// that is false if no value was stored at the key.
func (n NamespacedKV) Time(key string) (time.Time, bool) {
	var t time.Time
	keyBs := append(n.prefix, []byte(key)...)
	valBs, err := n.db.Get(keyBs, nil)
	if err != nil {
		return t, false
	}
	err = t.UnmarshalBinary(valBs)
	return t, err == nil
}

// PutString stores a new string. Any existing value (even if of another type)
// is overwritten.
func (n *NamespacedKV) PutString(key, val string) {
	keyBs := append(n.prefix, []byte(key)...)
	n.db.Put(keyBs, []byte(val), nil)
}

// String returns the stored value interpreted as a string and a boolean that
// is false if no value was stored at the key.
func (n NamespacedKV) String(key string) (string, bool) {
	keyBs := append(n.prefix, []byte(key)...)
	valBs, err := n.db.Get(keyBs, nil)
	if err != nil {
		return "", false
	}
	return string(valBs), true
}

// Delete deletes the specified key. It is allowed to delete a nonexistent
// key.
func (n NamespacedKV) Delete(key string) {
	keyBs := append(n.prefix, []byte(key)...)
	n.db.Delete(keyBs, nil)
}
