class CFG_Node:
    __counter = 1

    def __init__(self, ast_node=None):
        self.ast_node = ast_node
        self.children = set()
        self.parents = set()
        self.label = None  # Optional label for the node
        
        self.id = CFG_Node.__counter
        CFG_Node.__counter += 1

    def get_children(self):
        return self.children

    def get_parents(self):
        return self.parents

    def add_child(self, child: 'CFG_Node', propagate=True):
        if propagate:
            child.parents.add(self)
        self.children.add(child)

    def add_parent(self, parent: 'CFG_Node', propagate=True):
        if propagate:
            parent.add_child(self)
        self.parents.add(parent)

    def remove_child(self, child: 'CFG_Node', propagate=True):
        if propagate:
            child.parents.remove(self)
        self.children.remove(child)

    def remove_parent(self, parent: 'CFG_Node', propagate=True):
        if propagate:
            parent.children.remove(self)
        self.parents.remove(parent)

    def __str__(self):
        if self.label:
            return f"CFG_Node({self.id}, label='{self.label}')"
        elif self.ast_node:
            return f"CFG_Node({self.id}, ast={type(self.ast_node).__name__})"
        else:
            return f"CFG_Node({self.id})"

    def __repr__(self):
        return self.__str__()


class CFG_START(CFG_Node):
    def dot_shape(self):
        return "box"

    def dot_label(self):
        return "START"


class CFG_END(CFG_Node):
    def dot_shape(self):
        return "box"

    def dot_label(self):
        return "END"


class CFG_DIAMOND(CFG_Node):
    def dot_shape(self):
        return "diamond"


class CFG_CALL(CFG_Node):
    def dot_shape(self):
        return "box"


class CFG_RETURN(CFG_Node):
    def dot_shape(self):
        return "box"
