package bolt

import (
	"encoding"
	"encoding/json"

	bolt "github.com/coreos/bbolt"
)

type BucketStats bolt.BucketStats

type Bucket struct {
	bucket  *bolt.Bucket
	options Optioner
}

func (b *Bucket) Bucket(name []byte) *Bucket {
	return &Bucket{
		bucket:  b.bucket.Bucket(name),
		options: b.options,
	}
}

func (b *Bucket) CreateBucket(key []byte) (*Bucket, error) {
	bucket, err := b.bucket.CreateBucket(key)
	if err != nil {
		return nil, err
	}
	return &Bucket{
		bucket:  bucket,
		options: b.options,
	}, nil
}

func (b *Bucket) CreateBucketIfNotExists(key []byte) (*Bucket, error) {
	bucket, err := b.bucket.CreateBucket(key)
	if err != nil {
		return nil, err
	}
	return &Bucket{
		bucket:  bucket,
		options: b.options,
	}, nil
}

func (b *Bucket) Cursor() *Cursor {
	return &Cursor{
		cursor:  b.bucket.Cursor(),
		options: b.options,
	}
}

func (b *Bucket) Delete(key []byte) error {
	return b.bucket.Delete(key)
}

func (b *Bucket) DeleteBucket(key []byte) error {
	return b.bucket.DeleteBucket(key)
}

func (b *Bucket) ForEach(fn func(k, v []byte) error) error {
	f := func(k, v []byte) (err error) {
		if v, err = decrypt(b.options, v); err != nil {
			return err
		}
		return fn(k, v)
	}
	return b.bucket.ForEach(f)
}

func (b *Bucket) Get(key []byte) (value []byte, err error) {
	if value, err = decrypt(b.options, b.bucket.Get(key)); err != nil {
		return nil, err
	}
	return value, nil
}

func (b *Bucket) NextSequence() ([]byte, error) {
	s, err := b.bucket.NextSequence()
	if err != nil {
		return nil, err
	}
	return itob(s), nil
}

func (b *Bucket) Put(key []byte, bs []byte) error {
	bse, err := encrypt(b.options, bs)
	if err != nil {
		return err
	}
	return b.bucket.Put(key, bse)
}

func (b *Bucket) PutString(key []byte, s string) error {
	return b.Put(key, []byte(s))
}

func (b *Bucket) PutInterface(key []byte, i interface{}) error {
	bs, err := json.Marshal(i)
	if err != nil {
		return err
	}
	return b.Put(key, bs)
}

func (b *Bucket) PutJSON(key []byte, m json.Marshaler) error {
	bs, err := m.MarshalJSON()
	if err != nil {
		return err
	}
	return b.Put(key, bs)
}

func (b *Bucket) PutBinary(key []byte, m encoding.BinaryMarshaler) error {
	bs, err := m.MarshalBinary()
	if err != nil {
		return err
	}
	return b.Put(key, bs)
}

func (b *Bucket) Root() interface{} {
	return b.bucket.Root()
}

func (b *Bucket) Sequence() uint64 {
	return b.bucket.Sequence()
}

func (b *Bucket) SetSequence(s uint64) {
	b.bucket.SetSequence(s)
}

func (b *Bucket) Stats() BucketStats {
	return BucketStats(b.bucket.Stats())
}

func (b *Bucket) Tx() *Tx {
	return &Tx{
		tx:      b.bucket.Tx(),
		options: b.options,
	}
}

func (b *Bucket) Writable() bool {
	return b.bucket.Writable()
}
