import random

class Node:
    def __init__(self, data):
       self.children = [] 
       self.data = data

    def __str__(self) -> str:
        if str(self.data).isnumeric():
            return str(self.data)
        if self.data == '+' or self. data == '-':
            return f'({str(self.children[0])} {self.data} {str(self.children[1])})'
        return f'({str(self.children[0])} {self.data} {str(self.children[1])} + {str(self.children[2])})'

class Expression:
    def __init__(self, max_depth, min_seed, max_seed):
       self.max_depth = max_depth
       self.result = random.randint(min_seed, max_seed)
       self.root = Node(self.result) 
       self.leaves = [self.root]

    def grow_tree(self):
        funcs = [self.split_by_addition, self.split_by_subtraction, self.split_by_multiplication]
        for i in range(self.max_depth):
                temp_leaves = []
                for leaf in self.leaves:
                    if leaf.data > 3  and (i == 0 or bool(random.getrandbits(1))):
                        temp_leaves.extend(random.choice(funcs)(leaf))
                    else:
                        temp_leaves.append(leaf)
                self.leaves = temp_leaves 

    def split_by_addition(self, node):
        summand = random.randint(1, node.data)
        node.children = [Node(node.data-summand), Node(summand)]
        node.data = '+'

        return node.children
    
    def split_by_subtraction(self, node):
        summand = random.randint(1, node.data)
        node.children = [Node(node.data + summand), Node(summand)]
        node.data = '-'

        return node.children
    
    def split_by_multiplication(self, node):
        factor = random.randint(1, 9)
        node.children = [Node(node.data // factor), Node(factor)] 
        remainder = Node(node.data % factor)
        if remainder != 0:
            node.children.append(remainder)
        node.data = 'X'

        return node.children 
    
    def __str__(self) -> str:
        return str(self.root)
