// Copyright 2023 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0

package document

import "fmt"

type Tree[T any] struct {
	Level    int
	Children []*Tree[T]
	Parent   *Tree[T]
	Payload  T
}

func newTree[T any]() *Tree[T] {
	return &Tree[T]{}
}

func (t *Tree[T]) traverseAndInitLevel(level int) {
	t.Level = level
	for _, child := range t.Children {
		child.traverseAndInitLevel(level + 1)
	}

}

func (t *Tree[T]) addChild(child *Tree[T]) {
	t.Children = append(t.Children, child)
	child.Parent = t

	t.getRoot().traverseAndInitLevel(0)

}

func (t *Tree[T]) addChildren(children []*Tree[T]) {
	for _, child := range children {
		t.addChild(child)
		child.Parent = t
	}
	t.getRoot().traverseAndInitLevel(0)
}

func (t *Tree[T]) getChildren() []*Tree[T] {
	return t.Children
}

func (t *Tree[T]) getLevel() int {
	return t.Level
}

func (t *Tree[T]) getParent() *Tree[T] {
	return t.Parent
}

func (t *Tree[T]) getRoot() *Tree[T] {
	if t.Parent == nil {
		return t
	}
	return t.Parent.getRoot()
}

func (t *Tree[T]) getPayload() T {
	return t.Payload
}

func (t *Tree[T]) setPayload(payload T) {
	t.Payload = payload
}

func (t *Tree[T]) getPayloadAsString() string {
	return fmt.Sprintf("%v", t.Payload)
}

func (t *Tree[T]) appendAndGet() *Tree[T] {
	parent := t.getParent()

	if parent == nil {
		parent = t
	}

	n := newTree[T]()
	parent.addChild(n)
	return n
}

func (t *Tree[T]) up(level int) *Tree[T] {

	if level > t.getLevel() {
		panic("level>t.getLevel()")
	}

	if level == t.getLevel() {
		return t
	}

	return t.Parent.up(level)

}

func (t *Tree[T]) recursiveIterate(f func(*Tree[T])) {
	f(t)
	for _, child := range t.Children {
		child.recursiveIterate(f)
	}
}

func (t *Tree[T]) down(level int) *Tree[T] {

	if level < t.Level {
		panic("level < t.Level")
	}

	if t.Level == level {
		return t
	}

	if t.Children == nil {
		t.addChild(newTree[T]())
		return t.down(level)
	}

	return t.Children[len(t.Children)-1].down(level)
}

//func (t *Tree[T]) getSubTree() []Tree {
//	return t.getSubTreeWithLevel(t.Level)
//}
//
//func (t *Tree[T]) getSubTreeWithLevel(level int) []Tree {
//	if t.Level == level {
//		return []Tree{*t}
//	}
//	var result []Tree
//	for _, child := range t.Children {
//		result = append(result, child.getSubTreeWithLevel(level)...)
//	}
//	return result
//}