class VirtApt(object):
    def __init__(self, xml):

        # pylint: disable=too-many-statements
        self.xml = xml

        arch = xml.text("project/buildimage/arch", key="arch")
        suite = xml.text("project/suite")

        self.basefs = TmpdirFilesystem()
        self.initialize_dirs()

        create_apt_prefs(self.xml, self.basefs)

        mirror = self.xml.create_apt_sources_list(build_sources=True,
                                                  initvm=False)
        self.basefs.write_file("etc/apt/sources.list", 0o644, mirror)

        self.setup_gpg()
        self.import_keys()

        apt_pkg.config.set("APT::Architecture", arch)
        apt_pkg.config.set("APT::Architectures", arch)
        apt_pkg.config.set("Acquire::http::Proxy::127.0.0.1", "DIRECT")
        apt_pkg.config.set("APT::Install-Recommends", "0")
        apt_pkg.config.set("Dir::Etc", self.basefs.fname('/'))
        apt_pkg.config.set("Dir::Etc::Trusted",
                           self.basefs.fname('/etc/apt/trusted.gpg'))
        apt_pkg.config.set("Dir::Etc::TrustedParts",
                           self.basefs.fname('/etc/apt/trusted.gpg.d'))
        apt_pkg.config.set("APT::Cache-Limit", "0")
        apt_pkg.config.set("APT::Cache-Start", "32505856")
        apt_pkg.config.set("APT::Cache-Grow", "2097152")
        apt_pkg.config.set("Dir::State", self.basefs.fname("state"))
        apt_pkg.config.set("Dir::State::status",
                           self.basefs.fname("state/status"))
        apt_pkg.config.set("Dir::Cache", self.basefs.fname("cache"))
        apt_pkg.config.set("Dir::Cache::archives",
                           self.basefs.fname("cache/archives"))
        apt_pkg.config.set("Dir::Etc", self.basefs.fname("etc/apt"))
        apt_pkg.config.set("Dir::Log", self.basefs.fname("log"))
        if self.xml.has('project/noauth'):
            apt_pkg.config.set("APT::Get::AllowUnauthenticated", "1")
            apt_pkg.config.set("Acquire::AllowInsecureRepositories", "1")
        else:
            apt_pkg.config.set("APT::Get::AllowUnauthenticated", "0")
            apt_pkg.config.set("Acquire::AllowInsecureRepositories", "0")

        apt_pkg.init_system()

        self.source = apt_pkg.SourceList()
        self.source.read_main_list()
        self.cache = apt_pkg.Cache()
        try:
            self.cache.update(self, self.source)
        except BaseException as e:
            print(e)

        apt_pkg.config.set("APT::Default-Release", suite)

        self.cache = apt_pkg.Cache()
        try:
            self.cache.update(self, self.source)
        except BaseException as e:
            print(e)

        try:
            self.depcache = apt_pkg.DepCache(self.cache)
            prefs_name = self.basefs.fname("/etc/apt/preferences")
            self.depcache.read_pinfile(prefs_name)
        except BaseException as e:
            print(e)

        self.downloads = {}
        self.acquire = apt_pkg.Acquire(self)

    def add_key(self, key):
        cmd = 'echo "%s" > %s' % (key, self.basefs.fname("tmp/key.pub"))
        clean = 'rm -f %s' % self.basefs.fname("tmp/key.pub")
        system(cmd)
        system('fakeroot apt-key --keyring "%s" add "%s"' %
               (self.basefs.fname('/etc/apt/trusted.gpg'),
                self.basefs.fname("tmp/key.pub")))
        system(clean)

    def import_keys(self):
        if self.xml.has('project/mirror/url-list'):
            # Should we use self.xml.prj.has("noauth")???
            #
            # If so, this is related to issue #220 -
            # https://github.com/Linutronix/elbe/issues/220
            #
            # I could make a none global 'noauth' flag for mirrors
            for url in self.xml.node('project/mirror/url-list'):
                if url.has('raw-key'):
                    key = "\n".join(
                        line.strip(" \t")
                        for line in url.text('raw-key').splitlines()[1:-1])
                    self.add_key(key)

    def start(self):
        pass

    def stop(self):
        pass

    @staticmethod
    def pulse(_obj):
        return True

    def initialize_dirs(self):
        self.basefs.mkdir_p("cache/archives/partial")
        self.basefs.mkdir_p("etc/apt/preferences.d")
        self.basefs.mkdir_p("etc/apt/trusted.gpg.d")
        self.basefs.mkdir_p("db")
        self.basefs.mkdir_p("log")
        self.basefs.mkdir_p("state/lists/partial")
        self.basefs.mkdir_p("tmp")
        self.basefs.touch_file("state/status")

    def setup_gpg(self):
        ring_path = self.basefs.fname("etc/apt/trusted.gpg")
        if not os.path.isdir("/etc/apt/trusted.gpg.d"):
            print("/etc/apt/trusted.gpg.d doesn't exist")
            print("apt-get install debian-archive-keyring may "
                  "fix this problem")
            sys.exit(20)

        if os.path.exists("/etc/apt/trusted.gpg"):
            system('cp /etc/apt/trusted.gpg "%s"' % ring_path)

        trustkeys = os.listdir("/etc/apt/trusted.gpg.d")
        for key in trustkeys:
            system('cp "/etc/apt/trusted.gpg.d/%s" "%s"' %
                   (key, ring_path + '.d'))

    def mark_install(self, pkgname):
        self.depcache.mark_install(self.cache[pkgname])

    def marked_install(self, pkgname):
        return self.depcache.marked_install(self.cache[pkgname])

    def get_candidate_ver(self, pkgname):
        return self.depcache.get_candidate_ver(self.cache[pkgname]).ver_str

    def has_pkg(self, pkgname):
        return pkgname in self.cache

    def mark_pkg_download(self, pkgname):
        pkg = self.cache[pkgname]
        c = self.depcache.get_candidate_ver(pkg)

        r = apt_pkg.PackageRecords(self.cache)
        r.lookup(c.file_list[0])

        x = self.source.find_index(c.file_list[0][0])
        uri = x.archive_uri(r.filename)
        hashval = str(r.hashes.find('SHA256'))

        acq = apt_pkg.AcquireFile(self.acquire,
                                  uri,
                                  hash=hashval,
                                  size=c.size,
                                  descr=r.long_desc,
                                  short_descr=r.short_desc,
                                  destdir=self.basefs.fname('/cache/archives'))
        self.downloads[pkgname] = acq

    def do_downloads(self):
        res = self.acquire.run()
        print(res)

    def get_downloaded_files(self):
        ret = []
        for _, d in self.downloads.items():
            if d.complete:
                ret.append(d.destfile)
            else:
                print('incomplete download "%s"' % d.desc_uri)

        return ret

    def get_downloaded_pkg(self, pkgname):
        d = self.downloads[pkgname]

        if not d.complete:
            print('incomplete download "%s"' % d.desc_uri)
            raise KeyError

        return d.destfile

    def get_uri(self, target_pkg, incl_deps=False):

        d = apt_pkg.DepCache(self.cache)

        if not incl_deps:
            return [lookup_uri(self, d, target_pkg)]

        deps = [lookup_uri(self, d, target_pkg)]
        togo = [target_pkg]
        while togo:
            pp = togo.pop()
            try:
                pkg = self.cache[pp]
                c = d.get_candidate_ver(pkg)
            except KeyError:
                pkg = None
                c = None
            if not c:
                # pylint: disable=E1133
                for p in self.cache.packages:
                    for x in p.provides_list:
                        if pp == x[0]:
                            pkg = self.cache[x[2].parent_pkg.name]
                            c = d.get_candidate_ver(pkg)
            if not c:
                print("couldnt get candidate: %s" % pkg)
            else:
                for p in getdeps(c):
                    if [y for y in deps if y[0] == p]:
                        continue
                    if p != target_pkg and p == pp:
                        continue
                    deps.append(lookup_uri(self, d, p))
                    togo.append(p)

        return list(set(deps))
