from random import *
from random import *
from scanner import *
from graphviz import Graph
class Node:
    children = []
    def __init__(self, info):
        self.info = info
    def __delete__(self, instance):
        for child in self.children:
            del child


class Parser:
    global_pointer = int()
    scan = Scanner()
    counter = 0
    graph = Graph(comment='The Round Table')
    # Constructor
    def __init__(self):
        self.global_pointer = 0

    def returnAsNode(self, buffer = 0):
        return Node(self.scan.tokens[self.global_pointer + buffer][0])

    # Functions ( Procedures )  for the Grammar rules

    def match(self, token, option):
        # option da 7aeb2a 7agten bool y3ne 0 lw el token 7aqarnw bl value, 1 -> type
        # Example1 match( '(' , 0)
        # Example2 match( 'number' , 1)
        prev_pointer = self.global_pointer
        if(self.global_pointer<len(self.scan.tokens) and token == self.scan.tokens[self.global_pointer][option]):
            self.global_pointer += 1
            return True
        self.global_pointer = prev_pointer
        return False

    def factor(self):
        prev_pointer = self.global_pointer
        if (self.match('number',1)):
            return self.returnAsNode(-1)
        if (self.match('identifier', 1)):
            return self.returnAsNode(-1)
        # match '(' and exp and ')'
        if (self.match('(',0)):
            node = self.expression()
            if node is not None and self.match(')',0):
                return node
        self.global_pointer = prev_pointer
        return None

    def term(self):
        prev_pointer = self.global_pointer
        parent_node = self.factor()
        if(parent_node  is not None):
            current_node = None
            current_parent = None
            while (self.match('*', 0) or self.match('/', 0)  ):
                node_1 = self.returnAsNode(-1)
                right_node = self.factor()
                if(right_node is None):
                    self.global_pointer = prev_pointer
                    return None
                if current_node is None:
                    node_1.children = [parent_node, right_node]
                    parent_node = node_1
                else:
                    node_1.children = [current_parent.children[1], right_node]
                    current_parent.children[1] = node_1
                current_node = right_node
                current_parent = node_1
            return parent_node
        return None

    def simple_exp(self):
        prev_pointer = self.global_pointer
        parent_node = self.term()
        if (parent_node is not None):
            current_node = None
            current_parent = None
            while (self.match('+', 0) or self.match('-', 0)):
                node_1 = self.returnAsNode(-1)
                right_node = self.term()
                if (right_node is None):
                    self.global_pointer = prev_pointer
                    return None
                if current_node is None:
                    node_1.children = [parent_node, right_node]
                    parent_node = node_1
                else:
                    node_1.children = [current_parent.children[1], right_node]
                    current_parent.children[1] = node_1
                current_node = right_node
                current_parent = node_1
            return parent_node
        return None

    def expression(self):
        prev_pointer = self.global_pointer
        left_node = self.simple_exp()
        if left_node is not None and (self.match('<', 0) or self.match('=', 0)):
            parent_node = self.returnAsNode(-1)
            right_node = self.simple_exp()
            if right_node is not None:
                parent_node.children = [left_node, right_node]
                return parent_node
        self.global_pointer = prev_pointer
        node = self.simple_exp()
        if(node is not None):
            return node
        self.global_pointer = prev_pointer
        return None

    def if_statement(self):
        prev_pointer = self.global_pointer
        if self.match('if',0):
            parent_node = self.returnAsNode(-1)
            child_node_1 = self.expression()
            if child_node_1 is not None:
                if self.match('then',0):
                    child_node_2 = self.stmt_seq()
                    if child_node_2 is not None:
                        if self.match('end',0):
                            parent_node.children = [child_node_1, child_node_2]
                            return parent_node
        self.global_pointer = prev_pointer
        if self.match('if',0):
            parent_node = self.returnAsNode(-1)
            child_node_1 = self.expression()
            if child_node_1 is not None:
                if self.match('then',0):
                    child_node_2 = self.stmt_seq()
                    if child_node_2 is not None:
                        if self.match('else',0):
                            child_node_3 = self.stmt_seq()
                            if child_node_3 is not None:
                                if self.match('end', 0):
                                    parent_node.children = [child_node_1, child_node_2, child_node_3]
                                    return parent_node
        self.global_pointer = prev_pointer
        return None

    def repeat_statement(self):
        prev_pointer = self.global_pointer
        if self.match('repeat', 0):
            parent_node = self.returnAsNode(-1)
            left_node = self.stmt_seq()
            if left_node is not None:
                if self.match('until', 0):
                    right_node = self.expression()
                    if right_node is not None:
                        parent_node.children = [left_node, right_node]
                        return parent_node
        self.global_pointer = prev_pointer
        return None

    def assign_statement(self):
        prev_pointer = self.global_pointer
        if(self.match('identifier', 1)):
            left_node = self.returnAsNode(-1)
            if self.match(':=', 0):
                parent_node = self.returnAsNode(-1)
                right_node = self.expression()
                if right_node is not None:
                    parent_node.children = [left_node, right_node]
                    return parent_node
        self.global_pointer = prev_pointer
        return None

    def read_statement(self):
        prev_pointer = self.global_pointer
        if(self.match('read',0) and self.match('identifier',1)):
            node = self.returnAsNode(-2)
            node.children = [self.returnAsNode(-1)]
            return node
        self.global_pointer = prev_pointer
        return None

    def write_statement(self):
        prev_pointer = self.global_pointer
        if (self.match('write', 0) and self.expression()):
            node = self.returnAsNode(-2)
            node.children = [self.returnAsNode(-1)]
            return node
        self.global_pointer = prev_pointer
        return None

    def statement(self):
        prev_pointer = self.global_pointer
        node = self.if_statement()
        if(node is not None):
            return node
        self.global_pointer = prev_pointer
        node = self.repeat_statement()
        if (node is not None):
            return node
        self.global_pointer = prev_pointer
        node = self.write_statement()
        if (node is not None):
            return node
        self.global_pointer = prev_pointer
        node = self.read_statement()
        if (node is not None):
            return node
        self.global_pointer = prev_pointer
        node = self.assign_statement()
        if (node is not None):
            return node
        self.global_pointer = prev_pointer
        return None

    def stmt_seq(self):
        prev_pointer = self.global_pointer
        parent_node = self.statement()
        if(parent_node  is not None):
            flag = True
            while(self.match(';',0)):
                node = self.statement()
                if(node is None):
                    self.global_pointer = prev_pointer
                    return None
                if flag:
                    temp = Node('statements')
                    temp.children = [parent_node, node]
                    parent_node = temp
                    flag = False
                else:
                    parent_node.children.append(node)
            if self.global_pointer != len(self.scan.tokens):
                if (self.scan.tokens[self.global_pointer][0] == 'end'
                    or
                    self.scan.tokens[self.global_pointer][0] == 'else'
                    or
                    self.scan.tokens[self.global_pointer][0] == 'until'):
                    pass
                else:
                    return  None
            return parent_node
        return None

    def program(self):
        node = self.stmt_seq()
        if(node is not None):
            self.draw(node)
            print(self.graph.source)  # doctest: +NORMALIZE_WHITESPACE
            self.graph.render('test-output/round-table'+str(uniform(0,100))+'.gv', view=True)
            return True
        return False

    def draw(self, node):
        index = str(self.counter)
        self.graph.node(index, node.info)
        self.counter += 1
        for child in node.children:
            self.graph.edge(self.draw(child),index)
        return index