#!/usr/bin/python

import os
import shutil
from stat import *
import string
import sys
import tempfile
import re
import smtplib
from optparse import OptionParser
from yum.constants import *
from yum.misc import getCacheDir

# HAAACK
import imp
sys.modules['repoclosure'] = imp.load_source("repoclosure","/usr/bin/repoclosure")
import repoclosure

owners = {}
deps = {}

def generateConfig(distdir, arch):
    if not os.path.exists(os.path.join(distdir, arch)):
        return None
    if arch == 'source' or arch == 'SRPMS':
        return None
    if os.path.exists(os.path.join(distdir, arch, "os")):
        subdir = "os"
    else:
        subdir = ""
    if not os.path.exists(os.path.join(distdir, arch, subdir, "repodata", "repomd.xml")):
        return None

    (fd, conffile) = tempfile.mkstemp()
    confheader = """
[main]
debuglevel=2
logfile=/var/log/yum.log
pkgpolicy=newest
distroverpkg=fedora-release
reposdir=/dev/null
keepcache=0

[development-%s]
name=Fedora Development Tree - %s
baseurl=file://%s/%s/%s
enabled=1

""" % (arch, arch, distdir, arch, subdir)
    os.write(fd,confheader)
    os.close(fd)
    return conffile


def libmunge(match):
    if match.groups()[1].isdigit():
        return "%s%d" % (match.groups()[0],int(match.groups()[1])+1)
    else:
        return "%s%s" % (match.groups()[0],match.groups()[1])

def addOwner(list, pkg):
    if list.get(pkg):
        return True

    if list.has_key(pkg):
        return False

    f = "%s-owner@fedoraproject.org" % pkg
    list[pkg] = f
    if f:
        return True
    return False

def getSrcPkg(pkg):
    if pkg.arch == 'src':
      return pkg.name
    srpm = pkg.returnSimple('sourcerpm')
    if not srpm:
        return None
    srcpkg = string.join(srpm.split('-')[:-2],'-')
    return srcpkg

def printableReq(pkg, dep):
    (n, f, v) = dep
    req = '%s' % n
    if f:
        flag = LETTERFLAGS[f]
        req = '%s %s' % (req, flag)
    if v:
        req = '%s %s' % (req, v)
    return "%s requires %s" % (pkg, req,)

def assignBlame(resolver, dep, guilty):
    def __addpackages(sack):
        for package in sack.returnPackages():
            p = getSrcPkg(package)
            if addOwner(guilty, p):
                list.append(p)
    
    # Given a dep, find potential responsible parties

    list = []
    
    # The dep itself
    list.append(dep)

    # Something that provides the dep
    __addpackages(resolver.whatProvides(dep, None, None))

    # Libraries: check for variant in soname
    if re.match("lib.*\.so\.[0-9]+",dep):
        new = re.sub("(lib.*\.so\.)([0-9])+",libmunge,dep)
        __addpackages(resolver.whatProvides(new, None, None))
        libname = dep.split('.')[0]
        __addpackages(resolver.whatProvides(libname, None, None))

    return list

def generateSpam(pkgname, sendmail = True):

    package = deps[pkgname]
    guilty = owners[pkgname]
    conspirators = []

    for s in package.keys():
        subpackage = package[s]
        for arch in subpackage.keys():
            brokendeps = subpackage[arch]
            for dep in brokendeps:
                for blame in dep[2]:
                    # We might not have an owner here for virtual deps
                    try:
                        party = owners[blame]
                    except:
                        continue
                    if party != guilty and party not in conspirators:
                        conspirators.append(party)

    data = """

%s has broken dependencies in the development tree:
""" % (pkgname,)

    for s in package.keys():
        subpackage = package[s]
        for arch in subpackage.keys():
            data = data + "On %s:\n" % (arch)
            brokendeps = subpackage[arch]
            for dep in brokendeps:
                data = data + "\t%s\n" % printableReq(dep[0],dep[1])

    data = data + "Please resolve this as soon as possible.\n\n"
    
    fromaddr = 'buildsys@fedoraproject.org'
    toaddrs = [guilty]
    if conspirators:
        toaddrs = toaddrs + conspirators

    msg = """From: %s
To: %s
Cc: %s
Subject: Broken dependencies: %s

%s
""" % (fromaddr, guilty, string.join(conspirators,','), pkgname, data)
    if sendmail:
        try:
            server = smtplib.SMTP('localhost')
            server.set_debuglevel(1)
            server.sendmail(fromaddr, toaddrs, msg)
        except:
            print 'sending mail failed'
    else:
        print msg

def doit(dir, mail=True):
    for arch in os.listdir(dir):
        conffile = generateConfig(dir, arch)
        if not conffile:
            continue
        if arch == 'i386':
            carch = 'i686'
        elif arch == 'ppc':
            carch = 'ppc64'
        elif arch == 'sparc':
            carch = 'sparc64v'
        else:
            carch = arch
        my = repoclosure.RepoClosure(config = conffile, arch = [carch])
        cachedir = getCacheDir()
        my.repos.setCacheDir(cachedir)
        my.readMetadata()
        baddeps = my.getBrokenDeps(newest = False)
        pkgs = baddeps.keys()
        tmplist = [(x.returnSimple('name'), x) for x in pkgs]
        tmplist.sort()
        pkgs = [x for (key, x) in tmplist]
        if len(pkgs) > 0:
            print "Broken deps for %s" % (arch,)
            print "----------------------------------------------------------"
        for pkg in pkgs:
            srcpkg = getSrcPkg(pkg)

            addOwner(owners, srcpkg)

            if not deps.has_key(srcpkg):
                deps[srcpkg] = {}

            pkgid = "%s-%s" % (pkg.name, pkg.printVer())

            if not deps[srcpkg].has_key(pkgid):
                deps[srcpkg][pkgid] = {}

            broken = []
            for (n, f, v) in baddeps[pkg]:
                print "\t%s" % printableReq(pkg, (n, f, v))

                blamelist = assignBlame(my, n, owners)

                broken.append( (pkg, (n, f, v), blamelist) )

            deps[srcpkg][pkgid][arch] = broken

        print "\n\n"
        os.unlink(conffile)
        shutil.rmtree(cachedir, ignore_errors = True)

    pkglist = deps.keys()
    for pkg in pkglist:
        generateSpam(pkg, mail)

if __name__ == '__main__':

    parser = OptionParser("usage: %prog [options] <directory>")
    parser.add_option("--nomail", action="store_true")
    (options, args) = parser.parse_args(sys.argv[1:])
    if len(args) != 1:
        parser.error("incorrect number of arguments")
        sys.exit(1)
    if options.nomail:
        mail = False
    else:
        mail = True
    doit(args[0], mail)
