from .abc import Expression, SequenceExpression
from .expression.value.valueexpr import VALUE


def declaration_to_dot(decl, fname):
    with open(fname, "w") as fo:
        return declaration_to_dot_stream(decl, fo)


def declaration_to_dot_stream(decl, ostream):
    nodes = set()
    links = set()

    ostream.write(
        'digraph G {\n\trankdir=LR;\n\tgraph [fontname = "helvetica"];\n\tnode [fontname = "helvetica"];\n\tedge [fontname = "helvetica"];\n'
    )

    for parent, key, obj in decl.walk():
        if parent is not None:
            nodes.add(parent)

        if isinstance(obj, Expression):
            nodes.add(obj)

            if (parent is not None) and (parent.Id, obj.Id, key) not in links:
                if obj in nodes:
                    style = "dashed"  # Link by alias
                else:
                    style = "filled"

                ostream.write(
                    '\t"{}" -> "{}" [style={},label="{}"];\n'.format(
                        parent.Id, obj.Id, style, key
                    )
                )
                links.add((parent.Id, obj.Id, key))

        else:
            vid = ".{}.val".format(parent.Id)
            if (parent.Id, vid, key) not in links:
                ostream.write(
                    '\t"{}" -> "{}" [label="{}"];\n'.format(parent.Id, vid, key)
                )
                ostream.write(
                    '\t"{}" [shape=box,label="<{}>\\n{}"]\n'.format(
                        vid, obj.__class__.__name__, obj
                    )
                )
                links.add((parent.Id, vid, key))

    for obj in nodes:
        if isinstance(obj, SequenceExpression):
            shape = "hexagon"
            addinfo = ""
        elif isinstance(obj, VALUE):
            shape = "box"
            addinfo = "\\n" + str(obj.Value)
        else:
            shape = "octagon"
            addinfo = ""

        ostream.write(
            '\t"{}" [label="<{}>\\n{}{}",shape="{}",style=filled,fillcolor=lightgray];\n'.format(
                obj.Id, obj.get_outlet_type(), obj.__class__.__name__, addinfo, shape
            )
        )

    ostream.write("}\n")
