#!/usr/bin/env python
#
# rsync.py
#
# usage:
#   rsync.py get dir1/a dir2/b
#   rsync.py put work
#

import sys
stderr = sys.stderr

RSYNC_CMDLINE = [
  'rsync',
  '-auvbz',
  '--delete',
  '--exclude', 'LOCAL',
  ]


##  prepare_rsync
##
class DirectoryStructureError(ValueError): pass

def prepare_rsync(cmd, pwd, local_home, backup_base, host, args, debug=0):
  import os.path
  
  pwd = os.path.normpath(pwd)
  local_home = os.path.normpath(local_home)

  def relpath(path1, path2):
    if not path2.startswith(path1):
      raise DirectoryStructureError('%r is not under %r' % (path2, path1))
    if len(path1) < len(path2):
      return os.path.normpath('./'+path2[len(path1):])
    else:
      return '.'

  def isconsistent(paths):
    parent = None
    for p in paths:
      (head,_) = os.path.split(p)
      if parent and head != parent: return False
      parent = head
    return True

  def escape(p):
    return p.replace("'", "\\'")
  
  paths = [ os.path.normpath(os.path.join(pwd, p)) for p in args ]
  if not isconsistent(paths):
    raise DirectoryStructureError('directory not even: %r' % paths)
  common = os.path.dirname(os.path.commonprefix(paths))
  rel_paths = [ relpath(local_home, p) for p in paths ]
  rel_common = relpath(local_home, common)
  os.chdir(local_home)
  if debug:
    print >>stderr, 'rel_common=%r, rel_paths=%r' % (rel_common, rel_paths)
    
  # get
  if cmd == 'get':
    return (os.path.join(os.path.join(local_home, backup_base), rel_common),
            ['%s:%s' % (host, ' '.join(["'%s'" % escape(p) for p in rel_paths]))],
            common+'/')

  # put
  if cmd == 'put':
    for p in paths:
      if not os.path.exists(p):
        raise IOError('not found: %r' % p)
    return (os.path.join(os.path.join('.', backup_base), rel_common),
            paths,
            '%s:%s/' % (host, rel_common))

  raise ValueError('invalid command: %r' % cmd)

def unittest():
  home = '/home/yusuke'
  base = '.old'
  host = 'dummyhost'
  assert (prepare_rsync('get', '/home/yusuke/Mail', home, base, host, ['.']) ==
          ('/home/yusuke/.old/.', ["dummyhost:'Mail'"], '/home/yusuke/'))
  assert (prepare_rsync('put', '/home/yusuke/Mail', home, base, host, ['.']) ==
          ('./.old/.', ['/home/yusuke/Mail'], 'dummyhost:./'))
  assert (prepare_rsync('get', '/home/yusuke', home, base, host, ['work/p', 'work/m']) ==
          ('/home/yusuke/.old/work', ["dummyhost:'work/p' 'work/m'"], '/home/yusuke/work/'))
  assert (prepare_rsync('put', '/home/yusuke', home, base, host, ['work/p', 'work/m']) ==
          ('./.old/work', ['/home/yusuke/work/p', '/home/yusuke/work/m'], 'dummyhost:work/'))
  return

#unittest()


# main
def main(argv):
  import os, getopt
  global debug
  def usage():
    print 'usage: %s [-d] [-b backup_base] {get|put} host file ...' % argv[0]
    return 100
  try:
    (opts, args) = getopt.getopt(argv[1:], 'db:')
  except getopt.GetoptError:
    return usage()
  if len(args) < 2:
    return usage()
  debug = 1
  backup_base = '.old'
  for (k, v) in opts:
    if k == '-d': debug += 1
    elif k == '-b': backup_base = v
  pwd = os.environ['PWD']
  home = os.environ['HOME']
  (cmd, host) = args[:2]
  #
  try:
    (backup_dir, src, dest) = prepare_rsync(cmd, pwd, home, backup_base, host,
                                            args[2:], debug=debug)
  except DirectoryStructureError, e:
    print >>stderr, e
    return 1
  cmdline = RSYNC_CMDLINE
  if backup_base:
    cmdline += [ '--backup-dir', backup_dir+'/' ]
  cmdline += src + [dest]
  if debug:
    print >>stderr, 'cmdline: %r' % cmdline
  os.execvp(cmdline[0], cmdline)
  return

if __name__ == '__main__': sys.exit(main(sys.argv))
