#!/usr/bin/python

# TODO:
# - Exclude essential packages from dependencies

import sys
sys.path.insert(1, '/usr/share/germinate')
import urllib2
import urlparse
import gzip
import sets
import re
import StringIO
import os
import logging
import apt_pkg
from Germinate import Germinator
import Germinate.Archive

seeds = ['minimal', 'standard', 'desktop', 'live']
architectures = ['i386', 'amd64', 'powerpc', 'ia64', 'sparc', 'hppa']
archive_base_default = 'http://archive.ubuntu.com/ubuntu/'
archive_base_ports = 'http://ports.ubuntu.com/ubuntu-ports/'
archive_base = { 'i386' : archive_base_default,
                 'amd64' : archive_base_default,
                 'powerpc' : archive_base_default,
                 'hppa' : archive_base_ports,
                 'ia64' : archive_base_ports,
                 'sparc' : archive_base_ports,
                }
dist = 'dapper'
seed_base = 'http://people.ubuntu.com/~cjwatson/seeds/kubuntu-%s/' % dist
seed_entry = re.compile(' *\* *(?P<package>\S+) *(\[(?P<arches>[^]]*)\])? *(#.*)?')
components = ['main', 'restricted']
debootstrap_version_file = 'debootstrap-version'
metapackages = map(lambda seed: 'kubuntu-' + seed, seeds)
seed_package_blacklist = sets.Set(metapackages)

def get_debootstrap_version():
    version = os.popen("dpkg-query -W --showformat '${Version}' debootstrap").read()
    if not version:
        raise RuntimeError('debootstrap does not appear to be installed')

    return version

def debootstrap_packages(arch):
    debootstrap = os.popen('debootstrap --arch %s --print-debs %s debootstrap-dir %s' % (arch,dist,archive_base[arch]))
    packages = debootstrap.read().split()
    if debootstrap.close():
        raise RuntimeError('Unable to retrieve package list from debootstrap')
    
    
    # sometimes debootstrap gives empty packages / multiple separators
    packages = filter(None, packages)
    
    packages.sort()

    return packages

def check_debootstrap_version():
    if os.path.exists(debootstrap_version_file):
        old_debootstrap_version = open(debootstrap_version_file).read().strip()
        debootstrap_version = get_debootstrap_version()
        failed = os.system("dpkg --compare-versions '%s' ge '%s'" % (debootstrap_version,
                                                                     old_debootstrap_version))
        if failed:
            raise RuntimeError('Installed debootstrap is older than in the previous version! (%s < %s)' % (
                debootstrap_version,
                old_debootstrap_version
                ))

def update_debootstrap_version():
    open(debootstrap_version_file, 'w').write(get_debootstrap_version() + '\n')

def open_seed(seed_name):
    url = urlparse.urljoin(seed_base, seed_name)
    req = urllib2.Request(url)
    req.add_header('Cache-Control', 'no-cache')
    req.add_header('Pragma', 'no-cache')
    return urllib2.urlopen(req)

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter('%(levelname)s%(message)s'))
logger.addHandler(handler)

check_debootstrap_version()

additions = {}
removals = {}
apt_pkg.InitConfig()
for architecture in architectures:
    print "[%s] Downloading available package lists..." % architecture
    apt_pkg.Config.Set("APT::Architecture", architecture)
    germinator = Germinator()
    Germinate.Archive.TagFile(archive_base[architecture], archive_base_default).feed(
        germinator, [dist], components, architecture, cleanup=True)
    debootstrap_base = sets.Set(debootstrap_packages(architecture))

    print "[%s] Loading seed lists..." % architecture
    (seed_names, seed_inherit) = germinator.parseStructure(open_seed("STRUCTURE"))
    for seed_name in seeds:
        germinator.plantSeed(open_seed(seed_name), architecture, seed_name,
                             list(seed_inherit[seed_name]), "foo")

    print "[%s] Merging seeds with available package lists..." % architecture
    for seed_name in seeds:
        output_filename = '%s-%s' % (seed_name,architecture)
        old_list = None
        if os.path.exists(output_filename):
            old_list = sets.Set(map(str.strip,open(output_filename).readlines()))
            os.rename(output_filename, output_filename + '.old')

        new_list = []
        for package in germinator.seed[seed_name]:
            if package in seed_package_blacklist:
                continue
            if seed_name == 'minimal' and package not in debootstrap_base:
                print "%s/%s: Skipping package %s (package not in debootstrap)" % (seed_name,architecture,package)
            else:
                new_list.append(package)

        new_list.sort()
        output = open(output_filename, 'w')
        for package in new_list:
            output.write(package)
            output.write('\n')
        output.close()
        

        # Calculate deltas
        if old_list is not None:
            merged = {}
            for package in new_list:
                merged.setdefault(package, 0)
                merged[package] += 1
            for package in old_list:
                merged.setdefault(package, 0)
                merged[package] -= 1

            mergeditems = merged.items()
            mergeditems.sort()
            for package, value in mergeditems:
                #print package, value
                if value == 1:
                    additions.setdefault(package,[])
                    additions[package].append(output_filename)
                elif value == -1:
                    removals.setdefault(package,[])
                    removals[package].append(output_filename)

if additions or removals:
    os.system("dch -i 'Refreshed dependencies'")
    changes = []
    for package, files in additions.items():
        changes.append('Added %s to %s' % (package, ', '.join(files)))
    for package, files in removals.items():
        changes.append('Removed %s from %s' % (package, ', '.join(files)))
    for change in changes:
        print change
        os.system("dch -a '%s'" % change)
    update_debootstrap_version()
else:
    print "No changes found"
