package ticktock

import (
	"testing"

	. "github.com/smartystreets/goconvey/convey"
	"gitlink.org.cn/cloudream/common/pkgs/bitmap"
	"gitlink.org.cn/cloudream/jcs-pub/client/types"
)

func newTreeTest(nodeBlocksMap []bitmap.Bitmap64) combinatorialTree {
	tree := combinatorialTree{
		blocksMaps:        make(map[int]bitmap.Bitmap64),
		stgIDToLocalStgID: make(map[types.UserSpaceID]int),
	}

	tree.nodes = make([]combinatorialTreeNode, (1 << len(nodeBlocksMap)))
	for id, mp := range nodeBlocksMap {
		tree.stgIDToLocalStgID[types.UserSpaceID(id)] = len(tree.localStgIDToStgID)
		tree.blocksMaps[len(tree.localStgIDToStgID)] = mp
		tree.localStgIDToStgID = append(tree.localStgIDToStgID, types.UserSpaceID(id))
	}

	tree.nodes[0].localHubID = -1
	index := 1
	tree.initNode(0, &tree.nodes[0], &index)

	return tree
}

func Test_iterCombBits(t *testing.T) {
	testcases := []struct {
		title          string
		width          int
		count          int
		expectedValues []int
	}{
		{
			title:          "1 of 4",
			width:          4,
			count:          1,
			expectedValues: []int{16, 8, 4, 2},
		},

		{
			title:          "2 of 4",
			width:          4,
			count:          2,
			expectedValues: []int{24, 20, 18, 12, 10, 6},
		},

		{
			title:          "3 of 4",
			width:          4,
			count:          3,
			expectedValues: []int{28, 26, 22, 14},
		},

		{
			title:          "4 of 4",
			width:          4,
			count:          4,
			expectedValues: []int{30},
		},
	}

	for _, test := range testcases {
		Convey(test.title, t, func() {
			var ret []int
			var t combinatorialTree
			t.iterCombBits(test.width, test.count, 0, func(i int) {
				ret = append(ret, i)
			})

			So(ret, ShouldResemble, test.expectedValues)
		})
	}
}

func Test_newCombinatorialTree(t *testing.T) {
	testcases := []struct {
		title                    string
		nodeBlocks               []bitmap.Bitmap64
		expectedTreeNodeLocalIDs []int
		expectedTreeNodeBitmaps  []int
	}{
		{
			title:                    "1个节点",
			nodeBlocks:               []bitmap.Bitmap64{1},
			expectedTreeNodeLocalIDs: []int{-1, 0},
			expectedTreeNodeBitmaps:  []int{0, 1},
		},
		{
			title:                    "2个节点",
			nodeBlocks:               []bitmap.Bitmap64{1, 0},
			expectedTreeNodeLocalIDs: []int{-1, 0, 1, 1},
			expectedTreeNodeBitmaps:  []int{0, 1, 1, 0},
		},
		{
			title:                    "4个节点",
			nodeBlocks:               []bitmap.Bitmap64{1, 2, 4, 8},
			expectedTreeNodeLocalIDs: []int{-1, 0, 1, 2, 3, 3, 2, 3, 3, 1, 2, 3, 3, 2, 3, 3},
			expectedTreeNodeBitmaps:  []int{0, 1, 3, 7, 15, 11, 5, 13, 9, 2, 6, 14, 10, 4, 12, 8},
		},
	}

	for _, test := range testcases {
		Convey(test.title, t, func() {
			t := newTreeTest(test.nodeBlocks)

			var localIDs []int
			var bitmaps []int
			for _, n := range t.nodes {
				localIDs = append(localIDs, n.localHubID)
				bitmaps = append(bitmaps, int(n.blocksBitmap))
			}

			So(localIDs, ShouldResemble, test.expectedTreeNodeLocalIDs)
			So(bitmaps, ShouldResemble, test.expectedTreeNodeBitmaps)
		})
	}
}

