# Automatically reload all of the modules in the current directory
# or its subdirectories every time you enter a command in IPython.
# Usage: from the IPython toplevel, do
# In [1]: import autoreload
# autoreload enabled
# Author: Andrew Owens
import os
import sys
import traceback
import imp


def relative_module(m):
    return hasattr(m, "__file__") and (
        (not m.__file__.startswith("/")) or m.__file__.startswith(os.getcwd())
    )


def reload_all():
    # Reloading __main__ is supposed to throw an error
    # For some reason in ipython I did not get an error and lost the
    # ability to send reload_all() to my ipython shell after making
    # changes.
    excludes = set(["__main__", "autoreload"])
    for name, m in list(sys.modules.items()):
        # todo: Check source modification time (use os.path.getmtime) to see if it has changed.
        if (
            m
            and relative_module(m)
            and (name not in excludes)
            and (not hasattr(m, "__no_autoreload__"))
        ):
            imp.reload(m)
            # superreload.superreload(m)


def ipython_autoreload_hook(self):
    try:
        reload_all()
    except Exception:
        traceback.print_exc()
        print("Reload error. Modules not reloaded.")


def enable():
    try:
        import IPython

        ip = IPython.get_ipython()
        prerun_hook_name = "pre_run_code_hook"
    except Exception:
        try:
            # IPython 0.11
            import IPython.core.ipapi as ipapi

            prerun_hook_name = "pre_run_code_hook"
            ip = ipapi.get()
        except Exception:
            # IPython 0.10.1
            import IPython.ipapi as ipapi

            prerun_hook_name = "pre_runcode_hook"
            ip = ipapi.get()
    ip.set_hook(prerun_hook_name, ipython_autoreload_hook)
    print("autoreload enabled")


# # taken from ipy_autoreload.py; should probably just use that instead
# def superreload(module, reload=reload):
#   """Enhanced version of the builtin reload function.
#   superreload replaces the class dictionary of every top-level
#   class in the module with the new one automatically,
#   as well as every function's code object.
#   """
#   module = reload(module)
#   # iterate over all objects and update them
#   count = 0
#   for name, new_obj in module.__dict__.items():
#     key = (module.__name__, name)
#     if _old_objects.has_key(key):
#       for old_obj in _old_objects[key]:
#         if type(new_obj) == types.ClassType:
#           old_obj.__dict__.update(new_obj.__dict__)
#           count += 1
#         elif type(new_obj) == types.FunctionType:
#           update_function(old_obj,
#                  new_obj,
#                  "func_code func_defaults func_doc".split())
#           count += 1
#         elif type(new_obj) == types.MethodType:
#           update_function(old_obj.im_func,
#                  new_obj.im_func,
#                  "func_code func_defaults func_doc".split())
#           count += 1
#     return module


# automatically enable when imported
__no_autoreload__ = True

# enable()
