package bbolt

// Compact will create a copy of the source DB and in the destination DB. This may
// reclaim space that the source database no longer has use for. txMaxSize can be
// used to limit the transactions size of this process and may trigger intermittent
// commits. A value of zero will ignore transaction sizes.
// TODO: merge with: https://github.com/etcd-io/etcd/blob/b7f0f52a16dbf83f18ca1d803f7892d750366a94/mvcc/backend/backend.go#L349
func (,  *DB,  int64) error {
	// commit regularly, or we'll run out of memory for large datasets if using one transaction.
	var  int64
	,  := .Begin(true)
	if  != nil {
		return 
	}
	defer .Rollback()

	if  := walk(, func( [][]byte, ,  []byte,  uint64) error {
		// On each key/value, check if we have exceeded tx size.
		 := int64(len() + len())
		if + >  &&  != 0 {
			// Commit previous transaction.
			if  := .Commit();  != nil {
				return 
			}

			// Start new transaction.
			,  = .Begin(true)
			if  != nil {
				return 
			}
			 = 0
		}
		 += 

		// Create bucket on the root transaction if this is the first level.
		 := len()
		if  == 0 {
			,  := .CreateBucket()
			if  != nil {
				return 
			}
			if  := .SetSequence();  != nil {
				return 
			}
			return nil
		}

		// Create buckets on subsequent levels, if necessary.
		 := .Bucket([0])
		if  > 1 {
			for ,  := range [1:] {
				 = .Bucket()
			}
		}

		// Fill the entire page for best compaction.
		.FillPercent = 1.0

		// If there is no value then this is a bucket call.
		if  == nil {
			,  := .CreateBucket()
			if  != nil {
				return 
			}
			if  := .SetSequence();  != nil {
				return 
			}
			return nil
		}

		// Otherwise treat it as a key/value pair.
		return .Put(, )
	});  != nil {
		return 
	}

	return .Commit()
}

// walkFunc is the type of the function called for keys (buckets and "normal"
// values) discovered by Walk. keys is the list of keys to descend to the bucket
// owning the discovered key/value pair k/v.
type walkFunc func(keys [][]byte, k, v []byte, seq uint64) error

// walk walks recursively the bolt database db, calling walkFn for each key it finds.
func walk( *DB,  walkFunc) error {
	return .View(func( *Tx) error {
		return .ForEach(func( []byte,  *Bucket) error {
			return walkBucket(, nil, , nil, .Sequence(), )
		})
	})
}

func walkBucket( *Bucket,  [][]byte, ,  []byte,  uint64,  walkFunc) error {
	// Execute callback.
	if  := (, , , );  != nil {
		return 
	}

	// If this is not a bucket then stop.
	if  != nil {
		return nil
	}

	// Iterate over each child key/value.
	 = append(, )
	return .ForEach(func(,  []byte) error {
		if  == nil {
			 := .Bucket()
			return (, , , nil, .Sequence(), )
		}
		return (, , , , .Sequence(), )
	})
}