// Include Files							/*{{{*/
#include <config.h>

#include <apt-pkg/cachefile.h>
#include <apt-pkg/cachefilter.h>
#include <apt-pkg/cacheset.h>
#include <apt-pkg/cmndline.h>
#include <apt-pkg/pkgrecords.h>
#include <apt-pkg/progress.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/configuration.h>
#include <apt-pkg/macros.h>
#include <apt-pkg/pkgcache.h>
#include <apt-pkg/cacheiterators.h>

#include <apt-private/private-cacheset.h>
#include <apt-private/private-list.h>
#include <apt-private/private-output.h>

#include <iostream>
#include <sstream>
#include <map>
#include <string>
#include <utility>
#include <vector>

#include <apti18n.h>
									/*}}}*/

struct PackageSortAlphabetic						/*{{{*/
{
   bool operator () (const pkgCache::PkgIterator &p_lhs, 
                     const pkgCache::PkgIterator &p_rhs)
    {
       const std::string &l_name = p_lhs.FullName(true);
       const std::string &r_name = p_rhs.FullName(true);
       return (l_name < r_name);
    }
};
									/*}}}*/
class PackageNameMatcher : public Matcher				/*{{{*/
{
#ifdef PACKAGE_MATCHER_ABI_COMPAT
#define PackageMatcher PackageNameMatchesFnmatch
#endif
  public:
   PackageNameMatcher(const char **patterns)
   {
      for(int i=0; patterns[i] != NULL; ++i)
      {
         std::string pattern = patterns[i];
#ifdef PACKAGE_MATCHER_ABI_COMPAT
            APT::CacheFilter::PackageNameMatchesFnmatch *cachefilter = NULL;
            cachefilter = new APT::CacheFilter::PackageNameMatchesFnmatch(pattern);
#else
         APT::CacheFilter::PackageMatcher *cachefilter = NULL;
         if(_config->FindB("APT::Cmd::Use-Regexp", false) == true)
            cachefilter = new APT::CacheFilter::PackageNameMatchesRegEx(pattern);
         else
            cachefilter = new APT::CacheFilter::PackageNameMatchesFnmatch(pattern);
#endif
         filters.push_back(cachefilter);
      }
   }
   virtual ~PackageNameMatcher()
   {
      for(J=filters.begin(); J != filters.end(); ++J)
         delete *J;
   }
   virtual bool operator () (const pkgCache::PkgIterator &P) 
   {
      for(J=filters.begin(); J != filters.end(); ++J)
      {
         APT::CacheFilter::PackageMatcher *cachefilter = *J;
         if((*cachefilter)(P)) 
            return true;
      }
      return false;
   }

private:
   std::vector<APT::CacheFilter::PackageMatcher*> filters;   
   std::vector<APT::CacheFilter::PackageMatcher*>::const_iterator J;
   #undef PackageMatcher
};
									/*}}}*/
static void ListAllVersions(pkgCacheFile &CacheFile, pkgRecords &records,/*{{{*/
                     pkgCache::PkgIterator P,    
                     std::ostream &outs,
                     bool include_summary=true)
{
   for (pkgCache::VerIterator Ver = P.VersionList();
        Ver.end() == false; ++Ver)
   {
      ListSingleVersion(CacheFile, records, Ver, outs, include_summary);
      outs << "\n";
   }
}
									/*}}}*/
// list - list package based on criteria        			/*{{{*/
// ---------------------------------------------------------------------
bool DoList(CommandLine &Cmd)
{
   pkgCacheFile CacheFile;
   pkgCache *Cache = CacheFile.GetPkgCache();
   if (unlikely(Cache == NULL))
      return false;
   pkgRecords records(CacheFile);

   const char **patterns;
   const char *all_pattern[] = { "*", NULL};

   if (strv_length(Cmd.FileList + 1) == 0)
   {
      patterns = all_pattern;
   } else {
      patterns = Cmd.FileList + 1;
   }

   std::map<std::string, std::string> output_map;
   std::map<std::string, std::string>::const_iterator K;

   bool includeSummary = _config->FindB("APT::Cmd::List-Include-Summary");

   PackageNameMatcher matcher(patterns);
   LocalitySortedVersionSet bag;
   OpTextProgress progress(*_config);
   progress.OverallProgress(0,
                            Cache->Head().PackageCount, 
                            Cache->Head().PackageCount,
                            _("Listing"));
   GetLocalitySortedVersionSet(CacheFile, bag, matcher, progress);
   bool ShowAllVersions = _config->FindB("APT::Cmd::All-Versions", false);
   for (LocalitySortedVersionSet::iterator V = bag.begin(); V != bag.end(); ++V)
   {
      std::stringstream outs;
      if(ShowAllVersions == true)
      {
         ListAllVersions(CacheFile, records, V.ParentPkg(), outs, includeSummary);
         output_map.insert(std::make_pair<std::string, std::string>(
            V.ParentPkg().Name(), outs.str()));
      } else {
         ListSingleVersion(CacheFile, records, V, outs, includeSummary);
         output_map.insert(std::make_pair<std::string, std::string>(
                           V.ParentPkg().Name(), outs.str()));
      }
   }

   // FIXME: SORT! and make sorting flexible (alphabetic, by pkg status)
   // output the sorted map
   for (K = output_map.begin(); K != output_map.end(); ++K)
      std::cout << (*K).second << std::endl;


   // be nice and tell the user if there is more to see
   if (bag.size() == 1 && ShowAllVersions == false)
   {
      // start with -1 as we already displayed one version
      int versions = -1;
      pkgCache::VerIterator Ver = *bag.begin();
      for ( ; Ver.end() == false; Ver++)
         versions++;
      if (versions > 0)
         _error->Notice(P_("There is %i additional version. Please use the '-a' switch to see it", "There are %i additional versions. Please use the '-a' switch to see them.", versions), versions);
   }

   return true;
}