// Copyright 2019 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package compact provides compact Merkle tree data structures.
package compact

import (
	"bytes"
	"errors"
	"fmt"
	"math/bits"
)

// HashFn computes an internal node's hash using the hashes of its child nodes.
type HashFn func(left, right []byte) []byte

// VisitFn visits the node with the specified ID and hash.
type VisitFn func(id NodeID, hash []byte)

// RangeFactory allows creating compact ranges with the specified hash
// function, which must not be nil, and must not be changed.
type RangeFactory struct {
	Hash HashFn
}

// NewRange creates a Range for [begin, end) with the given set of hashes. The
// hashes correspond to the roots of the minimal set of perfect sub-trees
// covering the [begin, end) leaves range, ordered left to right.
func (f *RangeFactory) NewRange(begin, end uint64, hashes [][]byte) (*Range, error) {
	if end < begin {
		return nil, fmt.Errorf("invalid range: end=%d, want >= %d", end, begin)
	}
	left, right := Decompose(begin, end)
	ones := bits.OnesCount64(left) + bits.OnesCount64(right)
	if ln := len(hashes); ln != ones {
		return nil, fmt.Errorf("invalid hashes: got %d values, want %d", ln, ones)
	}
	return &Range{f: f, begin: begin, end: end, hashes: hashes}, nil
}

// NewEmptyRange returns a new Range for an empty [begin, begin) range. The
// value of begin defines where the range will start growing from when entries
// are appended to it.
func (f *RangeFactory) NewEmptyRange(begin uint64) *Range {
	return &Range{f: f, begin: begin, end: begin}
}

// Range represents a compact Merkle tree range for leaf indices [begin, end).
//
// It contains the minimal set of perfect subtrees whose leaves comprise this
// range. The structure is efficiently mergeable with other compact ranges that
// share one of the endpoints with it.
//
// TODO(pavelkalinnikov): Add document with more details on how it works, and
// what it can be used for.
type Range struct {
	f      *RangeFactory
	begin  uint64
	end    uint64
	hashes [][]byte
}

// Begin returns the first index covered by the range (inclusive).
func (r *Range) Begin() uint64 {
	return r.begin
}

// End returns the last index covered by the range (exclusive).
func (r *Range) End() uint64 {
	return r.end
}

// Hashes returns sub-tree hashes corresponding to the minimal set of perfect
// sub-trees covering the [begin, end) range, ordered left to right.
func (r *Range) Hashes() [][]byte {
	return r.hashes
}

// Append extends the compact range by appending the passed in hash to it. It
// uses the tree hasher to calculate hashes of newly created nodes, and reports
// them through the visitor function (if non-nil).
func (r *Range) Append(hash []byte, visitor VisitFn) error {
	return r.appendImpl(r.end+1, hash, nil, visitor)
}

// AppendRange extends the compact range by merging in the other compact range
// from the right. It uses the tree hasher to calculate hashes of newly created
// nodes, and reports them through the visitor function (if non-nil).
func (r *Range) AppendRange(other *Range, visitor VisitFn) error {
	if other.f != r.f {
		return errors.New("incompatible ranges")
	}
	if got, want := other.begin, r.end; got != want {
		return fmt.Errorf("ranges are disjoint: other.begin=%d, want %d", got, want)
	}
	if len(other.hashes) == 0 { // The other range is empty, merging is trivial.
		return nil
	}
	return r.appendImpl(other.end, other.hashes[0], other.hashes[1:], visitor)
}

// GetRootHash returns the root hash of the Merkle tree represented by this
// compact range. Requires the range to start at index 0. If the range is
// empty, returns nil.
//
// If visitor is not nil, it is called with all "ephemeral" nodes (i.e. the
// ones rooting imperfect subtrees) along the right border of the tree.
func (r *Range) GetRootHash(visitor VisitFn) ([]byte, error) {
	if r.begin != 0 {
		return nil, fmt.Errorf("begin=%d, want 0", r.begin)
	}
	ln := len(r.hashes)
	if ln == 0 {
		return nil, nil
	}
	hash := r.hashes[ln-1]
	// All non-perfect subtree hashes along the right border of the tree
	// correspond to the parents of all perfect subtree nodes except the lowest
	// one (therefore the loop skips it).
	for i, size := ln-2, r.end; i >= 0; i-- {
		hash = r.f.Hash(r.hashes[i], hash)
		if visitor != nil {
			size &= size - 1                              // Delete the previous node.
			level := uint(bits.TrailingZeros64(size)) + 1 // Compute the parent level.
			index := size >> level                        // And its horizontal index.
			visitor(NewNodeID(level, index), hash)
		}
	}
	return hash, nil
}

