import collections
import inspect
import importlib
import io
import os
import sys
import types

def flatten(seq):
    for obj in seq:
        try:
            it = iter(obj)
        except TypeError:
            yield obj
        else:
            for o in flatten(obj):
                yield o

def sorted_mapping(mapping, **kwargs):
    out = collections.OrderedDict()
    
    for k in sorted(mapping.keys(), **kwargs):
        out[k] = mapping[k]
    
    return out

def order_classes(classes):
    classes = list(classes)
    ordered = list(flatten(inspect.getclasstree(classes)))
    
    ordered.reverse()
    
    for cls in list(ordered):
        if cls not in classes or ordered.count(cls) > 1:
            ordered.remove(cls)
    
    ordered.reverse()
    
    return ordered

def order_attributes(attrs):
    constants = {}
    functions = {}
    classes = {}
    
    for key, value in attrs.items():
        if isinstance(value, type(int.real)) or key in ("__abstractmethods__", "__base__", "__bases__", "__class__", "__dict__", "__dictoffset__", "__file__", "__flags__", "__itemsize__", "__module__", "__name__", "__package__", "__subclasses__", "__weakrefoffset__"):
            pass
        elif isinstance(value, (type, types.ClassType)):
            classes[key] = value
        elif isinstance(value, (types.FunctionType, types.BuiltinFunctionType, type(list.append), type(object.__init__), classmethod, staticmethod)):
            if not (key.startswith("__") and key.endswith("__")):
                functions[key] = value
        else:
            constants[key] = value
    
    constants = sorted_mapping(constants)
    functions = sorted_mapping(functions)
    classes = sorted_mapping(classes)
    classes_reverse = collections.OrderedDict((v, k) for k, v in classes.items())
    
    classes_ordered = collections.OrderedDict((classes_reverse[cls], cls) for cls in order_classes(classes.values()))
    
    return collections.OrderedDict(constants.items() + functions.items() + classes_ordered.items())

def stringify_constant(name, value):
    return u"{} = {!r}".format(name, value)

def stringify_function(name, value):
    return u"def {}(*args, **kwargs): pass".format(name)

def stringify_classmethod(name, value):
    return u"def {}(cls, *args, **kwargs): pass".format(name)

def stringify_method(name, value):
    return u"def {}(self, *args, **kwargs): pass".format(name)

def stringify_class(name, value):
    attrs = list((k, v) for k, v in inspect.getmembers(value) if not hasattr(super(value), k) or v != getattr(super(value), k))
    
    if type(value) not in [type(cls) for cls in value.__bases__]:
        attrs = [("__metaclass__", type(value))] + attrs
    
    body = u"\n".join(u"    " + line for line in stringify_attributes(order_attributes(collections.OrderedDict(attrs))).splitlines())
    
    return u"\nclass {}({}):\n{}".format(name, ", ".join(cls.__name__ for cls in value.__bases__), body)

def stringify_attributes(attrs):
    lines = []
    
    for k, v in attrs.items():
        if isinstance(v, (type, types.ClassType)):
            lines.append(stringify_class(k, v))
        elif isinstance(v, (types.MethodType, type(list.append), type(object.__init__))):
            lines.append(stringify_method(k, v))
        elif isinstance(v, classmethod):
            lines.append(stringify_classmethod(k, v))
        elif isinstance(v, (types.FunctionType, types.BuiltinFunctionType, staticmethod)):
            lines.append(stringify_function(k, v))
        else:
            lines.append(stringify_constant(k, v))
    
    return u"\n".join(lines)

if __name__ == "__main__":
    name = sys.argv[1]
    
    with io.open(os.extsep.join((name, u"py")), "w") as f:
        f.write(stringify_attributes(order_attributes(collections.OrderedDict(inspect.getmembers(importlib.import_module(name))))))