import boto
from boto.exception import S3ResponseError
from boto.s3.connection import OrdinaryCallingFormat


def sizeof_fmt(num):
    for x in ['b ', 'KB', 'MB', 'GB', 'TB', 'XB']:
        if num < 1024.0:
            return "%3.1f %s" % (num, x)
        num /= 1024.0
    return "%3.1f %s" % (num, x)


def list_bucket(b, prefix=None, marker=None):
    """List everything in a bucket"""
    from boto.s3.prefix import Prefix
    from boto.s3.key import Key
    total = 0

    if prefix:
        if not prefix.endswith("/"):
            prefix = prefix + "/"
        query = b.list(prefix=prefix, delimiter="/", marker=marker)
        print("%s" % prefix)
    else:
        query = b.list(delimiter="/", marker=marker)

    num = 0
    for k in query:
        num += 1
        mode = "-rwx---"
        if isinstance(k, Prefix):
            mode = "drwxr--"
            size = 0
        else:
            size = k.size
            for g in k.get_acl().acl.grants:
                if g.id == None:
                    if g.permission == "READ":
                        mode = "-rwxr--"
                    elif g.permission == "FULL_CONTROL":
                        mode = "-rwxrwx"
        if isinstance(k, Key):
            print("%s\t%s\t%010s\t%s" % (mode, k.last_modified,
                                         sizeof_fmt(size), k.name))
        else:
           #If it's not a Key object, it doesn't have a last_modified time, so
           #print nothing instead
            print("%s\t%s\t%010s\t%s" % (mode, ' ' * 24,
                                         sizeof_fmt(size), k.name))
        total += size
    print ("=" * 80)
    print ("\t\tTOTAL:  \t%010s \t%i Files" % (sizeof_fmt(total), num))


def list_buckets(s3, display_tags=False):
    """List all the buckets"""
    for b in s3.get_all_buckets():
        print(b.name)
        if display_tags:
            try:
                tags = b.get_tags()
                for tag in tags[0]:
                    print("   %s:%s" % (tag.key, tag.value))
            except S3ResponseError as e:
                if e.status != 404:
                    raise


def main():
    import optparse
    import sys

    usage = "usage: %prog [options] [BUCKET1] [BUCKET2]"
    description = "List all S3 buckets OR list keys in the named buckets"
    parser = optparse.OptionParser(description=description, usage=usage)
    parser.add_option('-m', '--marker',
                      help='The S3 key where the listing starts after it.')
    parser.add_option('-t', '--tags', action='store_true',
                      help='Display tags when listing all buckets.')
    options, buckets = parser.parse_args()
    marker = options.marker

    if not buckets:
        list_buckets(boto.connect_s3(), options.tags)
        sys.exit(0)

    if options.tags:
        print("-t option only works for the overall bucket list")
        sys.exit(1)

    pairs = []
    mixedCase = False
    for name in buckets:
        if "/" in name:
            pairs.append(name.split("/", 1))
        else:
            pairs.append([name, None])
        if pairs[-1][0].lower() != pairs[-1][0]:
            mixedCase = True

    if mixedCase:
        s3 = boto.connect_s3(calling_format=OrdinaryCallingFormat())
    else:
        s3 = boto.connect_s3()

    for name, prefix in pairs:
        list_bucket(s3.get_bucket(name), prefix, marker=marker)


if __name__ == "__main__":
    main()
