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

#include <apt-pkg/error.h>
#include <apt-pkg/cachefile.h>
#include <apt-pkg/cachefilter.h>
#include <apt-pkg/cacheset.h>
#include <apt-pkg/init.h>
#include <apt-pkg/progress.h>
#include <apt-pkg/sourcelist.h>
#include <apt-pkg/cmndline.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/pkgrecords.h>
#include <apt-pkg/srcrecords.h>
#include <apt-pkg/version.h>
#include <apt-pkg/policy.h>
#include <apt-pkg/tagfile.h>
#include <apt-pkg/algorithms.h>
#include <apt-pkg/sptr.h>
#include <apt-pkg/pkgsystem.h>
#include <apt-pkg/indexfile.h>
#include <apt-pkg/metaindex.h>

#include <sstream>
#include <vector>
#include <utility>
#include <cassert>
#include <locale.h>
#include <iostream>
#include <unistd.h>
#include <errno.h>
#include <regex.h>
#include <stdio.h>
#include <algorithm>

#include "private-cmndline.h"
#include "private-list.h"
#include "private-output.h"
#include "private-cacheset.h"

#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);
    }
};

#ifdef PACKAGE_MATCHER_ABI_COMPAT
#define PackageMatcher PackageNameMatchesFnmatch
#endif
class PackageNameMatcher : public Matcher
{
  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::UseRegexp", 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
};


void ListAllVersions(pkgCacheFile &CacheFile, pkgRecords &records, 
                     pkgCache::PkgIterator P,    
                     std::ostream &outs)
{
   for (pkgCache::VerIterator Ver = P.VersionList();
        Ver.end() == false; Ver++) 
      ListSingleVersion(CacheFile, records, Ver, outs);
}

// list - list package based on criteria        			/*{{{*/
// ---------------------------------------------------------------------
bool List(CommandLine &Cmd)
{
   pkgCacheFile CacheFile;
   pkgCache *Cache = CacheFile.GetPkgCache();
   pkgRecords records(CacheFile);

   if (unlikely(Cache == NULL))
      return false;

   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;

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


   return true;
}