#!/usr/bin/env python
#
# Copyright 2007 Google 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.
#




"""Stubs for File service."""


import base64
import datetime
import hashlib
import os
import random
import string
import StringIO
import tempfile
import time

from google.appengine.api import apiproxy_stub
from google.appengine.api import datastore
from google.appengine.api import datastore_errors
from google.appengine.api import blobstore as api_blobstore
from google.appengine.api.blobstore import blobstore_stub
from google.appengine.api.files import blobstore as files_blobstore
from google.appengine.api.files import file as files
from google.appengine.api.files import file_service_pb
from google.appengine.api.files import gs
from google.appengine.ext import blobstore
from google.appengine.ext.cloudstorage import cloudstorage_stub
from google.appengine.runtime import apiproxy_errors


MAX_REQUEST_SIZE = 32 << 20
GS_INFO_KIND = blobstore_stub._GS_INFO_KIND


_now_function = datetime.datetime.now


def _to_seconds(datetime_obj):
  return int(time.mktime(datetime_obj.timetuple()))


def _random_string(length):
  """Generate a random string of given length."""
  return ''.join(
      random.choice(string.letters + string.digits) for _ in range(length))


def raise_error(error_code, error_detail=''):
  """Raise application error helper method."""
  raise apiproxy_errors.ApplicationError(error_code, error_detail=error_detail)


_BLOBSTORE_DIRECTORY = files_blobstore._BLOBSTORE_DIRECTORY
_GS_PREFIX = gs._GS_PREFIX
_GS_UPLOAD_PREFIX = _GS_PREFIX + 'writable:'



class _GoogleStorageUpload(tuple):
  """Stores information about a writable Google Storage file."""
  buf = property(lambda self: self[0])
  content_type = property(lambda self: self[1])
  gs_filename = property(lambda self: self[2])


class GoogleStorage(object):
  """Virtual google storage to be used by file api."""



  def _Upload(self, buf, content_type, gs_filename):
    return _GoogleStorageUpload([buf, content_type, gs_filename])

  def __init__(self, blob_storage):
    """Constructor.

    Args:
      blob_storage:
          apphosting.api.blobstore.blobstore_stub.BlobStorage instance.
    """
    self.blob_storage = blob_storage
    self.gs_stub = cloudstorage_stub.CloudStorageStub(self.blob_storage)
    self.uploads = {}
    self.finalized = set()
    self.sequence_keys = {}

  def remove_gs_prefix(self, gs_filename):
    return gs_filename[len('/gs'):]

  def add_gs_prefix(self, gs_filename):
    return '/gs' + gs_filename

  def get_blobkey(self, gs_filename):
    return blobstore.create_gs_key(gs_filename)

  def has_upload(self, filename):
    """Checks if there is an upload at this filename."""
    return filename in self.uploads

  def finalize(self, filename):
    """Marks file as finalized."""
    upload = self.uploads[filename]
    self.finalized.add(filename)
    upload.buf.seek(0)
    content = upload.buf.read()
    blobkey = self.gs_stub.post_start_creation(
        self.remove_gs_prefix(upload.gs_filename),
        {'content-type': upload.content_type})
    assert blobkey == self.get_blobkey(upload.gs_filename)
    self.gs_stub.put_continue_creation(
        blobkey, content, (0, len(content) - 1), True)

    del self.sequence_keys[filename]

  def is_finalized(self, filename):
    """Checks if file is already finalized."""
    assert filename in self.uploads
    return filename in self.finalized

  def start_upload(self, request):
    """Starts a new upload based on the specified CreateRequest."""

    mime_type = None
    gs_filename = request.filename()
    ignored_parameters = [
        gs._CACHE_CONTROL_PARAMETER,
        gs._CANNED_ACL_PARAMETER,
        gs._CONTENT_DISPOSITION_PARAMETER,
        gs._CONTENT_ENCODING_PARAMETER,
        ]

    for param in request.parameters_list():
      name = param.name()
      if name == gs._MIME_TYPE_PARAMETER:
        mime_type = param.value()
      elif (name in ignored_parameters or
            name.startswith(gs._USER_METADATA_PREFIX)):
        pass
      else:
        raise_error(file_service_pb.FileServiceErrors.INVALID_PARAMETER)