#!/usr/bin/env python

"""
Test/utility code for moto-sync plugin, independent of opensync
"""

import sys, types, os.path, popen2
from optparse import OptionParser

try:
    import motosync
except ImportError:
    # motosync wasn't in our standard import path
    # try looking in the opensync python plugin dir for it
    child = popen2.Popen3('pkg-config opensync-1.0 --variable=libdir')
    libdir = child.fromchild.readline().rstrip('\n')
    if child.wait() != 0 or not os.path.isdir(libdir):
        sys.stderr.write("Error: couldn't locate OpenSync library directory\n")
        sys.exit(1)
    sys.path.append(os.path.join(libdir, 'opensync', 'python-plugins'))
    import motosync

DEFAULT_DEVICE = '/dev/rfcomm0'

def parse_args():
    p = OptionParser(version=motosync.__revision__,
                     description='moto-sync test utility')
    p.add_option('-d', '--device', dest='device',
                 help='device to access phone, defaults to %s' % DEFAULT_DEVICE)
    p.add_option('-v', '--verbose', action='store_true', dest='verbose',
                 help='log phone commands to stdout')
    p.add_option('-t', '--type', action='append', dest='objtype',
                 choices=motosync.SUPPORTED_OBJTYPES,
                 help='object type to access (defaults to all types)')
    p.add_option('-f', '--file', dest='filename',
                 help='name of backup/restore data file')
    p.add_option('--list', action='store_const', dest='mode', const='list',
                 help='list data on the phone in OpenSync XML format')
    p.add_option('--backup', action='store_const', dest='mode', const='backup',
                 help='backup entries from the phone to a file')
    p.add_option('--restore', action='store_const', dest='mode', const='restore',
                 help='restore a backup created with the --backup option')
    p.add_option('--delete', action='store_const', dest='mode', const='delete',
                 help='delete all entries on the phone')
    p.set_defaults(device=DEFAULT_DEVICE, verbose=False, filename=None,
                   mode=None, objtype=None)
    options, args = p.parse_args()
    if not options.mode:
        p.error('one of the list, backup, restore, delete actions is required')
    if options.mode in ['backup', 'restore'] and not options.filename:
        p.error('this action requires a --file argument')
    return options

def prompt_user(options):
    """Prompt the user if they are trying to write to the phone."""
    if options.mode == 'delete' or options.mode == 'restore':
        if not motosync.WRITE_ENABLED:
            print 'WARNING: writes disabled, this will not save/delete anything'
        else:
            print ('WARNING: About to %s all %s entries from the phone!'
                % (options.mode, ' & '.join(options.objtype)))
        print 'Are you sure? [yn] ',
        if sys.stdin.read(1).lower() != 'y':
            print 'Operation aborted'
            return

def pack_backup(typestr, edata):
    """pack event data into a single-line string to write to the backup file"""
    strings = []
    for val in edata:
        if type(val) == types.IntType:
            strings.append(str(val))
        else:
            strings.append('"%s"' % val.encode('utf7'))
            assert('"' not in val)
    return ','.join([typestr] + strings) + '\n'

def unpack_backup(line):
    """reverse the pack_backup function above"""
    if line[-1] == '\n':
        line = line[:-1]
    parts = []
    nextpart = ''
    wasquote = False
    inquote = False
    for c in line:
        if c == ',' and not inquote:
            if not wasquote and len(parts) > 0:
                nextpart = int(nextpart)
            parts.append(nextpart)
            nextpart = ''
            wasquote = False
        elif c == '"':
            wasquote = True
            inquote = not inquote
        else:
            nextpart = nextpart + c
    if wasquote:
        parts.append(nextpart.decode('utf7'))
    else:
        parts.append(int(nextpart))
    return parts[0], parts[1:]

def main():
    options = parse_args()
    if not options.objtype:
        options.objtype = motosync.SUPPORTED_OBJTYPES
    motosync.DEBUG_OUTPUT = options.verbose

    prompt_user(options)
    pc = motosync.PhoneComms(options.device)

    if options.mode == 'list':
        pa = motosync.PhoneAccess(pc)
        for objtype in options.objtype:
            for change in pa.list_changes(objtype):
                print change.uid
                print change.data

    if options.mode == 'delete' or options.mode == 'restore':
        if 'event' in options.objtype:
            for edata in pc.read_events():
                pc.delete_event(edata[0])
        if 'contact' in options.objtype:
            pc.read_contact_params()
            for edata in pc.read_contacts():
                pc.delete_contact(edata[0])

    if options.mode == 'backup':
        f = open(options.filename, 'w')
        if 'event' in options.objtype:
            for edata in pc.read_events():
                f.write(pack_backup('E', edata[:-1]))
                if edata[-1] != []: # exceptions
                    f.write(pack_backup('X', edata[-1]))
        if 'contact' in options.objtype:
            pc.read_contact_params()
            for edata in pc.read_contacts():
                f.write(pack_backup('C', edata))
        f.close()

    if options.mode == 'restore':
        events = []
        contacts = []
        f = open(options.filename, 'r')
        for line in f.readlines():
            typestr, edata = unpack_backup(line)
            if typestr == 'E':
                events.append(edata + [[]])
            elif typestr == 'X':
                events[-1][-1] = edata
            elif typestr == 'C':
                contacts.append(edata)
            else:
                assert(False, 'Unexpected type %s' % typestr)
        f.close()

        if 'event' in options.objtype:
            for e in events:
                pc.write_event(e)
        if 'contact' in options.objtype:
            for e in contacts:
                pc.write_contact(e)


if __name__ == "__main__":
    main()
