#!/usr/bin/env python
# -*- coding: utf-8 -*-

###############################################################################
#  Copyright 2014 Kitware 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.
###############################################################################

import json
import os
import re
import requests
import six
import threading

from girder import events
from girder.utility import assetstore_utilities

from .. import base
from .. import mongo_replicaset
from girder.utility.s3_assetstore_adapter import botoConnectS3


Chunk1, Chunk2 = ('hello ', 'world')


def setUpModule():
    base.startServer(mockS3=True)


def tearDownModule():
    base.stopServer()


def _send_s3_request(request, data=None):
    """
    Send a request to an S3 server.
    :param request: a dictionary of headers, method, and url.
    :param data: data to include in the request.
    :returns: the result of the request.
    """
    if request['method'] == 'PUT':
        req = requests.put(url=request['url'], data=data,
                           headers=request.get('headers', {}))
    elif request['method'] == 'POST':
        req = requests.post(url=request['url'], data=data,
                            headers=request.get('headers', {}))
    return req


class UploadTestCase(base.TestCase):
    def setUp(self):
        base.TestCase.setUp(self)
        admin = {
            'email': 'admin@email.com',
            'login': 'admin',
            'firstName': 'Admin',
            'lastName': 'Admin',
            'password': 'adminpassword',
            'admin': True
        }
        self.admin = self.model('user').createUser(**admin)
        user = {
            'email': 'good@email.com',
            'login': 'goodlogin',
            'firstName': 'First',
            'lastName': 'Last',
            'password': 'goodpassword',
            'admin': False
        }
        self.user = self.model('user').createUser(**user)
        folders = self.model('folder').childFolders(
            parent=self.user, parentType='user', user=self.user)
        for folder in folders:
            if folder['public'] is True:
                self.folder = folder

    def _uploadFile(self, name, partial=False, largeFile=False):
        """
        Upload a file either completely or partially.
        :param name: the name of the file to upload.
        :param partial: the number of steps to complete in the uploads: 0
                        initializes the upload, 1 uploads 1 chunk, etc.  False
                        to complete the upload.
        :param largeFile: if True, upload a file that is > 32Mb
        :returns: the upload record which includes the upload id.
        """
        if not largeFile:
            chunk1 = Chunk1
            chunk2 = Chunk2
        else:
            chunk1 = '-' * (1024 * 1024 * 32)
            chunk2 = '-' * (1024 * 1024 * 1)
        resp = self.request(
            path='/file', method='POST', user=self.user, params={
                'parentType': 'folder',
                'parentId': self.folder['_id'],
                'name': name,
                'size': len(chunk1) + len(chunk2),
                'mimeType': 'text/plain'
            })
        self.assertStatusOk(resp)
        upload = resp.json
        if partial is not False and partial == 0:
            return upload
        if 's3' not in upload:
            fields = [('offset', 0), ('uploadId', upload['_id'])]
            files = [('chunk', 'helloWorld.txt', chunk1)]
            resp = self.multipartRequest(
                path='/file/chunk', user=self.user, fields=fields, files=files)
            self.assertStatusOk(resp)
            if partial is not False:
                return resp.json
            fields = [('offset', len(chunk1)), ('uploadId', upload['_id'])]
            files = [('chunk', 'helloWorld.txt', chunk2)]
            resp = self.multipartRequest(
                path='/file/chunk', user=self.user, fields=fields, files=files)
            self.assertStatusOk(resp)
            return upload
        # s3 uses a different method for uploading chunks
        # This has no error checking at all
        if not upload['s3']['chunked']:
            _send_s3_request(upload['s3']['request'], chunk1+chunk2)
            if partial is not False:
                return
        else:
            chunk1 = chunk1+chunk2
            s3resp = _send_s3_request(upload['s3']['request'])
            matches = re.search('<UploadId>(.*)</UploadId>', s3resp.text)
            s3uploadId = matches.groups()[0]
            offset = 0
            chunkN = 1
            while len(chunk1):
                params = {'offset': offset, 'uploadId': upload['_id']}
                params["chunk"] = json.dumps({'s3UploadId': s3uploadId,
                                              'partNumber': chunkN})
                resp = self.request(path='/file/chunk', method='POST',
                                    user=self.user, params=params)
                self.assertStatusOk(resp)
                upload = resp.json
                if len(chunk1) > upload['s3']['chunkLength']:
                    chunk2 = chunk1[upload['s3']['chunkLength']:]
                    chunk1 = chunk1[:upload['s3']['chunkLength']]
                else:
                    chunk2 = ""
                _send_s3_request(upload['s3']['request'], chunk1)
                chunk1 = chunk2
                if partial is not False:
                    partial -= 1
                chunkN += 1
                if partial is not False and not partial: