package ktrie

import "unicode"

// KNode ...
type KNode struct {
	val   rune
	end   bool
	links []*KNode
}

// NewKNode ...
func NewKNode(val rune) *KNode {
	return &KNode{
		val:   val,
		links: make([]*KNode, 0),
	}
}

// Add ...
func (n *KNode) Add(rs []rune) {
	cur := n

	for k, v := range rs {
		link := cur.linkByVal(v)

		if link == nil {
			link = NewKNode(v)
			cur.links = append(cur.links, link)
		}

		if k == len(rs)-1 {
			link.end = true
		}

		cur = link
	}
}

// Find ...
func (n *KNode) Find(rs []rune) bool {
	cur := n

	for _, v := range rs {
		cur = cur.linkByVal(v)

		if cur == nil {
			return false
		}
	}

	return cur.end
}

// FindAsUpper ...
func (n *KNode) FindAsUpper(rs []rune) bool {
	cur := n

	for _, v := range rs {
		cur = cur.linkByVal(unicode.ToUpper(v))

		if cur == nil {
			return false
		}
	}

	return cur.end
}

func (n *KNode) linkByVal(val rune) *KNode {
	for _, v := range n.links {
		if v.val == val {
			return v
		}
	}

	return nil
}

// KTrie ...
type KTrie struct {
	*KNode

	maxDepth int
	minDepth int
}

// NewKTrie ...
func NewKTrie(data map[string]bool) (*KTrie, error) {
	n := NewKNode(0)

	maxDepth := 0
	minDepth := 9001

	for k := range data {
		rs := []rune(k)
		l := len(rs)

		n.Add(rs)

		if l > maxDepth {
			maxDepth = l
		}
		if l < minDepth {
			minDepth = l
		}
	}

	t := &KTrie{
		maxDepth: maxDepth,
		minDepth: minDepth,
		KNode:    n,
	}

	return t, nil
}

// MaxDepth ...
func (t *KTrie) MaxDepth() int {
	return t.maxDepth
}

// MinDepth ...
func (t *KTrie) MinDepth() int {
	return t.minDepth
}