"""Expand dependencies in a list of seed packages."""

# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
#               Canonical Ltd.
#
# Germinate is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any
# later version.
#
# Germinate is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Germinate; see the file COPYING.  If not, see
# <https://www.gnu.org/licenses/>.

import copy
import logging
import optparse
import os
import shutil
import sys

import germinate.archive
import germinate.defaults
import germinate.version
from germinate.germinator import Germinator
from germinate.log import germinate_logging
from germinate.seeds import Seed, SeedError, SeedStructure, SeedVcs


def check_seed_vcs(option, opt, value):
    if value == "none":  # or just omit the option
        return None
    elif value == "auto":
        return SeedVcs.AUTO
    elif value == "bzr":
        return SeedVcs.BZR
    elif value == "git":
        return SeedVcs.GIT
    else:
        raise optparse.OptionValueError(
            "option %s: unrecognised VCS value: %s" % (opt, value)
        )


class GerminateOption(optparse.Option):
    """A custom option type for use with optparse."""

    TYPES = optparse.Option.TYPES + ("vcs",)
    TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
    TYPE_CHECKER["vcs"] = check_seed_vcs


def parse_options(argv):
    parser = optparse.OptionParser(
        prog="germinate",
        version="%prog " + germinate.version.VERSION,
        option_class=GerminateOption,
    )
    parser.add_option(
        "-v",
        "--verbose",
        dest="verbose",
        action="store_true",
        default=False,
        help="be more verbose when processing seeds",
    )
    parser.add_option(
        "-S",
        "--seed-source",
        dest="seeds",
        metavar="SOURCE",
        help="fetch seeds from SOURCE (default: %s)"
        % germinate.defaults.seeds,
    )
    parser.add_option(
        "-s",
        "--seed-dist",
        dest="release",
        metavar="DIST",
        default=germinate.defaults.release,
        help="fetch seeds for distribution DIST " "(default: %default)",
    )
    parser.add_option(
        "-m",
        "--mirror",
        dest="mirrors",
        action="append",
        metavar="MIRROR",
        help="get package lists from MIRROR (default: %s)"
        % germinate.defaults.mirror,
    )
    parser.add_option(
        "--source-mirror",
        dest="source_mirrors",
        action="append",
        metavar="MIRROR",
        help="get source package lists from mirror "
        "(default: value of --mirror)",
    )
    parser.add_option(
        "-d",
        "--dist",
        dest="dist",
        default=germinate.defaults.dist,
        help="operate on distribution DIST (default: %default)",
    )
    parser.add_option(
        "-a",
        "--arch",
        dest="arch",
        default=germinate.defaults.arch,
        help="operate on architecture ARCH (default: %default)",
    )
    parser.add_option(
        "-c",
        "--components",
        dest="components",
        default="main,restricted",
        metavar="COMPS",
        help="operate on components COMPS (default: %default)",
    )
    parser.add_option("-C", "--apt-config", dest="apt_config")
    parser.add_option(
        "--vcs",
        dest="vcs",
        action="store",
        type="vcs",
        help="version control system to use "
        "(auto, bzr, git; defaults to none)",
    )
    parser.add_option(
        "--bzr",
        dest="vcs",
        action="store_const",
        const=SeedVcs.BZR,
        help="fetch seeds using bzr (requires bzr to be "
        "installed; use --vcs=bzr instead)",
    )
    parser.add_option(
        "--cleanup",
        dest="cleanup",
        action="store_true",
        default=False,
        help="don't cache Packages or Sources files",
    )
    parser.add_option(
        "--no-rdepends",
        dest="want_rdepends",
        action="store_false",
        default=True,
        help="disable reverse-dependency calculations",
    )
    parser.add_option(
        "--no-installer",
        dest="installer",
        action="store_false",
        default=True,
        help="do not consider debian-installer udeb packages",
    )
    parser.add_option(
        "--seed-packages",
        dest="seed_packages",
        metavar="PARENT/PKG,PARENT/PKG,...",
        help="treat each PKG as a seed by itself, inheriting " "from PARENT",
    )
    parser.add_option(
        "--always-follow-build-depends",
        dest="always_follow_build_depends",
        action="store_true",
        default=False,
        help="always follow Build-Depends, regardless of seed "
        "feature flags",
    )
    options, _ = parser.parse_args(argv[1:])

    if options.seeds is None:
        if options.vcs is None:
            options.seeds = germinate.defaults.seeds
        elif options.vcs == SeedVcs.GIT:
            options.seeds = germinate.defaults.seeds_git
        else:
            options.seeds = germinate.defaults.seeds_bzr
    options.seeds = options.seeds.split(",")

    if options.mirrors is None:
        options.mirrors = [germinate.defaults.mirror]

    options.dist = options.dist.split(",")
    options.components = options.components.split(",")
    if options.seed_packages is None:
        options.seed_packages = []
    else:
        options.seed_packages = options.seed_packages.split(",")

    return options


