# -*- coding: cp932 -*-
#
# オブジェクトや名前空間の参照関係を可視化するライブラリ
# showNamespace(g)とshowNamespaceAsGraph(g)は引数としてglobals()、
# showObj(o)とshowObjAsGraph(o)はその他のオブジェクトが渡されることを前提にしています

DEFAULT_GLOBALS=globals()
DEFAULT_GLOBALS["DEFAULT_GLOBALS"]=DEFAULT_GLOBALS

# グラフでの可視化に使うGraphVisのdot.exeの場所
# *AsGraphを使う場合は環境に合わせて書き換える必要があります。

dotpos=r"C:\PROGRA~1\ATT\Graphviz\bin\dot.exe"
# 出力するフォーマット
dottyp="png"

def purifyNamespace(g):
    """引数に渡された辞書を名前空間とみなして、初学者が当惑しそうなものを隠す"""
    for k in g.keys():
        # 元からあるものをとりのぞく
        if DEFAULT_GLOBALS.has_key(k):
            if DEFAULT_GLOBALS[k]==g[k]:
                continue
        # _で始まるものをとりのぞく(オプショナルにするべき？)
        if k[0]=="_":
            continue
        # 自分と思しきものをとりのぞく
        if k=="pyObjVis":
            continue
        yield (k, g[k])

def wait():
    """表示後の待機"""
    raw_input("改行キーを押すと続行します...")
    
def showNamespace(g):
    objects=[]
    for (key, value) in purifyNamespace(g):
        print "変数名「%s」:" % key
        print indent(str(factory(value, objects)))
    wait()
    
def showNamespaceAsGraph(g):
    tmp={}
    for (key, value) in purifyNamespace(g):
        tmp[key]=value
    showObjAsGraph(tmp)

def showObj(o):
    objects=[]
    result=factory(o, objects)
    print result
    wait()

def showObjAsGraph(o):
    objects=[]
    result=factory(o, objects)
    fo=open("tmp.dot","w")
    fo.write("""digraph structs {
    graph [rankdir = "LR"];
    node [shape=record];""")    
    for o in objects:
        fo.write("%s\n" % o.getDotVertex())
    for o in objects:
        for e in o.edges:
            fo.write("%s\n" % e)
    fo.write("}")
    fo.close()
    import os

    os.system(dotpos+" -T%s tmp.dot -o tmp.%s"%(dottyp,dottyp))
    os.system("tmp.%s"%dottyp)
    wait()

def indent(str):
    """与えられた文字列をインデントする"""
    return "\n".join(["\t%s"%line for line in str.split("\n")])

# タイプの日本語表記
typJa={
    "str":"文字列",
    "int":"整数",
    "float":"実数",
    "tuple":"タプル",
    "list":"リスト",
    "dict":"辞書",
    "function":"関数",
    "builtin_function_or_method":"組み込み関数",
    "module":"モジュール"
}

def factory(value, objects):
    typ=type(value).__name__
    if typ=="str":
        cls=SingleLine
    elif typ=="int":
        cls=SingleLine
    elif typ=="float":
        cls=SingleLine
    elif typ=="tuple":
        cls=ListLike
    elif typ=="list":
        cls=ListLike
    elif typ=="dict":
        cls=DictLike
    elif typ=="function":
        cls=FuncLike
    elif typ=="builtin_function_or_method":
        cls=FuncLike
    elif typ=="module":
        cls=FuncLike
    else:
        cls=SingleLine
    obj = cls(value, typ, objects)
    return obj
    
class ObjWrapper:
    def __init__(self, value, typ, objects):
        objects.append(self)
        self.id=len(objects)
        self.value=value
        self.type=typ

        strExpr="タイプ:"
        if typJa.has_key(typ):
            strExpr+="%s(%s)"%(typJa[typ], typ)
        else:
            strExpr+=typ
        self.strExpr=[strExpr]

        self.vertex = ['obj%d [shape=record, label="' % self.id]
        self.vertex.append("<f0>%s" % typ)

        self.edges=[]

    def __str__(self):
        return "\n".join(self.strExpr)
    def getDotVertex(self):
        return "".join(self.vertex)
    def getDotEdges(self):
        return self.edges

class SingleLine(ObjWrapper):
    def __init__(self, value, typ, objects):
        ObjWrapper.__init__(self, value, typ, objects)
        self.strExpr.append("値:%s"%repr(value))
        self.vertex.append('|%s"];'%repr(value))

class ListLike(ObjWrapper):
    def __init__(self, value, typ, objects):
        ObjWrapper.__init__(self, value, typ, objects)

        for i in range(len(value)):
            self.strExpr.append("%d番目の値:"%i)
            for o in objects:
                if o.value is value[i]:
                    self.strExpr.append(indent("**木構造ではないため表示できません**"))
                    break
            else:
                o=factory(value[i], objects)
                self.strExpr.append(indent(str(o)))
            self.vertex.append("|<f%d>%dth"%(i+1, i))
            self.edges.append("obj%d:f%d -> obj%d;"%(self.id, i+1, o.id))
        self.vertex.append('"];')
            

        
class DictLike(ObjWrapper):
    def __init__(self, value, typ, objects):
        ObjWrapper.__init__(self, value, typ, objects)

        for i in range(len(value)):
            k=value.keys()[i]
            self.strExpr.append("キー「%s」に対応する値:"%k)

            for o in objects:
                if o.value is value[k]:
                    self.strExpr.append(indent("**木構造ではないため表示できません**"))
                    break
            else:
                o=factory(value[k], objects)
                self.strExpr.append(indent(str(o)))
            self.vertex.append("|<f%d>key: %s"%(i+1, repr(k)))
            self.edges.append("obj%d:f%d -> obj%d;"%(self.id, i+1, o.id))
        self.vertex.append('"];')

class FuncLike(ObjWrapper):
    def __init__(self, value, typ, objects):
        ObjWrapper.__init__(self, value, typ, objects)
        self.strExpr.append("名前:%s"%value.__name__)
        self.vertex.append('|name:%s"];'%value.__name__)