func Test_UpdateBitmap(t *testing.T) {
	testcases := []struct {
		title                   string
		nodeBlocks              []bitmap.Bitmap64
		updatedHubID            types.UserSpaceID
		updatedBitmap           bitmap.Bitmap64
		k                       int
		expectedTreeNodeBitmaps []int
	}{

		{
			title:                   "4个节点，更新但值不变",
			nodeBlocks:              []bitmap.Bitmap64{1, 2, 4, 8},
			updatedHubID:            types.UserSpaceID(0),
			updatedBitmap:           bitmap.Bitmap64(1),
			k:                       4,
			expectedTreeNodeBitmaps: []int{0, 1, 3, 7, 15, 11, 5, 13, 9, 2, 6, 14, 10, 4, 12, 8},
		},

		{
			title:                   "4个节点，更新0",
			nodeBlocks:              []bitmap.Bitmap64{1, 2, 4, 8},
			updatedHubID:            types.UserSpaceID(0),
			updatedBitmap:           bitmap.Bitmap64(2),
			k:                       4,
			expectedTreeNodeBitmaps: []int{0, 2, 2, 6, 14, 10, 6, 14, 10, 2, 6, 14, 10, 4, 12, 8},
		},

		{
			title:                   "4个节点，更新1",
			nodeBlocks:              []bitmap.Bitmap64{1, 2, 4, 8},
			updatedHubID:            types.UserSpaceID(1),
			updatedBitmap:           bitmap.Bitmap64(1),
			k:                       4,
			expectedTreeNodeBitmaps: []int{0, 1, 1, 5, 13, 9, 5, 13, 9, 1, 5, 13, 9, 4, 12, 8},
		},

		{
			title:                   "4个节点，更新2",
			nodeBlocks:              []bitmap.Bitmap64{1, 2, 4, 8},
			updatedHubID:            types.UserSpaceID(2),
			updatedBitmap:           bitmap.Bitmap64(1),
			k:                       4,
			expectedTreeNodeBitmaps: []int{0, 1, 3, 3, 11, 11, 1, 9, 9, 2, 3, 11, 10, 1, 9, 8},
		},

		{
			title:                   "4个节点，更新3",
			nodeBlocks:              []bitmap.Bitmap64{1, 2, 4, 8},
			updatedHubID:            types.UserSpaceID(3),
			updatedBitmap:           bitmap.Bitmap64(1),
			k:                       4,
			expectedTreeNodeBitmaps: []int{0, 1, 3, 7, 7, 3, 5, 5, 1, 2, 6, 7, 3, 4, 5, 1},
		},

		{
			title:                   "4个节点，k<4，更新0，0之前没有k个块，现在拥有",
			nodeBlocks:              []bitmap.Bitmap64{1, 2, 4, 8},
			updatedHubID:            types.UserSpaceID(0),
			updatedBitmap:           bitmap.Bitmap64(3),
			k:                       2,
			expectedTreeNodeBitmaps: []int{0, 3, 3, 7, 15, 11, 5, 13, 9, 2, 6, 14, 10, 4, 12, 8},
		},
		{
			title:                   "4个节点，k<4，更新0，0之前有k个块，现在没有",
			nodeBlocks:              []bitmap.Bitmap64{3, 4, 0, 0},
			updatedHubID:            types.UserSpaceID(0),
			updatedBitmap:           bitmap.Bitmap64(0),
			k:                       2,
			expectedTreeNodeBitmaps: []int{0, 0, 4, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0},
		},
	}

	for _, test := range testcases {
		Convey(test.title, t, func() {
			t := newTreeTest(test.nodeBlocks)
			t.UpdateBitmap(test.updatedHubID, test.updatedBitmap, test.k)

			var bitmaps []int
			for _, n := range t.nodes {
				bitmaps = append(bitmaps, int(n.blocksBitmap))
			}

			So(bitmaps, ShouldResemble, test.expectedTreeNodeBitmaps)
		})
	}
}

func Test_FindKBlocksMaxDepth(t *testing.T) {
	testcases := []struct {
		title      string
		nodeBlocks []bitmap.Bitmap64
		k          int
		expected   int
	}{
		{
			title:      "每个节点各有一个块",
			nodeBlocks: []bitmap.Bitmap64{1, 2, 4, 8},
			k:          2,
			expected:   2,
		},
		{
			title:      "所有节点加起来块数不足",
			nodeBlocks: []bitmap.Bitmap64{1, 1, 1, 1},
			k:          2,
			expected:   4,
		},
		{
			title:      "不同节点有相同块",
			nodeBlocks: []bitmap.Bitmap64{1, 1, 2, 4},
			k:          2,
			expected:   3,
		},
		{
			title:      "一个节点就拥有所有块",
			nodeBlocks: []bitmap.Bitmap64{3, 6, 12, 24},
			k:          2,
			expected:   1,
		},
		{
			title:      "只有一块，且只在某一个节点1",
			nodeBlocks: []bitmap.Bitmap64{1, 0},
			k:          1,
			expected:   2,
		},
		{
			title:      "只有一块，且只在某一个节点2",
			nodeBlocks: []bitmap.Bitmap64{0, 1},
			k:          1,
			expected:   2,
		},
	}

	for _, test := range testcases {
		Convey(test.title, t, func() {
			t := newTreeTest(test.nodeBlocks)
			ret := t.FindKBlocksMaxDepth(test.k)
			So(ret, ShouldResemble, test.expected)
		})
	}
}