# Possibly should source this from dpkg's varianttable, but we want to run
# this on machines that are not running the latest dpkg...
isa_tag_to_abi_tag = {
    "amd64v3": "amd64",
}


def main(argv):
    options = parse_options(argv)

    if options.verbose:
        germinate_logging(logging.DEBUG)
    else:
        germinate_logging(logging.INFO)

    isa_tag = options.arch
    abi_tag = isa_tag_to_abi_tag.get(isa_tag, isa_tag)

    g = Germinator(abi_tag)
    g._always_follow_build_depends = options.always_follow_build_depends

    if options.apt_config:
        archive = germinate.archive.AptArchive(options.apt_config)
    else:
        # It isn't strictly correct to just pass isa_tag here: for an amd64v3
        # image we should really probe for amd64v3, amd64v3, amd64 Packages
        # files in order (and possibly download all of these in the case of
        # partial variants) but for now this actually works fine (as Ubuntu
        # does not publish amd64v2 and amd64v3 is a total variant).
        archive = germinate.archive.TagFile(
            options.dist,
            options.components,
            isa_tag,
            options.mirrors,
            source_mirrors=options.source_mirrors,
            installer_packages=options.installer,
            cleanup=options.cleanup,
        )
    g.parse_archive(archive)

    if os.path.isfile("hints"):
        with open("hints") as hints:
            g.parse_hints(hints)

    try:
        structure = SeedStructure(options.release, options.seeds, options.vcs)
        for seed_package in options.seed_packages:
            parent, pkg = seed_package.split("/")
            structure.add(pkg, [" * " + pkg], parent)
        g.plant_seeds(structure)
    except SeedError:
        sys.exit(1)

    try:
        """Accept blacklist/blocklist for a transitional period"""
        if os.path.exists("blocklist"):
            bl_cklist = "blocklist"
        else:
            bl_cklist = "blacklist"
        with Seed(
            options.seeds, options.release, bl_cklist, options.vcs
        ) as blocklist:
            g.parse_blocklist(structure, blocklist)
    except SeedError:
        pass

    g.grow(structure)
    g.add_extras(structure)
    if options.want_rdepends:
        g.reverse_depends(structure)

    for seedname in structure.names + ["extra"]:
        g.write_full_list(structure, seedname, seedname)
        g.write_full_list_json(structure, seedname + ".json", seedname)
        g.write_seed_list(structure, seedname + ".seed", seedname)
        g.write_seed_recommends_list(
            structure, seedname + ".seed-recommends", seedname
        )
        g.write_depends_list(structure, seedname + ".depends", seedname)
        g.write_build_depends_list(
            structure, seedname + ".build-depends", seedname
        )
        g.write_snap_list(structure, seedname + ".snaps", seedname)

        if seedname != "extra" and seedname in structure:
            structure.write_seed_text(seedname + ".seedtext", seedname)
            g.write_sources_list(structure, seedname + ".sources", seedname)
        g.write_build_sources_list(
            structure, seedname + ".build-sources", seedname
        )

    g.write_all_list(structure, "all")
    g.write_all_list_json(structure, "all.json")
    g.write_all_source_list(structure, "all.sources")
    g.write_all_snap_list(structure, "all.snaps")

    g.write_supported_list(structure, "%s+build-depends" % structure.supported)
    g.write_supported_source_list(
        structure, "%s+build-depends.sources" % structure.supported
    )

    g.write_all_extra_list(structure, "all+extra")
    g.write_all_extra_source_list(structure, "all+extra.sources")

    g.write_provides_list(structure, "provides")

    structure.write("structure")
    structure.write_dot("structure.dot")

    if os.path.exists("rdepends"):
        shutil.rmtree("rdepends")
    if options.want_rdepends:
        os.mkdir("rdepends")
        os.mkdir(os.path.join("rdepends", "ALL"))
        for pkg in g.get_all(structure):
            dirname = os.path.join("rdepends", g.get_source(pkg))
            if not os.path.exists(dirname):
                os.mkdir(dirname)

            g.write_rdepend_list(structure, os.path.join(dirname, pkg), pkg)
            os.symlink(
                os.path.join("..", g.get_source(pkg), pkg),
                os.path.join("rdepends", "ALL", pkg),
            )

    g.write_blocklisted(structure, "blocklisted")
    """Create a symlink for a transitional period"""
    os.symlink("blocklisted", "blacklisted")

    return 0
