package main

import (
	"encoding/json"
	"maps"
	"tests/cut_the_tree/dll"
)

type Node struct {
	ID       string  `json:"id"`
	Name     string  `json:"name"`
	Children []*Node `json:"children"`
}

type WeightedNode struct {
	parent string
	weight uint16
}

func (root Node) WeighingTreeWithRecursion() map[string]uint16 {
	weight := getNodeWeight(root.ID, root.Name)
	var res = make(map[string]uint16)
	for _, v := range root.Children {
		maps.Copy(res, v.WeighingTreeWithRecursion()) // Т.е. в go нет оптимизации хвостовой рекурсии, это породит неимоверное кол-во аллокаций.
		weight = weight + res[v.ID]
	}
	res[root.ID] = weight
	return res
}

func (root Node) WeighingTreeWithStack() map[string]uint16 {
	weight := getNodeWeight(root.ID, root.Name)
	counter := map[string]*WeightedNode{root.ID: {
		weight: weight,
	}}
	stack := []*Node{&root}
	for len(stack) > 0 {
		current := stack[len(stack)-1]
		stack = stack[:len(stack)-1]

		nodeWeight := getNodeWeight(current.ID, current.Name)
		counter[current.ID].weight = nodeWeight
		// Прибавляем вес всем родительским нодам до корня
		parentID := counter[current.ID].parent
		for {
			currentNode := counter[parentID]
			if parentID == "" {
				break
			}
			currentNode.weight = currentNode.weight + nodeWeight
			parentID = currentNode.parent
		}

		for _, child := range current.Children {
			counter[child.ID] = &WeightedNode{
				parent: current.ID,
			}
			stack = append(stack, child)
		}
	}

	var res = make(map[string]uint16)
	for k, v := range counter {
		res[k] = v.weight
	}

	return res
}

func (root Node) WeightingTreeWithDLL() map[string]uint16 {
	weight := getNodeWeight(root.ID, root.Name)
	counter := map[string]*WeightedNode{root.ID: {
		weight: weight,
	}}
	dllist := dll.NewList()
	dllist.PushFront(root)

	for dllist.Len() > 0 {
		current := dllist.Front()
		dllist.Remove(current)

		nodeWeight := getNodeWeight(current.Value.(Node).ID, current.Value.(Node).Name)
		counter[current.Value.(Node).ID].weight = nodeWeight
		// Прибавляем вес всем родительским нодам до корня
		parentID := counter[current.Value.(Node).ID].parent
		for {
			currentNode := counter[parentID]
			if parentID == "" {
				break
			}
			currentNode.weight = currentNode.weight + nodeWeight
			parentID = currentNode.parent
		}

		for _, child := range current.Value.(Node).Children {
			counter[child.ID] = &WeightedNode{
				parent: current.Value.(Node).ID,
			}
			dllist.PushFront(*child)
		}
	}

	var res = make(map[string]uint16)
	for k, v := range counter {
		res[k] = v.weight
	}

	return res
}

func (root Node) DecomposeTree(weights map[string]uint16, limit int) ([]*Node, error) {

	return nil, nil
}

func getNodeWeight(id string, name string) uint16 {
	nodeBytes, _ := json.Marshal(Node{
		ID:   id,
		Name: name,
	})
	return uint16(len(nodeBytes))
}