import traceback
from os.path import join

from conans.client.generators.cmake_find_package import CMakeFindPackageGenerator
from conans.client.generators.cmake_find_package_multi import CMakeFindPackageMultiGenerator
from conans.client.generators.compiler_args import CompilerArgsGenerator
from conans.client.generators.pkg_config import PkgConfigGenerator
from conans.errors import ConanException
from conans.util.env_reader import get_env
from conans.util.files import normalize, save
from .b2 import B2Generator
from .boostbuild import BoostBuildGenerator
from .cmake import CMakeGenerator
from .cmake_multi import CMakeMultiGenerator
from .cmake_paths import CMakePathsGenerator
from .deploy import DeployGenerator
from .gcc import GCCGenerator
from .json_generator import JsonGenerator
from .make import MakeGenerator
from .premake import PremakeGenerator
from .qbs import QbsGenerator
from .qmake import QmakeGenerator
from .scons import SConsGenerator
from .text import TXTGenerator
from .virtualbuildenv import VirtualBuildEnvGenerator
from .virtualenv import VirtualEnvGenerator
from .virtualenv_python import VirtualEnvPythonGenerator
from .virtualrunenv import VirtualRunEnvGenerator
from .visualstudio import VisualStudioGenerator
from .visualstudio_multi import VisualStudioMultiGenerator
from .visualstudiolegacy import VisualStudioLegacyGenerator
from .xcode import XCodeGenerator
from .ycm import YouCompleteMeGenerator


class _GeneratorManager(object):
    def __init__(self):
        self._generators = {}

    def add(self, name, generator_class, custom=False):
        if name not in self._generators or custom:
            self._generators[name] = generator_class

    @property
    def available(self):
        return list(self._generators.keys())

    def __contains__(self, name):
        return name in self._generators

    def __getitem__(self, key):
        return self._generators[key]


registered_generators = _GeneratorManager()

registered_generators.add("txt", TXTGenerator)
registered_generators.add("gcc", GCCGenerator)
registered_generators.add("compiler_args", CompilerArgsGenerator)
registered_generators.add("cmake", CMakeGenerator)
registered_generators.add("cmake_multi", CMakeMultiGenerator)
registered_generators.add("cmake_paths", CMakePathsGenerator)
registered_generators.add("cmake_find_package", CMakeFindPackageGenerator)
registered_generators.add("cmake_find_package_multi", CMakeFindPackageMultiGenerator)
registered_generators.add("qmake", QmakeGenerator)
registered_generators.add("qbs", QbsGenerator)
registered_generators.add("scons", SConsGenerator)
registered_generators.add("visual_studio", VisualStudioGenerator)
registered_generators.add("visual_studio_multi", VisualStudioMultiGenerator)
registered_generators.add("visual_studio_legacy", VisualStudioLegacyGenerator)
registered_generators.add("xcode", XCodeGenerator)
registered_generators.add("ycm", YouCompleteMeGenerator)
registered_generators.add("virtualenv", VirtualEnvGenerator)
registered_generators.add("virtualenv_python", VirtualEnvPythonGenerator)
registered_generators.add("virtualbuildenv", VirtualBuildEnvGenerator)
registered_generators.add("virtualrunenv", VirtualRunEnvGenerator)
registered_generators.add("boost-build", BoostBuildGenerator)
registered_generators.add("pkg_config", PkgConfigGenerator)
registered_generators.add("json", JsonGenerator)
registered_generators.add("b2", B2Generator)
registered_generators.add("premake", PremakeGenerator)
registered_generators.add("make", MakeGenerator)
registered_generators.add("deploy", DeployGenerator)


def write_generators(conanfile, path, output):
    """ produces auxiliary files, required to build a project or a package.
    """
    for generator_name in conanfile.generators:
        try:
            generator_class = registered_generators[generator_name]
        except KeyError:
            raise ConanException("Invalid generator '%s'. Available types: %s" %
                                 (generator_name, ", ".join(registered_generators.available)))
        try:
            generator = generator_class(conanfile)
        except TypeError:
            # To allow old-style generator packages to work (e.g. premake)
            output.warn("Generator %s failed with new __init__(), trying old one")
            generator = generator_class(conanfile.deps_cpp_info, conanfile.cpp_info)

        try:
            generator.output_path = path
            content = generator.content
            if isinstance(content, dict):
                if generator.filename:
                    output.warn("Generator %s is multifile. Property 'filename' not used"
                                % (generator_name,))
                for k, v in content.items():
                    v = normalize(v)
                    output.info("Generator %s created %s" % (generator_name, k))
                    save(join(path, k), v, only_if_modified=True)
            else:
                content = normalize(content)
                output.info("Generator %s created %s" % (generator_name, generator.filename))
                save(join(path, generator.filename), content, only_if_modified=True)
        except Exception as e:
            if get_env("CONAN_VERBOSE_TRACEBACK", False):
                output.error(traceback.format_exc())
            output.error("Generator %s(file:%s) failed\n%s"
                         % (generator_name, generator.filename, str(e)))
            raise ConanException(e)
