/* vim: set sw=4 sts=4 et foldmethod=syntax : */

/*
 * Copyright (c) 2007 Piotr Jaroszyński <peper@gentoo.org>
 *
 * This file is part of the Paludis package manager. Paludis is free software;
 * you can redistribute it and/or modify it under the terms of the GNU General
 * Public License version 2, as published by the Free Software Foundation.
 *
 * Paludis 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
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <paludis_python.hh>

#include <datetime.h>

#include <paludis/repository.hh>
#include <paludis/repositories/gentoo/portage_repository.hh>

namespace p = paludis;
namespace pp = paludis::python;
namespace bp = boost::python;

struct RepositoryWrapper :
    p::Repository,
    bp::wrapper<p::Repository>
{
    std::tr1::shared_ptr<const p::RepositoryInfo>
    info(bool verbose) const
    {
        if (bp::override info = this->get_override("info"))
            return info(verbose);
        return p::Repository::info(verbose);
    }

    std::tr1::shared_ptr<const p::RepositoryInfo>
    default_info(bool verbose) const
    {
        return p::Repository::info(verbose);
    }
};

class p::QualifiedPackageName;
class p::VersionSpec;

struct RepositoryInstalledInterfaceWrapper :
    p::RepositoryInstalledInterface
{
    static PyObject *
    installed_time(const p::RepositoryInstalledInterface & self,
            const p::QualifiedPackageName & qpn, const p::VersionSpec & vs)
    {
        PyDateTime_IMPORT;
        return PyDateTime_FromTimestamp(bp::make_tuple(self.installed_time(qpn, vs)).ptr());
    }

};

struct RepositoryLicensesInterfaceWrapper :
    p::RepositoryLicensesInterface
{
    static PyObject *
    license_exists(const p::RepositoryLicensesInterface & self, const std::string & license)
    {
        std::tr1::shared_ptr<p::FSEntry> p(self.license_exists(license));
        if (p)
            return pp::to_string<p::FSEntry>::convert(*p);
        else
            return 0;
    }
};

struct RepositoryPortageInterfaceWrapper :
    p::RepositoryPortageInterface
{
    static bp::object
    my_find_profile(const p::RepositoryPortageInterface & self, const p::FSEntry & location)
    {
        ProfilesIterator p(self.find_profile(location));
        if (p == self.end_profiles())
            return bp::object();
        return bp::object(bp::ptr(&*p));
    }

    static void
    my_set_profile(p::RepositoryPortageInterface & self, const ProfilesDescLine & pdl)
    {
        self.set_profile(self.find_profile(pdl.path));
    }

};


void expose_repository()
{
    static pp::register_exception<p::PackageActionError>
        PackageActionError("PackageActionError");
    static pp::register_exception<p::PackageInstallActionError>
        PackageInstallActionError("PackageInstallActionError");
    static pp::register_exception<p::PackageFetchActionError>
        PackageFetchActionError("PackageFetchActionError");
    static pp::register_exception<p::PackageUninstallActionError>
        PackageUninstallActionError("PackageUninstallActionError");
    static pp::register_exception<p::PackageConfigActionError>
        PackageConfigActionError("PackageConfigActionError");
    static pp::register_exception<p::EnvironmentVariableActionError>
        EnvironmentVariableActionError("EnvironmentVariableActionError");

    pp::class_collection<p::DestinationsCollection>
        dc("DestinationsCollection",
                "Iterable of Repository.\n"
                "A set of Destinations."

          );

    bp::to_python_converter<std::pair<const std::string, std::string>,
            pp::pair_to_tuple<const std::string, std::string> >();
    pp::register_shared_ptrs_to_python<p::RepositoryInfoSection>();
    bp::class_<p::RepositoryInfoSection, boost::noncopyable>
        ris("RepositoryInfoSection",
                "A section of information about a Repository.",
                bp::init<const std::string &>()
           );
    ris.add_property("heading", &p::RepositoryInfoSection::heading,
            "[ro] string\n"
            "Heading."
            );
    ris.add_property("kvs", bp::range(&p::RepositoryInfoSection::begin_kvs, &p::RepositoryInfoSection::end_kvs),
            "[ro] Iterable of tuples\n"
            "Key-value pairs."
            );

    pp::register_shared_ptrs_to_python<p::RepositoryInfo>();
    bp::class_<p::RepositoryInfo, boost::noncopyable>
        ri("RepositoryInfo",
                "Information about a Repository, for the end user.",
                bp::no_init
           );
    ri.add_property("sections", bp::range(&p::RepositoryInfo::begin_sections, &p::RepositoryInfo::end_sections),
            "[ro] Iterable of RepositoryInfoSection."
            );

    pp::register_shared_ptrs_to_python<p::Repository>();
    bp::class_<RepositoryWrapper, boost::noncopyable>
        r("Repository",
                "A Repository provides a representation of a physical repository to a PackageDatabase.",
                bp::no_init
         );
    r.def("info", &p::Repository::info, &RepositoryWrapper::default_info,
            "info() -> RepositoryInfo"
            "Fetch information about the repository."
         );
    r.add_property("name", bp::make_function(&p::Repository::name,
                bp::return_value_policy<bp::copy_const_reference>()),
            "[ro] RepositoryName\n"
            "Our name."
         );
    r.add_property("format", &p::Repository::format,
            "[ro] string\n"
            "Our format."
         );
    r.def("has_category_named", &p::Repository::has_category_named,
            "has_category_named(CategoryNamePart) -> bool\n"
            "Do we have a category with the given name?"
         );
    r.def("has_package_named", &p::Repository::has_package_named,
            "Do we have a package in the given category with the given name?"
         );
    r.add_property("category_names", &p::Repository::category_names,
            "[ro] CategoryNamePartCollection\n"
            "Our category names."
         );
    r.def("category_names_containing_package", &p::Repository::category_names_containing_package,
            "category_names_containing_package(PackageNamePart) -> CategoryNamePartCollection\n"
            "Fetch categories that contain a named package."
         );
    r.def("package_names", &p::Repository::package_names,
            "package_names(CategoryNamePart) -> QualifiedPackageNameCollection\n"
            "Fetch our package names."
         );
    r.def("version_specs", &p::Repository::version_specs,
            "version_specs(QualifiedPackageName) -> VersionSpecCollection\n"
            "Fetch our versions."
         );
    r.def("has_version", &p::Repository::has_version,
            "has_version(QualifiedPackageName, VersionSpec) -> bool\n"
            "Do we have a version spec?"
         );
    r.def("version_metadata", &p::Repository::version_metadata,
            "version_metadata(QualifiedPackageName, VersionSpec) -> VersionMetadata\n"
            "Fetch metadata."
         );
    r.def_readonly("mask_interface", &p::Repository::mask_interface,
            "[ro] RepositoryMaskInterface"
            );
    r.def_readonly("use_interface", &p::Repository::use_interface,
            "[ro] RepositoryUseInterface"
            );
    r.def_readonly("installed_interface", &p::Repository::installed_interface,
            "[ro] RepositoryInstalledInterface"
            );
    r.def_readonly("installable_interface", &p::Repository::installable_interface,
            "[ro] RepositoryInstallableInterface"
            );
    r.def_readonly("uninstallable_interface", &p::Repository::uninstallable_interface,
            "[ro] RepositoryUninstallableInterface"
            );
    r.def_readonly("sets_interface", &p::Repository::sets_interface,
            "[ro] RepositorySetsInterface"
            );
    r.def_readonly("syncable_interface", &p::Repository::syncable_interface,
            "[ro] RepositorySyncableInterface"
            );
    r.def_readonly("world_interface", &p::Repository::world_interface,
            "[ro] RepositoryWorldInterface"
            );
    r.def_readonly("environment_variable_interface", &p::Repository::environment_variable_interface,
            "[ro] RepositoryEnvironmentInterface"
            );
    r.def_readonly("mirrors_interface", &p::Repository::mirrors_interface,
            "[ro] RepositoryMirrorsInterface"
            );
    r.def_readonly("virtuals_interface", &p::Repository::virtuals_interface,
            "[ro] RepositoryVirtualsInterface"
            );
    r.def_readonly("provides_interface", &p::Repository::provides_interface,
            "[ro] RepositoryProvidesInterface"
            );
    r.def_readonly("destination_interface", &p::Repository::destination_interface,
            "[ro] RepositoryDestinationInterface"
            );
    r.def_readonly("contents_interface", &p::Repository::contents_interface,
            "[ro] RepositoryContentsInterface"
            );
    r.def_readonly("config_interface", &p::Repository::config_interface,
            "[ro] RepositoryConfigInterface"
            );
    r.def_readonly("licenses_interface", &p::Repository::licenses_interface,
            "[ro] RepositoryLicensesInterface"
            );
    r.def_readonly("portage_interface", &p::Repository::portage_interface,
            "[ro] RepositoryPortageInterface"
            );


    bp::class_<p::RepositoryPortageInterfaceProfilesDescLine>
        rpipd("RepositoryPortageInterfaceProfilesDescLine",
                "A profiles.desc line in a Repository implementing RepositoryPortageInterface.",
                bp::no_init
             );
    rpipd.add_property("path", bp::make_getter(&p::RepositoryPortageInterfaceProfilesDescLine::path,
                bp::return_value_policy<bp::return_by_value>())
            );
    rpipd.def_readonly("arch", &p::RepositoryPortageInterfaceProfilesDescLine::arch);
    rpipd.def_readonly("status", &p::RepositoryPortageInterfaceProfilesDescLine::status);

    //Interfaces
    bp::register_ptr_to_python<p::RepositoryMaskInterface *>();
    bp::class_<p::RepositoryMaskInterface, boost::noncopyable>
        rmaski("RepositoryMaskInterface",
                "Interface for handling masks for the Repository class.",
                bp::no_init
           );
    rmaski.def("query_repository_masks", &p::RepositoryMaskInterface::query_repository_masks,
            "query_repository_masks(QualifiedPackageName, VersionSpec) -> bool\n"
            "Query repository masks."
           );
    rmaski.def("query_profile_masks", &p::RepositoryMaskInterface::query_profile_masks,
            "query_profile_masks(QualifiedPackageName, VersionSpec) -> bool\n"
            "Query profile masks."
           );

    bp::register_ptr_to_python<p::RepositoryUseInterface *>();
    bp::class_<p::RepositoryUseInterface, boost::noncopyable>
        rusei("RepositoryUseInterface",
                "Interface for handling USE flags for the Repository class.",
                bp::no_init
           );
    rusei.def("query_use", &p::RepositoryUseInterface::query_use,
            ("ufn", bp::arg("pde")=bp::object()),
            "query_use(UseFlagName, PackageDatabaseEntry=None) -> UseFlagState\n"
            "Query the state of the specified use flag."
           );
    rusei.def("query_use_mask", &p::RepositoryUseInterface::query_use_mask,
            ("ufn", bp::arg("pde")=bp::object()),
            "query_use_mask(UseFlagName, PackageDatabaseEntry=None) -> bool\n"
            "Query whether the specified use flag is masked."
           );
    rusei.def("query_use_force", &p::RepositoryUseInterface::query_use_force,
            ("ufn", bp::arg("pde")=bp::object()),
            "query_use_force(UseFlagName, PackageDatabaseEntry=None) -> bool\n"
            "Query whether the specified use flag is forceed."
           );
    rusei.def("describe_use_flag", &p::RepositoryUseInterface::describe_use_flag,
            ("ufn", bp::arg("pde")=bp::object()),
            "describe_use_flag(UseFlagName, PackageDatabaseEntry=None) -> string\n"
            "Describe a use flag."
           );

    bp::register_ptr_to_python<p::RepositoryInstalledInterface *>();
    bp::class_<p::RepositoryInstalledInterface, boost::noncopyable>
        rinstalledi("RepositoryInstalledInterface",
                "Interface for handling actions for installed repositories.",
                bp::no_init
             );
    rinstalledi.def("installed_time", &RepositoryInstalledInterfaceWrapper::installed_time,
            "installed_time(QualifiedPackageName, VersionSpec) -> datetime\n"
            "When was a package installed."
            );
    rinstalledi.def("root", bp::pure_virtual(&p::RepositoryInstalledInterface::root),
            "What is our filesystem root?"
            );

    bp::register_ptr_to_python<p::RepositoryInstallableInterface *>();
    bp::class_<p::RepositoryInstallableInterface, boost::noncopyable>
        rinstallablei("RepositoryInstallableInterface",
                "Interface for handling installs for repositories.",
                bp::no_init
               );

    bp::register_ptr_to_python<p::RepositoryUninstallableInterface *>();
    bp::class_<p::RepositoryUninstallableInterface, boost::noncopyable>
        runinstallablei("RepositoryUninstallableInterface",
                "Interface for handling uninstalls for repositories.",
                bp::no_init
         );

    bp::register_ptr_to_python<p::RepositorySetsInterface *>();
    bp::class_<p::RepositorySetsInterface, boost::noncopyable>
        rsetsi("RepositorySetsInterface",
                "Interface for package sets for repositories.",
                bp::no_init
         );

    bp::register_ptr_to_python<p::RepositorySyncableInterface *>();
    bp::class_<p::RepositorySyncableInterface, boost::noncopyable>
        rsynci("RepositorySyncableInterface",
                "Interface for syncing for repositories.",
                bp::no_init
         );

    bp::register_ptr_to_python<p::RepositoryWorldInterface *>();
    bp::class_<p::RepositoryWorldInterface, boost::noncopyable>
        rworldi("RepositoryWorldInterface",
                "Interface for world handling for repositories.",
                bp::no_init
         );

    bp::register_ptr_to_python<p::RepositoryEnvironmentVariableInterface *>();
    bp::class_<p::RepositoryEnvironmentVariableInterface, boost::noncopyable>
        renvvari("RepositoryEnvironmentVariableInterface",
                "Interface for environment variable querying for repositories.",
                bp::no_init
         );

    bp::register_ptr_to_python<p::RepositoryMirrorsInterface *>();
    bp::class_<p::RepositoryMirrorsInterface, boost::noncopyable>
        rmirrorsi("RepositoryMirrorsInterface",
                "Interface for mirror querying for repositories.",
                bp::no_init
         );

    bp::register_ptr_to_python<p::RepositoryVirtualsInterface *>();
    bp::class_<p::RepositoryVirtualsInterface, boost::noncopyable>
        rvirtualsi("RepositoryVirtualsInterface",
                "Interface for repositories that define virtuals.",
                bp::no_init
         );

    bp::register_ptr_to_python<p::RepositoryProvidesInterface *>();
    bp::class_<p::RepositoryProvidesInterface, boost::noncopyable>
        rprovidesi("RepositoryProvidesInterface",
                "Interface for repositories that provide packages.",
                bp::no_init
         );

    bp::register_ptr_to_python<p::RepositoryDestinationInterface *>();
    bp::class_<p::RepositoryDestinationInterface, boost::noncopyable>
        rdesti("RepositoryDestinationInterface",
                "Interface for repositories that can be used as an install destination.",
                bp::no_init
         );

    bp::register_ptr_to_python<p::RepositoryContentsInterface *>();
    bp::class_<p::RepositoryContentsInterface, boost::noncopyable>
        rcontentsi("RepositoryContentsInterface",
                "Interface for handling actions for repositories supporting contents queries.",
                bp::no_init
         );
    rcontentsi.def("contents", &p::RepositoryContentsInterface::contents,
            "contents(QualifiedPackageName, VersionSpec) -> Contents\n"
            "Fetch contents."
            );

    bp::register_ptr_to_python<p::RepositoryConfigInterface *>();
    bp::class_<p::RepositoryConfigInterface, boost::noncopyable>
        rconfigi("RepositoryConfigInterface",
                "Interface for handling actions for repositories supporting package configuration.",
                bp::no_init
         );

    bp::register_ptr_to_python<p::RepositoryLicensesInterface *>();
    bp::class_<p::RepositoryLicensesInterface, boost::noncopyable>
        rlici("RepositoryLicensesInterface",
                "Interface for handling actions relating to licenses.",
                bp::no_init
         );
    rlici.def("license_exists", &RepositoryLicensesInterfaceWrapper::license_exists,
            "license_exists(string) -> path_string or None\n"
            "Check if a license exists."
            );

    bp::register_ptr_to_python<p::RepositoryPortageInterface *>();
    bp::class_<p::RepositoryPortageInterface, boost::noncopyable>
        rporti("RepositoryPortageInterface",
                "Interface for handling PortageRepository specific functionality.",
                bp::no_init
         );
    rporti.add_property("profiles", bp::range(&p::RepositoryPortageInterface::begin_profiles,
                &p::RepositoryPortageInterface::end_profiles),
            "[ro] Iterable of Profiles"
            );
    rporti.def("profile_variable", &p::RepositoryPortageInterface::profile_variable);
    rporti.def("find_profile", &RepositoryPortageInterfaceWrapper::my_find_profile,
            "find_profile(path_string) -> RepositoryPortageInterfaceProfilesDescLine"
            );
    rporti.add_property("profile", bp::object(), &RepositoryPortageInterfaceWrapper::my_set_profile,
            "[wo] RepositoryPortageInterfaceProfilesDescLine"
            );
}
