#! /usr/bin/env python
# -*- coding: utf-8 -*-
'''
Created on 2020/6/4 18:13:09

@author: LiuKuan
@copyright: Apache License, Version 2.0
'''
import inspect, contextlib, types
#===============================================================================
# 
#===============================================================================

__all__ = ['dump']

def dump_help(obj):
    with open('help-%s.txt' % _get_name(obj), 'w') as f, contextlib.redirect_stdout(f):
        help(obj)

# _obj = object()

def dump(obj, ignore_fields = tuple()):

    # 空对象,直接不处理
    if obj is None:
        print('None')
        return

    # allFunc = []
    # allAttr = []
    # allClass = []
    # allModules = []
    # allWMethWrapper = []
    # allMethod = []

    all_Attr = []
    all_Class = []
    all_ModuleType = []
    all_MethodType = []
    all_FunctionType = []
    all_WrapperDescriptorType = []
    all_MethodWrapperType = []
    all_MethodDescriptorType = []
    all_BuiltinFunctionType = []
    all_BuiltinMethodType = []

    print('\n# -----', type(obj))
    for item in _get_attr_pair(obj):

        k, v = item

        if k in ignore_fields: # 忽略的字段
            continue

        if k.startswith('_') : # 私有变量不访问
            if not k.startswith('__') or not k.endswith('__'):
                continue

        if isinstance(v, type):
            all_Class.append(item)
        elif isinstance(v, types.ModuleType):
            all_ModuleType.append(item)
        elif isinstance(v, types.FunctionType):
            all_FunctionType.append(item)
        elif isinstance(v, types.MethodType):
            all_MethodType.append(item)
        elif isinstance(v, types.WrapperDescriptorType):
            all_WrapperDescriptorType.append(item)
        elif isinstance(v, types.MethodWrapperType):
            all_MethodWrapperType.append(item)
        elif isinstance(v, types.MethodDescriptorType):
            all_MethodDescriptorType.append(item)
        elif isinstance(v, types.BuiltinFunctionType):
            all_BuiltinFunctionType.append(item)
        elif isinstance(v, types.BuiltinMethodType):
            all_BuiltinMethodType.append(item)
        else:
            all_Attr.append(item)

    if all_ModuleType:
        print('# module ---->')
        for k,v in all_ModuleType:
            print('\t# %s [%s]' %(k, _get_doc(v)) )

    if all_Class:
        print('# class ---->')
        for k,v in all_Class:
            print('\t# %s [%s]' %(k, _get_doc(v)) )

    if all_Attr:
        print('# attr ---->')
        for k,v in all_Attr:
            print('\t# %s[%s] [%s]' %(k, str(v).replace('\n', ' ')[0:100], _get_doc(v)) )

    if any([all_FunctionType, all_MethodType, all_WrapperDescriptorType, all_MethodWrapperType,
            all_MethodDescriptorType,  all_BuiltinFunctionType, all_BuiltinMethodType]):
        print('# func ---->')

    if all_FunctionType:
        # print('# func ---->')
        for k,v in all_FunctionType:
            print('\t# %s%s [%s]' %(k, _get_signature(v), _get_doc(v)) )

    if all_MethodType:
        # print('# method ---->')
        for k,v in all_MethodType:
            print('\t# %s%s [%s]' %(k, _get_signature(v), _get_doc(v)) )

    if all_WrapperDescriptorType:
        # print('# warpper descriptor ---->')
        for k,v in all_WrapperDescriptorType:
            print('\t# %s%s [%s]' %(k, _get_signature(v), _get_doc(v)) )

    if all_MethodWrapperType:
        # print('# method warpper ---->')
        for k,v in all_MethodWrapperType:
            print('\t# %s%s [%s]' %(k, _get_signature(v), _get_doc(v)) )

    if all_MethodDescriptorType:
        # print('# method descriptor ---->')
        for k,v in all_MethodDescriptorType:
            print('\t# %s%s [%s]' %(k, _get_signature(v), _get_doc(v)) )

    if all_BuiltinFunctionType:
        # print('# builtin func ---->')
        for k,v in all_BuiltinFunctionType:
            print('\t# %s%s [%s]' %(k, _get_signature(v), _get_doc(v)) )

    if all_BuiltinMethodType:
        # print('# builtin method ---->')
        for k,v in all_BuiltinMethodType:
            print('\t# %s%s [%s]' %(k, _get_signature(v), _get_doc(v)) )

    print()

def _get_doc(v):
    if isinstance(v, str):
        return  '`str`'
    if isinstance(v, bytes):
        return  '`bytes`'
    elif isinstance(v, int):
        return  '`integer`'
    elif isinstance(v, dict):
        return  '`dict`'
    elif isinstance(v, set):
        return  '`set`'
    elif isinstance(v, frozenset):
        return '`fronzenset`'
    elif isinstance(v, float):
        return '`float`'
    elif isinstance(v, tuple):
        return '`tuple`'
    elif isinstance(v, list):
        return '`list`'

    try:
        doc = inspect.getdoc(v)
        return doc.replace('\n', ' ')[0:250]
    except Exception as e:
        return  ''

def _get_signature(v):
    try:
        return str(inspect.signature(v))
    except: # 有些函数没有签名
        return "(...)"

def _get_name(v):
    try:
        name = v.__name__
    except Exception:
        try:
            name = v.__class__.__name__
        except Exception:
            name = 'none'
    return name

def _get_attr_pair(obj):
    '''
    获取可能的属性列表
    '''

    # 默认从 __all__获取属性
    datalist = []
    for k in getattr(obj, '__all__', tuple()):
        if hasattr(obj, k):
            datalist.append( (k, getattr(obj, k)) )

    # 获取所有属性
    if not datalist:
        datalist = inspect.getmembers(obj)

    datalist.sort(key=lambda kv: kv[0])

    # 把 __init__放在最前面
    for i,(k,v) in enumerate(datalist):
        if k == '__init__':
            del datalist[i]
            datalist.insert(0, (k, v))
            break

    return datalist
