#!/usr/bin/python
# this script was written for Python 2.5.2 on Windows.

CONFIG = {
    #name of the perforce counter we read/write for --daemon mode
    'counter-name' : 'collab-last-updated-change',
    'collab-url' : 'http://collaborator:8080/
    'collab-login' : 'admin',
    'collab-password' : 'secret',
    #this is the p4port collab will use when updating changelist ids
    'p4port' : 'perforce:1666',
    #change this to add login/ticket information to all calls to perforce
    'p4' : ["p4"],
    'max-changes-per-daemon-wake' : 100,
}

DEBUG = False

def debug(*args):
    if DEBUG:
        print(args)

from subprocess import Popen, PIPE
from marshal import load

def get_counter_value():
    cmd = CONFIG['p4'] + ["-G", "counter", CONFIG["counter-name"]]
    popen = Popen(cmd, stdout=PIPE)
    counter = load(popen.stdout)
    debug('perforce get counter info: ', counter)
    return int(counter['value'])

def set_counter_value(value):
    popen = Popen(CONFIG['p4'] + ["-G", "counter", CONFIG["counter-name"], str(value)], stdout=PIPE)
    counter = load(popen.stdout)
    debug('perforce set counter info: ', counter)
    
    
def get_latest_submitted():
    cmd = CONFIG['p4'] + ["-G", "changes", "-m", "1", "-s", "submitted"]
    popen = Popen(cmd, stdout=PIPE)
    change = load(popen.stdout)
    debug('perforce last submitted change: ', change)
    return int(change["change"])

def get_old_change(change):
    popen = Popen(CONFIG['p4'] + ["-G", "describe", str(change)], stdout=PIPE)
    desc = load(popen.stdout)
    debug('Change description', desc)
    if desc.has_key("oldChange"):
        return int(desc["oldChange"])
    return None

def do_update_id(old_id, new_id):
    args = [
        "--scm", "perforce",
        "--p4port", CONFIG['p4port'],
        "--user", CONFIG['collab-login'],
        "--password", CONFIG['collab-password'],
        "--non-interactive",
        "admin", "changelist", "update-id",
        str(old_id), str(new_id)
    ]
    debug('updating ', old_id, ' to ', new_id)
    if DEBUG:
        args = ["--debug"] + args
    popen = Popen(["ccollab"] + args, stdout=PIPE, stderr=PIPE)
    debug('ccollab stdout', popen.stdout.readlines())
    stderr = popen.stderr.readlines()
    debug('ccollab stderr', stderr)
    if len(stderr) != 0:
        raise Exception('\n'.join(stderr))

def do_trigger(new_id):
    debug('checking change:', new_id)
    old_id = get_old_change(new_id)
    if old_id is None:
        debug('skipping: ', new_id)
        return
    do_update_id(old_id, new_id)
    
def change_range():
    last_hit = get_counter_value()
    stop_after = get_latest_submitted()
    
    cap = CONFIG['max-changes-per-daemon-wake']
    if stop_after - last_hit > cap:
        stop_after = last_hit + cap
    
    return int(last_hit), int(stop_after)
    
def do_daemon():
    debug('DAEMON MODE')
    last_hit, stop_after = change_range()
    max_touched = last_hit
    try:
        for change in xrange(last_hit + 1, stop_after + 1):
            do_trigger(change)
            max_touched = change
    finally:
        set_counter_value(max_touched)

from sys import argv

if "--debug" in argv:
    DEBUG = True

debug(CONFIG)

try:
    do_daemon()
except Exception, e:
    print 'Error:', e
    print """
Welcome to Smart Bear (tm)'s example changelist renumbering script
This script requires a Code Collaborator client and server both at
5.0.5005 or better, and a Perforce client and server 2007.3 or better

This script will renumber changelists uploaded to the Code Collaborator(tm)
server at %(collab-url)s for the Perforce(tm) server at %(p4port)s.

You can configure this script by editing the CONFIG dictionary at it's start.

USAGE:

To run in daemon mode, set up a recurring task (e.g. cron) that runs this:

    %(script-location)s 
    
If you're experiencing any problems with the script, you can enable 
debugging with:

    %(script-location)s --debug

""" % CONFIG