from rlextra.graphics.guiedit.mutils import getAttributes
from reportlab.graphics.shapes import Shape
from reportlab.graphics.widgetbase import Widget
import json, sys, inspect

def _dictDump(obj,_seen):
    R = obj.copy()
    for k in obj:
        if not isinstance(k,(str,unicode)):
            raise ValueError('dict has non-string key %r' % k)
        R[k] = _objDump(obj[k],_seen)
    return R

def _instDump(obj,_seen):
    # now we have to assume it has a __class__.__name__ etc etc
    R = {}
    R['__class__'] = str(obj.__class__)
    A = getAttributes(obj)
    AM = getattr(obj,'_attrMap',None)
    H = R['__help__'] = {}
    if A:
        R['__expandable__'] = True
        C = getattr(obj,'contents',None)
        if C:
            C = C[:]
            for k,v in A:
                if v in C:
                    C[C.index(v)] = k
            R['__contents__'] = C
        for k,v in A:
            R[k] = _objDump(v,_seen)
            if AM and k in AM:
                desc = AM[k].desc
                if desc:
                    H[k] = desc
    else:
        R.update(_dictDump(obj.__dict__,_seen))
    if isinstance(obj,(Shape,Widget)):
        try:
            bbox = obj.getBounds()
        except:
            bbox = 'unknown'
        R['__box__'] = bbox
    return R

def _objDump(obj,_seen=None):
    if _seen is None: _seen=[]
    if obj in _seen:
        return
    _seen.append(obj)
    try:
        if isinstance(obj,(int,float,str,unicode)) or obj is None:
            return obj

        if isinstance(obj,(list,tuple)):
            return [_objDump(x,_seen) for x in obj]

        if isinstance(obj,dict):
            return _dictDump(obj,_seen)
        
        if not hasattr(obj, '__class__'):
            return repr(obj)

        return _instDump(obj,_seen)
    finally:
        del _seen[-1]

def obj2json(obj):
    return json.dumps(_objDump(obj))

def _dictLoad(obj):
    R = {}
    for k,v in obj.iteritems():
        R[k] = _objLoad(v)
    return R

from pickle import _EmptyClass
def _makeInst(obj):
    P = obj.pop('__class__').split('.')
    mod = __import__('.'.join(P[:-1]),globals(),{},[''])
    I = _EmptyClass()
    I.__class__ = getattr(mod,P[-1])
    return I

def _instLoad(obj):
    inst = _makeInst(obj)
    x = obj.pop('__expandable__',None)
    C = obj.pop('__contents__',None)
    D = _dictLoad(obj)
    if not x:
        inst.__dict__ = D
    else:
        if C:
            A = inst.contents
            for c in C:
                v = D.pop(c,_instLoad)
                if v is _instLoad: continue
                A(v)
        for a,v in D.iteritems():
            setattr(inst,a,v)
    return inst

def _objLoad(obj):
    if isinstance(obj,(int,float,str,unicode)) or obj is None:
        return obj

    if isinstance(obj,list):
        return [_objLoad(x) for x in obj]
    
    if not obj.has_key('__expandable__'):
        return _dictLoad(obj)

    return _instLoad(obj)

def json2obj(j):
    return _objLoad(json.loads(j))

def getSample(modulePath):
    mod = __import__(modulePath,globals(),{},[''])
    C = [c for n,c in inspect.getmembers(mod,inspect.isclass) if c.__module__==modulePath]
    if len(C)==1: return C[0]()
    raise ValueError('Expected exactly one class definition in module %s found %d(%r)' % (modulePath,len(C),C))

if __name__=='__main__':
    import sys
    #e.g. python obj2json.py chart_galleries.quickcharts.QDrawing
    for fn in sys.argv[1:]:
        i = getSample(fn)
        j = obj2json(i)
        print fn, 'json',j
    #print 'obj',json2obj(j)
