#!/usr/bin/python3

import filecmp
import optparse
import os
import shutil
import sys

quiet = False

def main():
    global quiet

    parser = optparse.OptionParser(
        usage = 'usage: %prog dst src',
        description = 'Updates dst with src. If they differ, copies dst to src. '
                      'Deletes src either way.')
    parser.add_option("--update", action="store_true", dest="update",
                      help=("given dst src - update dst with src. "
                            "If they differ, copy dst to src. "
                            "Removes src either way"))
    parser.add_option("--copy", action="store_true", dest="copy",
                      help=("given src dst - copy src to dst. "
                            "Only updates files that differ."
                            "Multiple src args are OK if dst is a directory."))
    parser.add_option("--quiet", action="store_true", dest="quiet",
                      help=("Do not print out when copying a file."))

    (options, args) = parser.parse_args()


    if options.quiet:
        quiet = True

    if options.update:
        if len(args) != 2:
            parser.error("--update requires exactly 2 arguments")
        (dst, src) = args
        update(dst, src)

    elif options.copy:
        if len(args) < 2:
            parser.error("--copy requires at least 2 arguments")
        if len(args) == 2:
            (src, dst) = args
            copy(src, dst)
        else:
            srcs = args
            dst = srcs.pop()
            if not os.path.isdir(dst):
                parser.error("dst must be a directory for multi-file copy")
            else:
                for src in srcs:
                    copy(src, dst)

def update(dst, src):
    if not os.path.exists(dst) or not filecmp.cmp(src, dst):
        if not quiet:
            sys.stdout.write("Updating {0} from {1}\n".format(dst, src))
        shutil.copyfile(src, dst)

    os.remove(src)

def copy(src, dst):
    """ copy src into dst.
        The copy handles these two cases in a special way:
          * If dst is a directory containing a file not in a src directory,
            that file will be removed.
          * If dst contains a file that is the same as the file in src,
            the file will not be copied (so that it is not "updated"
            for follow-on build steps).
        Additionally, this function handles the case where src is a file
        and dst is a directory similarly to the 'cp' command.
    """

    if not os.path.isdir(src):
        # handle regular files

        if os.path.isdir(dst):
            # handle copying a file to a directory
            # like cp some/path/to/file somedir/
            # in that case, use dst=somedir/file
            sub = os.path.basename(src)
            dst = os.path.join(dst, sub)
        # do the copy
        if not os.path.exists(dst) or not filecmp.cmp(src, dst):
            if not quiet:
                sys.stdout.write("Copying {0} to {1}\n".format(src, dst))
            shutil.copyfile(src, dst)
    else:
        # handle directories
        if not os.path.exists(dst):
            if not quiet:
                sys.stdout.write("Copying dir {0} to {1}\n".format(src, dst))
            shutil.copytree(src, dst)
        else:
            srclist = sorted(os.listdir(src))
            srcset = set(srclist)
            # delete anything in dst that is not in src
            dstlist = sorted(os.listdir(dst))
            for sub in dstlist:
                if not sub in srcset:
                    rm = os.path.join(dst, sub)
                    if os.path.isdir(rm):
                        if not quiet:
                            sys.stdout.write("Removing dir {0} not present in {1}\n".format(rm, src))
                        shutil.rmtree(rm)
                    else:
                        if not quiet:
                            sys.stdout.write("Removing {0} not present in {1}\n".format(rm, src))
                        os.remove(rm)
            # recursively copy anything from src
            for sub in srclist:
                copy(os.path.join(src, sub), os.path.join(dst, sub))

if __name__ == '__main__':
    main()