class TestCopyFilelist(unittest.TestCase):
    def setUp(self):
        self.src = TmpdirFilesystem()
        self.dst = TmpdirFilesystem()

    def tearDown(self):
        del self.src
        del self.dst

    def test_usrmerge_abs(self):

        self.src.mkdir_p('/usr/bin')

        # this will link to /usr/bin in the host RFS,
        # when no special logic is applied.
        self.src.symlink('/usr/bin', '/bin')

        self.src.write_file('/bin/bla', 0o644, 'bla')

        copy_filelist(self.src, ['/bin/bla'], self.dst)

        # We should now have the same content from /SRC/usr/bin/bla in
        # /DST/usr/bin/bla
        self.assertEqual(self.src.read_file('/usr/bin/bla'),
                         self.dst.read_file('/usr/bin/bla'))

    def test_usrmerge_rel(self):

        self.src.mkdir_p('/usr/bin')

        # create a proper relative path, that should
        # work fine from inside.
        self.src.symlink('usr/bin', '/bin')

        self.src.write_file('/bin/bla', 0o644, 'bla')

        copy_filelist(self.src, ['/bin/bla'], self.dst)

        # We should now have the same content from /SRC/usr/bin/bla in
        # /DST/usr/bin/bla
        self.assertEqual(self.src.read_file('/usr/bin/bla'),
                         self.dst.read_file('/usr/bin/bla'))

    def test_deeplinks(self):

        self.src.mkdir_p('/a/b/c')

        # c <- /a/b/d
        self.src.symlink('c', '/a/b/d')

        # This write into /a/b/c/bla (c instead of d)
        self.src.write_file('/a/b/d/bla', 0o644, 'bla')

        copy_filelist(self.src, ['/a/b/d/bla'], self.dst)

        # We should now have the same content from /SRC/a/b/c/bla in
        # /DST/a/b/c/bla
        self.assertEqual(self.src.read_file('/a/b/c//bla'),
                         self.dst.read_file('/a/b/c/bla'))

    def test_multilinks(self):

        self.src.mkdir_p('/a')

        # a <- b
        # ../b <- /a/c
        self.src.symlink('a', '/b')
        self.src.symlink('../b', '/a/c')

        # This write into /a/bla
        self.src.write_file('a/c/bla', 0o644, 'bla')

        copy_filelist(self.src, ['/a/c/bla'], self.dst)

        # We should now have the content from /SRC/a/bla in /DST/a/bla
        self.assertEqual(self.src.read_file('/a/bla'),
                         self.dst.read_file('/a/bla'))

    @unittest.expectedFailure
    def test_badfile(self):
        # This should throw a CommandError
        copy_filelist(self.src, ['/doesnt/exist'], self.dst)