import line_profiler
import cProfile
import pstats
import functools
import time
import contextlib

from django.conf import settings


def profile_func(sortby="cumtime", limit=20):

    def decorator(func):

        @functools.wraps(func)
        def wrap(*args, **kwargs):
            p = cProfile.Profile()
            p.enable()
            try:
                return func(*args, **kwargs)
            finally:
                p.disable()
                s = pstats.Stats(p).sort_stats(sortby)
                s.print_stats(limit)

        return wrap

    return decorator


@contextlib.contextmanager
def profile_context(sortby="cumtime", limit=10):
    p = cProfile.Profile()
    p.enable()
    try:
        yield
    finally:
        p.disable()
        s = pstats.Stats(p).sort_stats(sortby)
        s.print_stats(limit)


def profile(view):
    """
    profile decorator.
    :param view:
    :return:
    """
    def wrapped(*args, **kwargs):
        prof = line_profiler.LineProfiler()
        prof.add_function(view)
        with prof:
            resp = view(*args, **kwargs)
        prof.print_stats()
        return resp
    return wrapped


class ProfileMetaclass(type):

    def __new__(mcs, name, base, dct):
        """
        profile meta class.
        :param name:
        :param base:
        :param dct:
        :return:
        """
        if settings.PROFILE:
            for key, value in dct.items():
                if callable(value) and not key.startswith('__init') and not key.startswith('format'):
                    dct[key] = profile(value)
        return super(ProfileMetaclass, mcs).__new__(mcs, name, base, dct)
