from functools import wraps
from time import perf_counter
import inspect

DO_TIMING = False
DISPLAY_LESS_PROGRESS = False
timer_dict = {}
counter = 0


def time(f):
    @wraps(f)
    def wrap(*args, **kw):
        if DO_TIMING:
            # Run function with timing
            ts = perf_counter()
            result = f(*args, **kw)
            te = perf_counter()
            tt = te-ts

            # Get function name
            arg_names = inspect.getfullargspec(f)[0]
            if arg_names[0] == 'self' and DISPLAY_LESS_PROGRESS:
                return result
            elif arg_names[0] == 'self':
                method_name = type(args[0]).__name__ + '.' + f.__name__
            else:
                method_name = f.__name__

            # Record accumulative time in each function for analysis
            if method_name in timer_dict.keys():
                timer_dict[method_name] += tt
            else:
                timer_dict[method_name] = tt

            # If code is finished, display timing summary
            if method_name == "Evaluator.evaluate":
                print("")
                print("Timing analysis:")
                for key, value in timer_dict.items():
                    print('%-70s %2.4f sec' % (key, value))
            else:
                # Get function argument values for printing special arguments of interest
                arg_titles = ['tracker', 'seq', 'cls']
                arg_vals = []
                for i, a in enumerate(arg_names):
                    if a in arg_titles:
                        arg_vals.append(args[i])
                arg_text = '(' + ', '.join(arg_vals) + ')'

                # Display methods and functions with different indentation.
                if arg_names[0] == 'self':
                    print('%-74s %2.4f sec' % (' '*4 + method_name + arg_text, tt))
                elif arg_names[0] == 'test':
                    pass
                else:
                    global counter
                    counter += 1
                    print('%i %-70s %2.4f sec' % (counter, method_name + arg_text, tt))

            return result
        else:
            # If config["TIME_PROGRESS"] is false, or config["USE_PARALLEL"] is true, run functions normally without timing.
            return f(*args, **kw)
    return wrap
