// Copyright (c) 2015-2016 The btcsuite developers
// Copyright (c) 2016-2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package tickettreap

import (
	"encoding/binary"
	"reflect"
	"testing"
)

// serializeUint32 returns the big-endian encoding of the passed uint32.
func serializeUint32(ui uint32) []byte {
	var ret [4]byte
	binary.BigEndian.PutUint32(ret[:], ui)
	return ret[:]
}

// uint32ToKey converts the provided uint32 to a treap Key.
func uint32ToKey(ui uint32) Key {
	var key Key
	binary.BigEndian.PutUint32(key[len(key)-4:], ui)
	return key
}

// TestSizeValues are correct.
func TestSizeValues(t *testing.T) {
	var node treapNode
	sizeOfNode := reflect.TypeOf(node).Size()
	if sizeOfNode != nodeFieldsSize {
		t.Errorf("Sizeof(treapNode) != nodeFieldsSize - %v != %v", sizeOfNode, nodeFieldsSize)
	} else {
		t.Log("All good nodeFieldsSize")
	}

	var v Value
	sizeOfValue := reflect.TypeOf(v).Size()
	if sizeOfValue != nodeValueSize {
		t.Errorf("Sizeof(Value) != nodeValueSize - %v != %v", sizeOfValue, nodeValueSize)
	} else {
		t.Log("All good nodeValueSize")
	}
}

// TestParentStack ensures the treapParentStack functionality works as intended.
func TestParentStack(t *testing.T) {
	t.Parallel()

	tests := []struct {
		numNodes int
	}{
		{numNodes: 1},
		{numNodes: staticDepth},
		{numNodes: staticDepth + 1}, // Test dynamic code paths
	}

testLoop:
	for i, test := range tests {
		nodes := make([]*treapNode, 0, test.numNodes)
		for j := 0; j < test.numNodes; j++ {
			key := uint32ToKey(uint32(j))
			value := Value{Height: uint32(j)}
			node := newTreapNode(key, &value, 0)
			nodes = append(nodes, node)
		}

		// Push all of the nodes onto the parent stack while testing
		// various stack properties.
		stack := &parentStack{}
		for j, node := range nodes {
			stack.Push(node)

			// Ensure the stack length is the expected value.
			if stack.Len() != j+1 {
				t.Errorf("Len #%d (%d): unexpected stack "+
					"length - got %d, want %d", i, j,
					stack.Len(), j+1)
				continue testLoop
			}

			// Ensure the node at each index is the expected one.
			for k := 0; k <= j; k++ {
				atNode := stack.At(j - k)
				if !reflect.DeepEqual(atNode, nodes[k]) {
					t.Errorf("At #%d (%d): mismatched node "+
						"- got %v, want %v", i, j-k,
						atNode, nodes[k])
					continue testLoop
				}
			}
		}

		// Ensure each popped node is the expected one.
		for j := 0; j < len(nodes); j++ {
			node := stack.Pop()
			expected := nodes[len(nodes)-j-1]
			if !reflect.DeepEqual(node, expected) {
				t.Errorf("At #%d (%d): mismatched node - "+
					"got %v, want %v", i, j, node, expected)
				continue testLoop
			}
		}

		// Ensure the stack is now empty.
		if stack.Len() != 0 {
			t.Errorf("Len #%d: stack is not empty - got %d", i,
				stack.Len())
			continue testLoop
		}

		// Ensure attempting to retrieve a node at an index beyond the
		// stack's length returns nil.
		if node := stack.At(2); node != nil {
			t.Errorf("At #%d: did not give back nil - got %v", i,
				node)
			continue testLoop
		}

		// Ensure attempting to pop a node from an empty stack returns
		// nil.
		if node := stack.Pop(); node != nil {
			t.Errorf("Pop #%d: did not give back nil - got %v", i,
				node)
			continue testLoop
		}
	}
}
