package org.limine.snapper.objects;

import org.limine.snapper.formats.limine8.LimineKey;
import org.limine.snapper.processes.MacroReplacer;
import org.limine.snapper.processes.Utility;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

public class TreeNode implements Comparable<TreeNode> {

    public final List<String> configLines;
    public final LinkedList<TreeNode> nodes;
    public final int depth;
    public final String parentNodeName;
    public String name, cleanName;
    public boolean enableLineBreak;
    private EntryOptions options;

    public TreeNode(String name, int depth, String parentNodeName) {
        this.name = name;
        this.cleanName = Utility.cleanName(name);
        this.configLines = new ArrayList<>();
        this.nodes = new LinkedList<>();
        this.depth = depth;
        this.parentNodeName = parentNodeName;
        this.enableLineBreak = false;
        this.options = null;
    }

    public void addConfigLine(String line) {
        this.configLines.add(line);
    }

    public void addNode(TreeNode node) {
        this.nodes.add(node);
    }

    public TreeNode findOsNode(String machineID, String osName, int selectedDepth) {
        TreeNode node = findNodeByMachineID(machineID, selectedDepth);
        if (node == null) {
            return findNodeByCleanName(osName, machineID, selectedDepth);
        } else {
            return node;
        }
    }

    public TreeNode findNodeByCleanName(String nodeCleanName, String machineID, int selectedDepth) {
        if (nodeCleanName == null) {
            return null;
        }
        if (nodeCleanName.isEmpty()) {
            return null;
        }
        if (0 >= selectedDepth) {
            if (Objects.equals(this.cleanName, nodeCleanName)) {
                if (this.options != null && this.options.machineID != null) {
                    if (this.options.machineID.isEmpty() || this.options.machineID.equals(machineID)) {
                        return this;
                    } else {
                        return null;
                    }
                } else {
                    return this;
                }
            }
            return null;
        }
        selectedDepth--;
        for (TreeNode node : nodes) {
            TreeNode result = node.findNodeByCleanName(nodeCleanName, machineID, selectedDepth);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    public TreeNode findNodeByName(String nodeName, int selectedDepth) {
        if (nodeName == null) {
            return null;
        }
        if (nodeName.isEmpty()) {
            return null;
        }
        if (0 >= selectedDepth) {
            if (this.name.trim().equals(nodeName)) {
                return this;
            }
            return null;
        }
        selectedDepth--;
        for (TreeNode node : nodes) {
            TreeNode result = node.findNodeByName(nodeName, selectedDepth);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    public TreeNode findNodeByMachineID(String machineID, int selectedDepth) {
        if (0 >= selectedDepth) {
            if (machineID.equals(this.getOrCreateOptions().machineID)) {
                return this;
            }
            return null;
        }
        selectedDepth--;
        for (TreeNode node : nodes) {
            TreeNode result = node.findNodeByMachineID(machineID, selectedDepth);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    public boolean searchNodeByName(String nodeName, int selectedDepth) {
        if (0 >= selectedDepth) {
            return this.name.trim().equals(nodeName);
        }
        selectedDepth--;
        for (TreeNode node : nodes) {
            if (node.searchNodeByName(nodeName, selectedDepth)) {
                return true;
            }
        }
        return false;
    }

    private EntryOptions getOrCreateOptions() {
        if (this.options == null) {
            this.options = new EntryOptions();
            for (String line : this.configLines) {
                if (line.trim().startsWith(LimineKey.COMMENT.toString())) {
                    this.options.parseComment(MacroReplacer.replaceMacros(line));
                }
            }
        }
        return options;
    }

    public void printTree(String prefix) {
        System.out.println(prefix + name + " (Depth: " + depth + ", Parent: " + parentNodeName + ")");
        for (String line : configLines) {
            System.out.println(prefix + line);
        }
        for (TreeNode node : nodes) {
            node.printTree(prefix);
        }
    }

    @Override
    public String toString() {
        return "TreeNode{" + "name='" + name + '\'' + ", configBlock=" + configLines + ", depth=" + depth + ", parentNodeName='" + parentNodeName + '\'' + '}';
    }


    @Override
    public int compareTo(TreeNode other) {
        return this.cleanName.compareTo(other.cleanName);
    }

}
