/*
 * debtags - Implement package tags support for Debian
 *
 * Copyright (C) 2003--2012  Enrico Zini <enrico@debian.org>
 *
 * This program 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 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 */

#ifndef DEBTAGS_PRINTER_H
#define DEBTAGS_PRINTER_H

#ifdef HAVE_CONFIG_H
#include <config.h>
#define APPNAME PACKAGE_NAME
#else
#warning No config.h found: using fallback values
#define APPNAME __FILE__
#define PACKAGE_VERSION "unknown"
#endif

#include <ept/apt/apt.h>
#include <ept/apt/packagerecord.h>
#include <ept/debtags/debtags.h>
#include <ept/debtags/vocabulary.h>
#include <ept/debtags/maint/debdbparser.h>
#include <tagcoll/TextFormat.h>
#include <tagcoll/coll/fast.h>

#include <iostream>

class CollPrinter : public wibble::mixin::OutputIterator< CollPrinter >
{
public:
	enum Type { NAME, FACETS, TAGS, QUIET };

protected:
	typedef std::string Package;
	typedef std::string Tag;

	Type m_type;

public:
    int count;

	CollPrinter(const Type& type) : m_type(type), count(0) {}

	template<typename ITEMS, typename TTAGS>
	CollPrinter& operator=(const std::pair<ITEMS, TTAGS>& data)
	{
		for (typename ITEMS::const_iterator i = data.first.begin();
				i != data.first.end(); ++i)
		{
            ++count;
			if (m_type == QUIET)
				continue;

			std::cout << *i;

			switch (m_type)
			{
				case NAME: break;
				case FACETS: {
					std::set<std::string> facets;
					for (typename TTAGS::const_iterator j = data.second.begin();
						   j != data.second.end(); ++j)
						facets.insert(ept::debtags::voc::getfacet(*j));

					for (std::set<std::string>::const_iterator j = facets.begin();
						   j != facets.end(); ++j)
					   if (j == facets.begin())
					   {
						   std::cout << ": ";
						   std::cout << *j;
					   } else {
						   std::cout << ", ";
						   std::cout << *j;
					   }
					break;
				}
				case TAGS:
					for (typename TTAGS::const_iterator j = data.second.begin();
						   j != data.second.end(); ++j)
					   if (j == data.second.begin())
					   {
						   std::cout << ": ";
						   std::cout << *j;
					   } else {
						   std::cout << ", ";
						   std::cout << *j;
					   }
					break;
				case QUIET:
					break;
			}
			std::cout << std::endl;
		}
		return *this;
	}
};

class PackagePrinter : public wibble::mixin::OutputIterator< PackagePrinter >
{
public:
	enum Type { NAME, SHORT, FULL, TAGS, QUIET };

	template<typename TTAGS>
	static inline void printTags(const TTAGS& tags)
	{
		for (typename TTAGS::const_iterator i = tags.begin(); i != tags.end(); ++i)
			if (i == tags.begin())
				std::cout << *i;
			else
				std::cout << ", " + *i;
	}

protected:
	typedef std::string Package;
	typedef ept::apt::Version Version;
	typedef std::string Tag;

	ept::apt::Apt& apt;
	ept::debtags::Debtags& debtags;
	Type m_type;

public:
    int count;

	PackagePrinter(const Type& type, ept::apt::Apt& apt, ept::debtags::Debtags& debtags)
		: apt(apt), debtags(debtags), m_type(type), count(0) {}

	template<typename ITEMS, typename TTAGS>
	PackagePrinter& operator=(const std::pair<ITEMS, TTAGS>& data)
	{
		for (typename ITEMS::const_iterator i = data.first.begin();
				i != data.first.end(); ++i)
		{
			**this = *i;
			++*this;
		}
		return *this;
	}

	PackagePrinter& operator=(const Package& pkg)
	{
        ++count;
		switch (m_type)
		{
			case NAME: std::cout << pkg << std::endl; break;
			case SHORT: {
				std::cout << pkg << " - ";

				Version ver = apt.candidateVersion(pkg);
				if (!ver.isValid())
					ver = apt.installedVersion(pkg);
				ept::apt::PackageRecord record(apt.rawRecord(ver));
				std::cout << record.shortDescription("(short description not available)");
				std::cout << std::endl;
				break;
			}
			case FULL: {
				Version ver = apt.candidateVersion(pkg);
				if (!ver.isValid())
					ver = apt.installedVersion(pkg);
				if (!ver.isValid())
					return *this;
				ept::apt::PackageRecord record(apt.rawRecord(ver));
				bool tags_found = false;
				for (size_t i = 0; i < record.size(); ++i)
				{
					if (record.name(i) == "Tag")
					{
						tags_found = true;
						std::set<Tag> tags = debtags.getTagsOfItem(pkg);
						if (!tags.empty())
						{
							std::cout << "Tag: ";
							printTags(tags);
							std::cout << std::endl;
						}
					} else {
						std::cout << record.field(i);
					}
				}
				if (!tags_found)
				{
					std::set<Tag> tags = debtags.getTagsOfItem(pkg);
					if (!tags.empty())
					{
						std::cout << "Tag: ";
						printTags(tags);
						std::cout << std::endl;
					}
				}
				std::cout << std::endl;
				break;
			}
			case TAGS: {
				std::set<Tag> tags = debtags.getTagsOfItem(pkg);
				if (tags.empty())
					std::cout << pkg << std::endl;
				else {
					std::cout << pkg << ": ";
					printTags(tags);
					std::cout << std::endl;
				}
				break;
			}
			case QUIET: break;
		}
		return *this;
	}

