import importlib.abc, importlib.util
import os.path
import zipimport


class MetaPathFinder(importlib.abc.MetaPathFinder):

    def add_file(self, filename, modulename=None):
        if not hasattr(self, "_registered_files"):
            self._registered_files = {}

        if not os.path.exists(filename):
            raise RuntimeError("'%s' must be exist!" % filename)

        if modulename is None:
            modulename = os.path.splitext(os.path.basename(filename))[0]

        unique_modulename = modulename
        uniqifier = 1
        while unique_modulename in self._registered_files:
            unique_modulename = "%s%d" % (modulename, uniqifier)
            uniqifier += 1

        self._registered_files[unique_modulename] = filename
        return unique_modulename

    def find_spec(self, fullname, path, target=None):
        if not hasattr(self, "_registered_files"):
            return None

        if fullname in self._registered_files:
            modulename = fullname
            filename = self._registered_files[fullname]

            if os.path.splitext(os.path.basename(filename))[1].lower() == ".zip":
                loader = zipimport.zipimporter(filename)
                spec = importlib.util.spec_from_loader(modulename, loader)
            else:
                spec = importlib.util.spec_from_file_location(modulename, filename)
            return spec

        return None


_path_finder_instance = MetaPathFinder()


def add_file(filename, modulename=None):
    global _path_finder_instance
    return _path_finder_instance.add_file(filename, modulename)


def _run_once(f):
    def func(*args, **kwargs):
        if not f.has_run:
            f.has_run = True
            return f(*args, **kwargs)

    f.has_run = False
    return func


@_run_once
def install_pathfinder():
    global _path_finder_instance

    import sys
    sys.meta_path.insert(0, _path_finder_instance)
