package filetree

import (
	"github.com/sirupsen/logrus"
)

type TreeCacheKey struct {
	bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int
}

type TreeCache struct {
	refTrees []*FileTree
	cache    map[TreeCacheKey]*FileTree
}

func (cache *TreeCache) Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) *FileTree {
	key := TreeCacheKey{bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop}
	if value, exists := cache.cache[key]; exists {
		return value
	}

	value := cache.buildTree(key)
	cache.cache[key] = value
	return value
}

func (cache *TreeCache) buildTree(key TreeCacheKey) *FileTree {
	newTree := StackTreeRange(cache.refTrees, key.bottomTreeStart, key.bottomTreeStop)
	for idx := key.topTreeStart; idx <= key.topTreeStop; idx++ {
		err := newTree.CompareAndMark(cache.refTrees[idx])
		if err != nil {
			logrus.Errorf("unable to build tree: %+v", err)
		}
	}
	return newTree
}

func (cache *TreeCache) Build() {
	var bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int

	// case 1: layer compare (top tree SIZE is fixed (BUT floats forward), Bottom tree SIZE changes)
	for selectIdx := 0; selectIdx < len(cache.refTrees); selectIdx++ {
		bottomTreeStart = 0
		topTreeStop = selectIdx

		if selectIdx == 0 {
			bottomTreeStop = selectIdx
			topTreeStart = selectIdx
		} else {
			bottomTreeStop = selectIdx - 1
			topTreeStart = selectIdx
		}

		cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop)
	}

	// case 2: aggregated compare (bottom tree is ENTIRELY fixed, top tree SIZE changes)
	for selectIdx := 0; selectIdx < len(cache.refTrees); selectIdx++ {
		bottomTreeStart = 0
		topTreeStop = selectIdx
		if selectIdx == 0 {
			bottomTreeStop = selectIdx
			topTreeStart = selectIdx
		} else {
			bottomTreeStop = 0
			topTreeStart = 1
		}

		cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop)
	}
}

func NewFileTreeCache(refTrees []*FileTree) TreeCache {

	return TreeCache{
		refTrees: refTrees,
		cache:    make(map[TreeCacheKey]*FileTree),
	}
}