	PackagePrinter& operator=(const ept::apt::PackageRecord& pkg)
	{
        ++count;
		switch (m_type)
		{
			case NAME: std::cout << pkg.package() << std::endl; break;
			case SHORT: {
				std::cout << pkg.package() << " - " << pkg.shortDescription("(short description not available)") << std::endl;
				break;
			}
			case FULL: {
				for (size_t i = 0; i < pkg.size(); ++i)
				{
					if (pkg.name(i) == "Tag")
					{
						std::set<Tag> tags = debtags.getTagsOfItem(Package(pkg.package()));
						if (!tags.empty())
						{
							std::cout << "Tag: ";
							printTags(tags);
							std::cout << std::endl;
						}
					} else {
						std::cout << pkg.field(i);
					}
				}
				std::cout << std::endl;
				break;
			}
			case TAGS: {
				std::set<Tag> tags = debtags.getTagsOfItem(Package(pkg.package()));
				if (tags.empty())
					std::cout << pkg.package() << std::endl;
				else {
					std::cout << pkg.package() << ": ";
					printTags(tags);
					std::cout << std::endl;
				}
				break;
			}
			case QUIET: break;
		}
		return *this;
	}
};

#if 0
class GroupedTagcollPrinter : public Printer<aptFront::cache::entity::Package, aptFront::cache::entity::Tag>
{
protected:
	typedef aptFront::cache::entity::Package Package;
	typedef aptFront::cache::entity::Tag Tag;

	Tagcoll::TextFormat<Package, Tag> writer;
	Tagcoll::ItemGrouper<Package, Tag> grouper;

	virtual void consumeItemUntagged(const Package& pkg)
	{
		grouper.consume(pkg);
	}
	virtual void consumeItem(const Package& pkg, const Tagcoll::OpSet<Tag>& tags)
	{
		grouper.consume(pkg, tags);
	}
	virtual void consumeItemsUntagged(const Tagcoll::OpSet<Package>& pkgs)
	{
		grouper.consume(pkgs);
	}
	virtual void consumeItems(const Tagcoll::OpSet<Package>& pkgs, const Tagcoll::OpSet<Tag>& tags)
	{
		grouper.consume(pkgs, tags);
	}

public:
	GroupedTagcollPrinter() : writer(aptFront::cache::Global::get().packagestringconverter(), aptFront::cache::Global::get().tagstringconverter(), stdout) {}

	virtual void flush()
	{
		grouper.output(writer);
	}
};
#endif

#if 0
class Installer : public Printer<aptFront::cache::entity::Package, aptFront::cache::entity::Tag>
{
protected:
	typedef aptFront::cache::entity::Package Package;
	typedef aptFront::cache::entity::Tag Tag;

	Tagcoll::OpSet<Package> selection;

	virtual void consumeItemUntagged(const Package& pkg)
	{
		selection += pkg;
	}
	virtual void consumeItem(const Package& pkg, const Tagcoll::OpSet<Tag>& tags)
	{
		selection += pkg;
	}
	virtual void consumeItemsUntagged(const Tagcoll::OpSet<Package>& pkgs)
	{
		selection += pkgs;
	}
	virtual void consumeItems(const Tagcoll::OpSet<Package>& pkgs, const Tagcoll::OpSet<Tag>& tags)
	{
		selection += pkgs;
	}

public:
	virtual void flush()
	{
		const char* parms[selection.size() + 1];
		int i = 0;
		parms[i++] = APTGET;
		parms[i++] = "install";
		for (std::set<Package>::iterator j = selection.begin();
				j != selection.end(); j++)
			parms[i++] = j->name().c_str();
		parms[i] = 0;
		execv(APTGET, (char* const*)parms);
		throw wibble::exception::System("Running apt-get");
	}
};
#endif

// vim:set ts=4 sw=4:
#endif
