import yaml
import glob
import os
from os import path
from conan.tools.files import copy


def relocate_directory(conanfile, dep, dst_package_folder, src_subdir, dst_subdir, *, attrib = None, copyfiles = False):
    src_fulldir = path.join(dep.folders.package_folder, src_subdir)
    dst_fulldir = path.join(dst_package_folder, dst_subdir)

    if copyfiles:
        # find symlinks and replace them with regular files
        # (libpng likes to put symlink into package output, and I build Linux artefacts in NTFS shared directory)
        for fn in glob.glob(path.join(src_fulldir, '**'), recursive = True):
            if path.islink(fn):
                print("resolving symlink: %s" % fn)
                data = open(fn, 'rb').read()
                os.unlink(fn)
                open(fn, 'wb').write(data)
        # copy the files physically
        copy(conanfile, "*", src = src_fulldir, dst = dst_fulldir)

    if attrib:
        # update conan's cpp_info attribute
        # this is necessary so that conan generators (CMakeDeps / MSBuildDeps) use paths to the copies
        def fix_attrib(value):
            pathlist = getattr(value, attrib)
            for i,src_infodir in enumerate(pathlist):
                reldir = path.relpath(src_infodir, src_fulldir)
                if reldir == '.':
                    reldir = ''
                assert not reldir.startswith('.'), f"Attrib {attrib} contains entry {src_infodir} not inside {src_fulldir}"
                dst_infodir = path.join(dst_fulldir, reldir)
                pathlist[i] = dst_infodir

        # this works for single-component packages like minizip
        fix_attrib(dep.cpp_info)
        # this works for multi-component packages like ffmpeg
        for key,value in dep.cpp_info.components.items():
            fix_attrib(value)


def deploy(graph, output_folder, **kwargs):
    conanfile = graph.root.conanfile
    rootdestdir = path.join(output_folder, "..", "tdm_deploy")
    conanfile.output.info(f"TheDarkMod custom deployer to {rootdestdir}")
    with open("packages.yml", "r") as f:
        doc = yaml.safe_load(f)

    for dep in conanfile.dependencies.values():
        pkgname = dep.ref.name
        destdir = path.join(rootdestdir, pkgname)

        if dep.ref.version != 'system':     # skip copying for opengl, xorg, etc.
            yamlpkg = None
            for yamldep in doc["packages"]:
                if yamldep["name"] == pkgname:
                    yamlpkg = yamldep
            # make sure to declare all packages in yaml so that we know e.g. how to treat headers
            assert yamlpkg is not None, f"Package {pkgname} is missing from packages.yml"

            relocate_directory(conanfile, dep, destdir, 'licenses', 'licenses', copyfiles = True)

            if yamlpkg.get("incltype", "default") == "os":
                inclsubdir = str(dep.info.settings.os).lower()  # separate include subdirectories per os
            else:
                inclsubdir = ""                                 # single include directory for all cases
            relocate_directory(conanfile, dep, destdir, 'include', f'include/{inclsubdir}', attrib = 'includedirs', copyfiles = True)

            if dep.info.settings.get_safe("arch"):  # skip header-only packages
                if dep.info.settings.compiler.get_safe("runtime_type") == "Debug" or dep.info.settings.get_safe("build_type") == "Debug":
                    buildtype = "dbg"
                else:
                    buildtype = "rel"
                binsubdir = str(dep.info.settings.os).lower()
                binsubdir += {'x86' : '32', 'x86_64' : '64'}[str(dep.info.settings.arch)]
                binsubdir += '_' + buildtype
                relocate_directory(conanfile, dep, destdir, 'lib', f'lib/{binsubdir}', attrib = 'libdirs', copyfiles = True)
                relocate_directory(conanfile, dep, destdir, 'bin', f'bin/{binsubdir}', attrib = 'bindirs', copyfiles = True)

        # update global directory for the package
        dep.set_deploy_folder(destdir)
