# encoding: utf-8
from asyncio import coroutine

import aiohttp

from boto.s3.connection import S3Connection

from waterbutler.exceptions import FileNotFoundError
from waterbutler.providers import core


@core.register_provider('s3')
class S3Provider(core.BaseProvider):

    def __init__(self, access_key, secret_key, bucket, **kwargs):
        self.connection = S3Connection(access_key, secret_key)
        self.bucket = self.connection.get_bucket(bucket, validate=False)

    @coroutine
    def download(self, path, **kwargs):
        key = self.bucket.new_key(path)
        url = key.generate_url(100)
        resp = yield from aiohttp.request('GET', url)
        if resp.status != 200:
            raise FileNotFoundError(path)

        return core.ResponseWrapper(resp)

    @coroutine
    def upload(self, obj, path):
        key = self.bucket.new_key(path)
        url = key.generate_url(100, 'PUT')
        resp = yield from aiohttp.request(
            'PUT', url,
            data=obj.content,
            headers={'Content-Length': obj.size}
        )

        return core.ResponseWrapper(resp)

    @coroutine
    def delete(self, path):
        key = self.bucket.new_key(path)
        url = key.generate_url(100, 'DELETE')
        resp = yield from aiohttp.request('DELETE', url)

        return resp

    @coroutine
    def metadata(self, path):
        url = self.bucket.generate_url(100, 'GET')
        resp = yield from aiohttp.request('GET', url, params={'prefix': path, 'delimiter': '/'})

        if resp.status_code == 404:
            raise Exception('TODO NOT FOUND ERROR')

        content = yield from resp.read_and_close()
        obj = lxml.objectify(content)

        files = [
            self.key_to_dict(k)
            for k in getattr(obj, 'Contents', [])
        ]

        folders = [
            self.key_to_dict(p)
            for p in getattr(obj, 'CommonPrefixes', [])
        ]

        if len(folders) == 0 and len(files) == 1:
            return files[0]

        return files + folders

    def key_to_dict(self, key, children=[]):
        return {
            'content': children,
            'provider': 's3',
            'kind': 'file',
            'name': os.path.split(fo.Key)[1],
            'size': fo.Size,
            'path': fo.Key,
            'modified': fo.LastModified,
            'extra': {
                'md5': fo.ETag,
            }
        }

    def prefix_to_dict(self, prefix, children=[]):
        def getname(st):
            sp = st.split('/')
            if not sp[-1]:
                return sp[-2]
            return sp[-1]

        return {
            'contents': children,
            'provider': 's3',
            'kind': 'folder',
            'name': getname(prefix.Prefix),
            'path': prefix.Prefix,
            'modified': None,
            'size': None,
            'extra': {}
        }

@@ -263,16 +263,60 @@
 ovider):
+%0A    %22%22%22Provider for the Amazon's S3%0A    %22%22%22
 %0A%0A    de
@@ -367,32 +367,192 @@
 ket, **kwargs):%0A
+        %22%22%22%0A        :param str access_key: AWS AccessKey%0A        :param str secret_key: AWS SecretKey%0A        :param str bucket: AWS S3 Bucket name%0A        %22%22%22%0A
         self.con
@@ -719,32 +719,389 @@
 ath, **kwargs):%0A
+        %22%22%22Returns a ResponseWrapper (Stream) for the specified path%0A        raises FileNotFoundError if the status from S3 is not 200%0A%0A        :param str path: Path to the key you want to download%0A        :param dict **kwargs: Additional arguments that are ignored%0A        :rtype ResponseWrapper:%0A        :raises: waterbutler.FileNotFoundError%0A        %22%22%22%0A
         key = se
@@ -1338,32 +1338,32 @@
 %0A    @coroutine%0A
-
     def upload(s
@@ -1371,32 +1371,246 @@
 lf, obj, path):%0A
+        %22%22%22Uploads the given stream to S3%0A        :param ResponseWrapper obj: The stream to put to S3%0A        :param str path: The full path of the key to upload to/into%0A        :rtype ResponseWrapper:%0A        %22%22%22%0A
         key = se

Add docstrings