# Copyright 2015, Rackspace, US, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""API for the swift service.
"""
import os

from django import forms
from django.http import StreamingHttpResponse
from django.utils.http import urlunquote
from django.views.decorators.csrf import csrf_exempt
from django.views import generic
import six

from horizon import exceptions
from openstack_dashboard import api
from openstack_dashboard.api.rest import urls
from openstack_dashboard.api.rest import utils as rest_utils
from openstack_dashboard.api import swift


@urls.register
class Info(generic.View):
    """API for information about the Swift installation.
    """
    url_regex = r'swift/info/$'

    @rest_utils.ajax()
    def get(self, request):
        """Get information about the Swift installation.
        """
        capabilities = api.swift.swift_get_capabilities(request)
        return {'info': capabilities}


@urls.register
class Containers(generic.View):
    """API for swift container listing for an account
    """
    url_regex = r'swift/containers/$'

    @rest_utils.ajax()
    def get(self, request):
        """Get the list of containers for this account

        TODO(neillc): Add pagination
        """
        containers, has_more = api.swift.swift_get_containers(request)
        containers = [container.to_dict() for container in containers]
        return {'items': containers, 'has_more': has_more}


@urls.register
class Container(generic.View):
    """API for swift container level information
    """

    url_regex = r'swift/containers/(?P<container>[^/]+)/metadata/$'

    @rest_utils.ajax()
    def get(self, request, container):
        """Get the container details
        """
        return api.swift.swift_get_container(request, container).to_dict()

    @rest_utils.ajax()
    def post(self, request, container):
        metadata = {}

        if 'is_public' in request.DATA:
            metadata['is_public'] = request.DATA['is_public']

        # This will raise an exception if the container already exists
        try:
            api.swift.swift_create_container(request, container,
                                             metadata=metadata)
        except exceptions.AlreadyExists as e:
            # 409 Conflict
            return rest_utils.JSONResponse(str(e), 409)

        return rest_utils.CreatedResponse(
            u'/api/swift/containers/%s' % container,
        )

    @rest_utils.ajax()
    def delete(self, request, container):
        try:
            api.swift.swift_delete_container(request, container)
        except exceptions.Conflict as e:
            # It cannot be deleted if it's not empty.
            return rest_utils.JSONResponse(str(e), 409)

    @rest_utils.ajax(data_required=True)
    def put(self, request, container):
        metadata = {'is_public': request.DATA['is_public']}
        api.swift.swift_update_container(request, container, metadata=metadata)


@urls.register
class Objects(generic.View):
    """API for a list of swift objects
    """
    url_regex = r'swift/containers/(?P<container>[^/]+)/objects/$'

    @rest_utils.ajax()
    def get(self, request, container):
        """Get object information.

        :param request:
        :param container:
        :return:
        """
        path = request.GET.get('path')
        if path is not None:
            path = urlunquote(path)

        objects = api.swift.swift_get_objects(
            request,
            container,
            prefix=path
        )

        # filter out the folder from the listing if we're filtering for
        # contents of a (pseudo) folder
        contents = [{
            'path': o.subdir if isinstance(o, swift.PseudoFolder) else o.name,
            'name': o.name.split('/')[-1],
            'bytes': o.bytes,
            'is_subdir': isinstance(o, swift.PseudoFolder),
            'is_object': not isinstance(o, swift.PseudoFolder),
            'content_type': getattr(o, 'content_type', None)
        } for o in objects[0] if o.name != path]
        return {'items': contents}


class UploadObjectForm(forms.Form):
    file = forms.FileField(required=False)


@urls.register
class Object(generic.View):
    """API for a single swift object or pseudo-folder
    """
    url_regex = r'swift/containers/(?P<container>[^/]+)/object/' \
        '(?P<object_name>.+)$'

    # note: not an AJAX request - the body will be raw file content
    @csrf_exempt
    def post(self, request, container, object_name):
        """Create or replace an object or pseudo-folder

        :param request:
        :param container:
        :param object_name:

        If the object_name (ie. POST path) ends in a '/' then a folder is
        created, rather than an object. Any file content passed along with
        the request will be ignored in that case.

        POST parameter:

        :param file: the file data for the upload.

        :return:
        """
        form = UploadObjectForm(request.POST, request.FILES)
        if not form.is_valid():
            raise rest_utils.AjaxError(500, 'Invalid request')

        data = form.clean()

        if object_name[-1] == '/':
            result = api.swift.swift_create_pseudo_folder(
                request,
                container,
                object_name
            )
        else:
            result = api.swift.swift_upload_object(
                request,
                container,
                object_name,
                data['file']
            )

        return rest_utils.CreatedResponse(
            u'/api/swift/containers/%s/object/%s' % (container, result.name)
        )

    @rest_utils.ajax()
    def delete(self, request, container, object_name):
        if object_name[-1] == '/':
            try:
                api.swift.swift_delete_folder(request, container, object_name)
            except exceptions.Conflict as e:
                # In case the given object is pseudo folder
                # It cannot be deleted if it's not empty.
                return rest_utils.JSONResponse(str(e), 409)
        else:
            api.swift.swift_delete_object(request, container, object_name)

    def get(self, request, container, object_name):
        """Get the object contents.
        """
        obj = api.swift.swift_get_object(
            request,
            container,
            object_name
        )

        # Add the original file extension back on if it wasn't preserved in the
        # name given to the object.
        filename = object_name.rsplit(api.swift.FOLDER_DELIMITER)[-1]
        if not os.path.splitext(obj.name)[1] and obj.orig_name:
            name, ext = os.path.splitext(obj.orig_name)
            filename = "%s%s" % (filename, ext)
        response = StreamingHttpResponse(obj.data)
        safe = filename.replace(",", "")
        if six.PY2:
            safe = safe.encode('utf-8')
        response['Content-Disposition'] = 'attachment; filename="%s"' % safe
        response['Content-Type'] = 'application/octet-stream'
        response['Content-Length'] = obj.bytes
        return response


@urls.register
class ObjectMetadata(generic.View):
    """API for a single swift object
    """
    url_regex = r'swift/containers/(?P<container>[^/]+)/metadata/' \
        '(?P<object_name>.+)$'

    @rest_utils.ajax()
    def get(self, request, container, object_name):
        return api.swift.swift_get_object(
            request,
            container_name=container,
            object_name=object_name,
            with_data=False
        ).to_dict()


@urls.register
class ObjectCopy(generic.View):
    """API to copy a swift object
    """
    url_regex = r'swift/containers/(?P<container>[^/]+)/copy/' \
        '(?P<object_name>.+)$'

    @rest_utils.ajax()
    def post(self, request, container, object_name):
        dest_container = request.DATA['dest_container']
        dest_name = request.DATA['dest_name']
        try:
            result = api.swift.swift_copy_object(
                request,
                container,
                object_name,
                dest_container,
                dest_name
            )
        except exceptions.AlreadyExists as e:
            return rest_utils.JSONResponse(str(e), 409)
        return rest_utils.CreatedResponse(
            u'/api/swift/containers/%s/object/%s' % (dest_container,
                                                     result.name)
        )

[((38, 5, 38, 22), 'openstack_dashboard.api.rest.utils.ajax', 'rest_utils.ajax', ({}, {}), '()', True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((52, 5, 52, 22), 'openstack_dashboard.api.rest.utils.ajax', 'rest_utils.ajax', ({}, {}), '()', True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((70, 5, 70, 22), 'openstack_dashboard.api.rest.utils.ajax', 'rest_utils.ajax', ({}, {}), '()', True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((76, 5, 76, 22), 'openstack_dashboard.api.rest.utils.ajax', 'rest_utils.ajax', ({}, {}), '()', True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((95, 5, 95, 22), 'openstack_dashboard.api.rest.utils.ajax', 'rest_utils.ajax', ({}, {}), '()', True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((103, 5, 103, 40), 'openstack_dashboard.api.rest.utils.ajax', 'rest_utils.ajax', (), '', True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((115, 5, 115, 22), 'openstack_dashboard.api.rest.utils.ajax', 'rest_utils.ajax', ({}, {}), '()', True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((147, 11, 147, 42), 'django.forms.FileField', 'forms.FileField', (), '', False, 'from django import forms\n'), ((200, 5, 200, 22), 'openstack_dashboard.api.rest.utils.ajax', 'rest_utils.ajax', ({}, {}), '()', True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((244, 5, 244, 22), 'openstack_dashboard.api.rest.utils.ajax', 'rest_utils.ajax', ({}, {}), '()', True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((261, 5, 261, 22), 'openstack_dashboard.api.rest.utils.ajax', 'rest_utils.ajax', ({}, {}), '()', True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((42, 23, 42, 64), 'openstack_dashboard.api.swift.swift_get_capabilities', 'api.swift.swift_get_capabilities', ({(42, 56, 42, 63): 'request'}, {}), '(request)', False, 'from openstack_dashboard import api\n'), ((58, 31, 58, 70), 'openstack_dashboard.api.swift.swift_get_containers', 'api.swift.swift_get_containers', ({(58, 62, 58, 69): 'request'}, {}), '(request)', False, 'from openstack_dashboard import api\n'), ((91, 15, 93, 9), 'openstack_dashboard.api.rest.utils.CreatedResponse', 'rest_utils.CreatedResponse', ({(92, 12, 92, 51): "(u'/api/swift/containers/%s' % container)"}, {}), "(u'/api/swift/containers/%s' % container)", True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((106, 8, 106, 79), 'openstack_dashboard.api.swift.swift_update_container', 'api.swift.swift_update_container', (), '', False, 'from openstack_dashboard import api\n'), ((127, 18, 131, 9), 'openstack_dashboard.api.swift.swift_get_objects', 'api.swift.swift_get_objects', (), '', False, 'from openstack_dashboard import api\n'), ((196, 15, 198, 9), 'openstack_dashboard.api.rest.utils.CreatedResponse', 'rest_utils.CreatedResponse', ({(197, 12, 197, 76): "(u'/api/swift/containers/%s/object/%s' % (container, result.name))"}, {}), "(u'/api/swift/containers/%s/object/%s' % (\n    container, result.name))", True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((215, 14, 219, 9), 'openstack_dashboard.api.swift.swift_get_object', 'api.swift.swift_get_object', ({(216, 12, 216, 19): 'request', (217, 12, 217, 21): 'container', (218, 12, 218, 23): 'object_name'}, {}), '(request, container, object_name)', False, 'from openstack_dashboard import api\n'), ((227, 19, 227, 50), 'django.http.StreamingHttpResponse', 'StreamingHttpResponse', ({(227, 41, 227, 49): 'obj.data'}, {}), '(obj.data)', False, 'from django.http import StreamingHttpResponse\n'), ((275, 15, 278, 9), 'openstack_dashboard.api.rest.utils.CreatedResponse', 'rest_utils.CreatedResponse', ({(276, 12, 277, 65): "(u'/api/swift/containers/%s/object/%s' % (dest_container, result.name))"}, {}), "(u'/api/swift/containers/%s/object/%s' % (\n    dest_container, result.name))", True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((85, 12, 86, 63), 'openstack_dashboard.api.swift.swift_create_container', 'api.swift.swift_create_container', (), '', False, 'from openstack_dashboard import api\n'), ((98, 12, 98, 64), 'openstack_dashboard.api.swift.swift_delete_container', 'api.swift.swift_delete_container', ({(98, 45, 98, 52): 'request', (98, 54, 98, 63): 'container'}, {}), '(request, container)', False, 'from openstack_dashboard import api\n'), ((125, 19, 125, 35), 'django.utils.http.urlunquote', 'urlunquote', ({(125, 30, 125, 34): 'path'}, {}), '(path)', False, 'from django.utils.http import urlunquote\n'), ((178, 18, 178, 62), 'openstack_dashboard.api.rest.utils.AjaxError', 'rest_utils.AjaxError', ({(178, 39, 178, 42): '(500)', (178, 44, 178, 61): '"""Invalid request"""'}, {}), "(500, 'Invalid request')", True, 'from openstack_dashboard.api.rest import utils as rest_utils\n'), ((183, 21, 187, 13), 'openstack_dashboard.api.swift.swift_create_pseudo_folder', 'api.swift.swift_create_pseudo_folder', ({(184, 16, 184, 23): 'request', (185, 16, 185, 25): 'container', (186, 16, 186, 27): 'object_name'}, {}), '(request, container, object_name)', False, 'from openstack_dashboard import api\n'), ((189, 21, 194, 13), 'openstack_dashboard.api.swift.swift_upload_object', 'api.swift.swift_upload_object', ({(190, 16, 190, 23): 'request', (191, 16, 191, 25): 'container', (192, 16, 192, 27): 'object_name', (193, 16, 193, 28): "data['file']"}, {}), "(request, container, object_name, data['file'])", False, 'from openstack_dashboard import api\n'), ((210, 12, 210, 74), 'openstack_dashboard.api.swift.swift_delete_object', 'api.swift.swift_delete_object', ({(210, 42, 210, 49): 'request', (210, 51, 210, 60): 'container', (210, 62, 210, 73): 'object_name'}, {}), '(request, container, object_name)', False, 'from openstack_dashboard import api\n'), ((225, 24, 225, 55), 'os.path.splitext', 'os.path.splitext', ({(225, 41, 225, 54): 'obj.orig_name'}, {}), '(obj.orig_name)', False, 'import os\n'), ((266, 21, 272, 13), 'openstack_dashboard.api.swift.swift_copy_object', 'api.swift.swift_copy_object', ({(267, 16, 267, 23): 'request', (268, 16, 268, 25): 'container', (269, 16, 269, 27): 'object_name', (270, 16, 270, 30): 'dest_container', (271, 16, 271, 25): 'dest_name'}, {}), '(request, container, object_name, dest_container,\n    dest_name)', False, 'from openstack_dashboard import api\n'), ((74, 15, 74, 64), 'openstack_dashboard.api.swift.swift_get_container', 'api.swift.swift_get_container', ({(74, 45, 74, 52): 'request', (74, 54, 74, 63): 'container'}, {}), '(request, container)', False, 'from openstack_dashboard import api\n'), ((204, 16, 204, 78), 'openstack_dashboard.api.swift.swift_delete_folder', 'api.swift.swift_delete_folder', ({(204, 46, 204, 53): 'request', (204, 55, 204, 64): 'container', (204, 66, 204, 77): 'object_name'}, {}), '(request, container, object_name)', False, 'from openstack_dashboard import api\n'), ((246, 15, 251, 9), 'openstack_dashboard.api.swift.swift_get_object', 'api.swift.swift_get_object', (), '', False, 'from openstack_dashboard import api\n'), ((224, 15, 224, 41), 'os.path.splitext', 'os.path.splitext', ({(224, 32, 224, 40): 'obj.name'}, {}), '(obj.name)', False, 'import os\n')]