#!/usr/bin/env python2.4
# for extra options, you can use it as a library, like this:
# % python -c "from bzr_recent import *; mail_recent('/path/to/branch', 'path/to/revid_file', 'email@example.com')
import os
from bzrlib.bzrdir import BzrDir
from bzrlib import log
from bzrlib.config import BranchConfig
from bzrlib.revisionspec import RevisionSpec
try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO
import email.Utils
from email.MIMEText import MIMEText
import smtplib

DEBUG = os.environ.get('DEBUG', False)

def mail_recent(branch_loc, revid_file, from_revision=None,
                to_address=None, from_address=None,
                subject='Recent commits in $BRANCH',
                log_options=None, format='long', format_options=None):

    bd = BzrDir.open(branch_loc)
    branch = bd.open_branch()
    branch.lock_read()
    history = branch.revision_history()
    history.reverse()

    removed_revids = []

    try:
        latest_revid = branch.last_revision()
        if DEBUG:
            print 'last branch revid:', branch.last_revision()

        if from_revision is not None:
            spec = RevisionSpec(from_revision)
            reported_revno, reported_revid = spec.in_history(branch)
        elif os.path.exists(revid_file):
            f = file(revid_file)
            reported_revids = [line.strip() for line in f.readlines()]
            reported_revids = filter(None, reported_revids)
            f.close(); del f
            removed_revids = [revid for revid in reported_revids
                              if revid not in history]
            for revid in history:
                if revid in reported_revids:
                    reported_revid = revid
                    reported_revno = branch.revision_id_to_revno(revid)
                    break
            else:
                # from the beginning
                reported_revid = None
                reported_revno = 0
        else:
            # special case: initialize to latest revision and exit
            f = file(revid_file, 'w')
            f.write(latest_revid)
            f.close(); del f
            return 0

        if DEBUG:
            print 'last reported revid:', reported_revid

        if latest_revid == reported_revid:
            if DEBUG:
                print 'nothing to see here, move along'
            return 0

        last_revno = branch.revno()

        output = StringIO()
        if removed_revids:
            print >> output, 'These revisions were removed from history:'
            for revid in removed_revids:
                print >> output, '   ', revid
            print >> output
            print >> output, 'New revisions:'
        format_options = format_options or {}
        formatter = log.log_formatter(format, output, **format_options)
        log_options = log_options or {'direction': 'forward',
                                      'verbose': True}
        log_options['start_revision'] = reported_revno + 1
        log_options['end_revision'] = None
        log._show_log(branch, formatter, **log_options)

    finally:
        branch.unlock()

    if from_address is None:
        config = BranchConfig(branch)
        from_address = config.username()
    if '$BRANCH' in subject:
        subject = subject.replace('$BRANCH', branch.nick)

    msg = MIMEText(output.getvalue())
    msg['Subject'] = subject
    msg['From'] = from_address
    msg['To'] = to_address or from_address
    msg['Date'] = email.Utils.formatdate()
    msg['Message-id'] = email.Utils.make_msgid('vos-report-' + branch.nick)

    if to_address is None:
        print msg.as_string()
    else:
        smtp = smtplib.SMTP()
        smtp.connect()
        smtp.sendmail(from_address, to_address, msg.as_string())
        smtp.close()

    f = file(revid_file, 'w')
    f.write('\n'.join(history))
    f.close(); del f

    return last_revno - reported_revno

if __name__ == '__main__':
    import optparse
    cmdline = optparse.OptionParser(usage="%prog [options] [branch]")
    cmdline.add_option("-f", "--revid-file", action="store", dest="revid_file",
                       help="file with the last reported revision id (REQUIRED)")
    cmdline.add_option("-t", "--to-address", action="store", dest="to_address",
                       help="address to send email to; if omitted, will dump "
                       "to standard output")
    cmdline.add_option("-F", "--from-address",
                       action="store", dest="from_address",
                       help="address to use in the email From header; if "
                       "omitted, will use your bzr user id (`bzr whoami`)")
    cmdline.add_option('-s', '--subject', action="store", dest="subject",
                       help="email subject; if it contains $BRANCH, that will "
                       "be replaced with the branch nickname.  Default: "
                       "``Recent commits in $BRANCH''")
    cmdline.add_option("-r", "--revision", action="store", dest="from_revision",
                       help="most recent revision that should NOT be in the "
                       "report; this overrides loading a revid from REVID_FILE. "
                       "You can use it to initialize your revid file, or to "
                       "run manual tests.  It takes any bzr revision spec; "
                       "see http://bazaar-vcs.org/BzrRevisionSpec")
    cmdline.add_option("--format", action="store", dest="format",
                       help="log format (see `bzr log --help`)")
    cmdline.add_option("--format-options", action="store", dest="format_options",
                       help="extra format options (ADVANCED)")
    cmdline.add_option("--log-options", action="store", dest="log_options",
                       help="extra log options (ADVANCED)")

    class options_bag(object):
        revid_file = log_options = format_options = None
    options, args = cmdline.parse_args(values=options_bag())

    if options.revid_file is None:
        print "ERROR: must supply a revision id file"
        print
        cmdline.print_help()
        raise SystemExit

    if options.log_options is not None:
        options.log_options = eval('dict(%s)' % options.log_options)
    if options.format_options is not None:
        options.format_options = eval('dict(%s)' % options.format_options)

    if len(args) > 1:
        print "ERROR: too many arguments"
        raise SystemExit
    elif len(args) == 1:
        branch_loc = args[0]
    else:
        branch_loc = '.'

    mail_recent(branch_loc, **options.__dict__)
