from tree import *

def parse_tree(file_string):
    # Read the tree string from the file
    try:
        with open(file_string, 'r') as file:
            input_string = file.read()
    except FileNotFoundError:
        print(f"Error: The file '{file_string}' was not found.")
        return
    except IOError:
        print(f"Error: There was an issue reading the file '{file_string}'.")
        return

    lines = input_string.strip().split('\n')
    tree = Tree()
    stack = []

    for line in lines:
        # Throw away the indents. All info is in the sequence of
        # nodes.
        line = line.lstrip(' |').rstrip()

        # print(stack)

        if '<=' in line:
            # This is a new inner node
            if ':' in line:
                # There is a right leaf node below the current node
                # Parse info of current node and leaf node
                dimension, rest = line.split('<=')
                # dimension = dimension.strip()[1:]  # Remove 'd' prefix
                dimension = dimension.strip()
                # dimension = int(dimension)
                threshold, rest = rest.split(':')
                threshold = float(threshold)
                class_label = rest.split('(')[0].strip()
                class_label = int(class_label)

                if not stack:
                    # Add root node
                    node = tree.insert_inner(dimension, threshold)
                    # Push new node to stack
                    stack.append([tree.root, False])
                    # Next child of root will be right child; we
                    # directly add the left one below.
                    
                    # Add leaf node
                    tree.insert_leaf(node, class_label = class_label)
                else:
                    # Not root node
                    parent, is_left = stack[-1]
                    if is_left:
                        # Next child of parent will be a right child,
                        # so update.
                        stack[-1][1] = False
                    else:
                        # We are currently adding the second child of
                        # parent. We won't need to refer to parent
                        # anymore.
                        stack.pop()
                    # Add inner node
                    node = tree.insert_inner(dimension, threshold, parent = parent, left = is_left)
                    # Push new node to stack
                    stack.append([node, False])
                    # Next child of node will be right child; we
                    # directly add the left one below.

                    # Add leaf node
                    tree.insert_leaf(node, class_label = class_label)
            else:
                # There is no right leaf below the current node
                # Parse info of current node
                dimension, threshold = line.split('<=')
                # dimension = dimension.strip()[1:]  # Remove 'd' prefix
                dimension = dimension.strip()
                # dimension = int(dimension)
                threshold = float(threshold)

                if not stack:
                    # Add root node
                    node = tree.insert_inner(dimension, threshold)
                    # Push new node to stack
                    stack.append([node, True]) # Next child of root will be left child
                else:
                    # Not root node
                    parent, is_left = stack[-1]
                    if is_left:
                        # Next child of parent will be a right child,
                        # so update.
                        stack[-1][1] = False
                    else:
                        # We are currently adding the second child of
                        # parent. We won't need to refer to parent
                        # anymore.
                        stack.pop()
                    # Add inner node.
                    node = tree.insert_inner(dimension, threshold, parent = parent, left = is_left)
                    # Push new node to stack
                    stack.append([node, True]) # Next child of node will be left child
        elif '>' in line:
            # We have already added this node if the data is
            # well-formed. Also, we have already added a left child
            # and updated the left/right indicator. We don't need to
            # do anything unless there is also a leaf node specified
            # in this line.

            # If data is well-formed, we should not have popped all
            # nodes yet.
            assert(stack)
            
            if ':' in line:
                # There is a (right) leaf node specified here.
                # Parse info of current node and leaf node.
                _, rest = line.split('>')
                # Throw away number of classified examples of each
                # class, just keep the class label of the leaf.
                class_label = rest.split(':')[1].split('(')[0].strip()
                class_label = int(class_label)

                # If the data is well-formed this is not the first
                # child that we add.
                parent, is_left = stack[-1]
                assert(not is_left)

                # Insert leaf.
                tree.insert_leaf(parent, class_label = class_label, left = False)
                # We have added the second child of parent, so pop parent.
                stack.pop()
    
    return tree

