#!/usr/bin/env python

import sys
import re
import time
import logging
import boto.s3
from boto.exception import S3ResponseError
from qav.listpack import ListPack

from umobj.utils import umobj_logging, umobj_init, \
    umobj_get_bucket_key_pair_from_string, print_word_list, sizeof_fmt
from umobj.options import umobj_parser, get_logging_level
from umobj.obj import Obj
from umobj.acl import all_users, get_formatted_grants_from_key
from umobj.key import get_metadata


def list_bucket(b, prefix=None, public=False, long_list=False, md5=False):
    """List everything in a bucket"""
    from boto.s3.prefix import Prefix
    from boto.s3.key import Key
    total = 0
    query = b
    if prefix:
        if not prefix.endswith("/"):
            prefix = prefix + "/"
        query = b.list(prefix=prefix, delimiter="/")
        print "%s" % prefix
    num = 0
    for k in query:
        if not long_list:
            print k.name
            continue
        num += 1
        mode = "-rwx---"
        if isinstance(k, Prefix):
            mode = "drwxr--"
            size = 0
        else:
            size = k.size
            try:
                for g in k.get_acl().acl.grants:
                    if g.id is None:
                        if g.permission == "READ":
                            mode = "-rwxr--"
                        elif g.permission == "FULL_CONTROL":
                            mode = "-rwxrwx"
            except S3ResponseError:
                mode = "-------"
        if isinstance(k, Key):
            d = time.strptime(k.last_modified, "%Y-%m-%dT%H:%M:%S.%fZ")
            print "%s\t%s\t%010s\t%s" % \
                  (mode, time.strftime("%m/%d/%Y %H:%M:%S", d),
                   sizeof_fmt(size), k.name)
            if md5:
                etag = k.etag.strip('\"')
                if re.search('-\d+', etag):
                    print "  MD5: Multipart"
                else:
                    print "  MD5: %s" % k.etag.strip('\"')
            try:
                grants = get_formatted_grants_from_key(k)
                print '  ACLs: ' + print_word_list(grants)
                metadata = get_metadata(b, k.name)
                if metadata:
                    data = [(mdk, mdv) for mdk, mdv in metadata.iteritems()]
                    lp = ListPack(data, indentation=4)
                    print '  Metadata:'
                    print lp
            except S3ResponseError:
                pass
            if public:
                try:
                    for grant in k.get_acl().acl.grants:
                        if grant.permission == 'READ':
                            if grant.uri == all_users:
                                print "  %s" % k.generate_url(expires_in=0,
                                                              query_auth=False)
                except S3ResponseError:
                    pass
        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
    if long_list:
        print "=" * 80
        if num == 1:
            print "\t\tTOTAL:  \t%010s \t%i File" % (sizeof_fmt(total), num)
        else:
            print "\t\tTOTAL:  \t%010s \t%i Files" % (sizeof_fmt(total), num)


def list_buckets(s3, long_list=False):
    """List all the buckets"""
    for b in s3.get_all_buckets():
        print b.name
        if long_list:
            try:
                grants = get_formatted_grants_from_key(b)
                print print_word_list(grants, preface=' ACLs:')
            except S3ResponseError:
                pass


if __name__ == "__main__":
    umobj_init()

    description = 'List Object(s)'
    parser = umobj_parser(description=description)
    parser.add_long(help='Show long listing including ACLs')
    parser.add_public(help='Show public URL for files if available')
    parser.add_md5(help='Show MD5 sum if available')
    parser.add_s3path(number='?', help='<BUCKET>[:KEY_PREFIX]')
    args = parser.parse_args()

    umobj_logging(get_logging_level(args))  # set up logging

    logging.info("Running %s" % sys.argv)

    if not Obj.connect(host=args.host,
                       port=args.port,
                       access_key=args.access_key,
                       secret_key=args.secret_key):
        logging.error('Unable to contact object store.')
        sys.exit(1)

    if args.S3PATH is None:
        list_buckets(Obj.conn, long_list=args.long_list)
    else:
        try:
            bucket_name, key_prefix = \
                umobj_get_bucket_key_pair_from_string(args.S3PATH)
            bucket = Obj.conn.get_bucket(bucket_name)
        except boto.exception.S3ResponseError:
            print "ERROR: bucket %s does not exist." % bucket_name
            sys.exit(1)
        if not key_prefix:
            list_bucket(bucket, public=args.public,
                        long_list=args.long_list, md5=args.md5)
        else:
            list_bucket(bucket, key_prefix, public=args.public,
                        long_list=args.long_list, md5=args.md5)
