import os

from conan.errors import ConanException


class _PatternEntry(object):

    def __init__(self):
        self.include = []
        self.lib = []
        self.bin = []
        self.src = []
        self.build = []
        self.res = []
        self.framework = []


class _Patterns(object):

    def __init__(self):
        self.source = _PatternEntry()
        self.build = _PatternEntry()


class AutoPackager(object):

    def __init__(self, conanfile):
        """
        :param conanfile: The current recipe object. Always use ``self``.
        """
        self._conanfile = conanfile
        self.patterns = _Patterns()

        self.patterns.source.include = ["*.h", "*.hpp", "*.hxx"]
        self.patterns.source.lib = []
        self.patterns.source.bin = []

        self.patterns.build.include = ["*.h", "*.hpp", "*.hxx"]
        self.patterns.build.lib = ["*.so", "*.so.*", "*.a", "*.lib", "*.dylib"]
        self.patterns.build.bin = ["*.exe", "*.dll"]
        conanfile.output.warning("AutoPackager is **** deprecated ****", warn_tag="deprecated")
        conanfile.output.warning("AutoPackager **** will be removed ****", warn_tag="deprecated")
        conanfile.output.warning("Use explicit copy() calls instead", warn_tag="deprecated")

    def run(self):
        cf = self._conanfile
        # Check that the components declared in source/build are in package
        cnames = set(cf.cpp.source.components)
        cnames = cnames.union(set(cf.cpp.build.components))
        if cnames.difference(set(cf.cpp.package.components)):
            # TODO: Raise? Warning? Ignore?
            raise ConanException("There are components declared in cpp.source.components"
                                 " or in cpp.build.components that are not declared in"
                                 " cpp.package.components")
        if cnames:
            for cname in cnames:
                if cname in cf.cpp.source.components:
                    self._package_cppinfo("source", cf.cpp.source.components[cname],
                                          cf.cpp.package.components[cname])
                if cname in cf.cpp.build.components:
                    self._package_cppinfo("build", cf.cpp.build.components[cname],
                                          cf.cpp.package.components[cname])
        else:  # No components declared
            self._package_cppinfo("source", cf.cpp.source, cf.cpp.package)
            self._package_cppinfo("build", cf.cpp.build, cf.cpp.package)

    def _package_cppinfo(self, origin_name, origin_cppinfo, dest_cppinfo):
        """
        @param origin_name: one from ["source", "build"]
        @param origin_cppinfo: cpp_info object of an origin (can be a component cppinfo too)
        @param dest_cppinfo: cpp_info object of the package or a component from package
        """

        patterns_var = getattr(self.patterns, origin_name)
        base_folder = getattr(self._conanfile, "{}_folder".format(origin_name))

        for var in ["include", "lib", "bin", "framework", "src", "build", "res"]:
            dirs_var_name = "{}dirs".format(var)
            origin_paths = getattr(origin_cppinfo, dirs_var_name)
            if not origin_paths:
                continue
            patterns = getattr(patterns_var, var)
            destinations = getattr(dest_cppinfo, dirs_var_name)

            if not destinations:  # For example: Not declared "includedirs" in package.cpp_info
                continue

            if len(destinations) > 1:
                # Check if there is only one possible destination at package, otherwise the
                # copy would need to be done manually
                err_msg = "The package has more than 1 cpp_info.{}, cannot package automatically"
                raise ConanException(err_msg.format(dirs_var_name))

            dst_folder = os.path.join(self._conanfile.folders.base_package, destinations[0])
            from conan.tools.files import copy
            for d in origin_paths:
                src_folder = os.path.join(base_folder, d)
                for pattern in patterns:
                    copy(self._conanfile, pattern, src_folder, dst_folder)