// Equal compares two Ranges for equality.
func (r *Range) Equal(other *Range) bool {
	if r.f != other.f || r.begin != other.begin || r.end != other.end {
		return false
	}
	if len(r.hashes) != len(other.hashes) {
		return false
	}
	for i := range r.hashes {
		if !bytes.Equal(r.hashes[i], other.hashes[i]) {
			return false
		}
	}
	return true
}

// appendImpl extends the compact range by merging the [r.end, end) compact
// range into it. The other compact range is decomposed into a seed hash and
// all the other hashes (possibly none). The method uses the tree hasher to
// calculate hashes of newly created nodes, and reports them through the
// visitor function (if non-nil).
func (r *Range) appendImpl(end uint64, seed []byte, hashes [][]byte, visitor VisitFn) error {
	// Bits [low, high) of r.end encode the merge path, i.e. the sequence of node
	// merges that transforms the two compact ranges into one.
	low, high := getMergePath(r.begin, r.end, end)
	if high < low {
		high = low
	}
	index := r.end >> low
	// Now bits [0, high-low) of index encode the merge path.

	// The number of one bits in index is the number of nodes from the left range
	// that will be merged, and zero bits correspond to the nodes in the right
	// range. Below we make sure that both ranges have enough hashes, which can
	// be false only in case the data is corrupted in some way.
	ones := bits.OnesCount64(index & (1<<(high-low) - 1))
	if ln := len(r.hashes); ln < ones {
		return fmt.Errorf("corrupted lhs range: got %d hashes, want >= %d", ln, ones)
	}
	if ln, zeros := len(hashes), int(high-low)-ones; ln < zeros {
		return fmt.Errorf("corrupted rhs range: got %d hashes, want >= %d", ln+1, zeros+1)
	}

	// Some of the trailing nodes of the left compact range, and some of the
	// leading nodes of the right range, are sequentially merged with the seed,
	// according to the mask. All new nodes are reported through the visitor.
	idx1, idx2 := len(r.hashes), 0
	for h := low; h < high; h++ {
		if index&1 == 0 {
			seed = r.f.Hash(seed, hashes[idx2])
			idx2++
		} else {
			idx1--
			seed = r.f.Hash(r.hashes[idx1], seed)
		}
		index >>= 1
		if visitor != nil {
			visitor(NewNodeID(h+1, index), seed)
		}
	}

	// All nodes from both ranges that have not been merged are bundled together
	// with the "merged" seed node.
	r.hashes = append(append(r.hashes[:idx1], seed), hashes[idx2:]...)
	r.end = end
	return nil
}

// getMergePath returns the merging path between the compact range [begin, mid)
// and [mid, end). The path is represented as a range of bits within mid, with
// bit indices [low, high). A bit value of 1 on level i of mid means that the
// node on this level merges with the corresponding node in the left compact
// range, whereas 0 represents merging with the right compact range. If the
// path is empty then high <= low.
//
// The output is not specified if begin <= mid <= end doesn't hold, but the
// function never panics.
func getMergePath(begin, mid, end uint64) (uint, uint) {
	low := bits.TrailingZeros64(mid)
	high := 64
	if begin != 0 {
		high = bits.Len64(mid ^ (begin - 1))
	}
	if high2 := bits.Len64((mid - 1) ^ end); high2 < high {
		high = high2
	}
	return uint(low), uint(high - 1)
}

// Decompose splits the [begin, end) range into a minimal number of sub-ranges,
// each of which is of the form [m * 2^k, (m+1) * 2^k), i.e. of length 2^k, for
// some integers m, k >= 0.
//
// The sequence of sizes is returned encoded as bitmasks left and right, where:
//  - a 1 bit in a bitmask denotes a sub-range of the corresponding size 2^k
//  - left mask bits in LSB-to-MSB order encode the left part of the sequence
//  - right mask bits in MSB-to-LSB order encode the right part
//
// The corresponding values of m are not returned (they can be calculated from
// begin and the sub-range sizes).
//
// For example, (begin, end) values of (0b110, 0b11101) would indicate a
// sequence of tree sizes: 2,8; 8,4,1.
//
// The output is not specified if begin > end, but the function never panics.
func Decompose(begin, end uint64) (uint64, uint64) {
	// Special case, as the code below works only if begin != 0, or end < 2^63.
	if begin == 0 {
		return 0, end
	}
	xbegin := begin - 1
	// Find where paths to leaves #begin-1 and #end diverge, and mask the upper
	// bits away, as only the nodes strictly below this point are in the range.
	d := bits.Len64(xbegin^end) - 1
	mask := uint64(1)<<uint(d) - 1
	// The left part of the compact range consists of all nodes strictly below
	// and to the right from the path to leaf #begin-1, corresponding to zero
	// bits in the masked part of begin-1. Likewise, the right part consists of
	// nodes below and to the left from the path to leaf #end, corresponding to
	// ones in the masked part of end.
	return ^xbegin & mask, end & mask
}
