import logging

import contextlib
import tempfile

from app import app


__all__ = ['load']
logger = logging.getLogger(__name__)


class Storage(object):

  """Storage is organized as follow:
  $ROOT/images/<image_id>/json
  $ROOT/images/<image_id>/layer
  $ROOT/repositories/<namespace>/<repository_name>/<tag_name>
  """

  # Useful if we want to change those locations later without rewriting
  # the code which uses Storage
  repositories = 'repositories'
  images = 'images'
  # Set the IO buffer to 64kB
  buffer_size = 64 * 1024

  #FIXME(samalba): Move all path resolver in each module (out of the base)
  def images_list_path(self, namespace, repository):
    return '{0}/{1}/{2}/_images_list'.format(self.repositories,
                                             namespace,
                                             repository)

  def image_json_path(self, namespace, repository, image_id):
    return '{0}/{1}/{2}/{3}/json'.format(self.images, namespace,
                                         repository, image_id)

  def image_mark_path(self, namespace, repository, image_id):
    return '{0}/{1}/{2}/{3}/_inprogress'.format(self.images, namespace,
                                                repository, image_id)

  def image_checksum_path(self, namespace, repository, image_id):
    return '{0}/{1}/{2}/{3}/_checksum'.format(self.images, namespace,
                                              repository, image_id)

  def image_layer_path(self, namespace, repository, image_id):
    return '{0}/{1}/{2}/{3}/layer'.format(self.images, namespace,
                                          repository, image_id)

  def image_ancestry_path(self, namespace, repository, image_id):
    return '{0}/{1}/{2}/{3}/ancestry'.format(self.images, namespace,
                                             repository, image_id)

  def repository_namespace_path(self, namespace, repository):
    return '{0}/{1}/{2}/'.format(self.images, namespace, repository)

  def image_file_trie_path(self, namespace, repository, image_id):
    return '{0}/{1}/{2}/{3}/files.trie'.format(self.images, namespace,
                                               repository, image_id)

  def image_file_diffs_path(self, namespace, repository, image_id):
    return '{0}/{1}/{2}/{3}/diffs.json'.format(self.images, namespace,
                                               repository, image_id)

  def get_content(self, path):
    raise NotImplementedError

  def put_content(self, path, content):
    raise NotImplementedError

  def stream_read(self, path):
    raise NotImplementedError

  def stream_read_file(self, path):
    raise NotImplementedError

  def stream_write(self, path, fp):
    raise NotImplementedError

  def list_directory(self, path=None):
    raise NotImplementedError

  def exists(self, path):
    raise NotImplementedError

  def remove(self, path):
    raise NotImplementedError

  def get_size(self, path):
    raise NotImplementedError


@contextlib.contextmanager
def store_stream(stream):
  """Stores the entire stream to a temporary file."""
  tmpf = tempfile.TemporaryFile()
  while True:
    try:
      buf = stream.read(4096)
      if not buf:
        break
      tmpf.write(buf)
    except IOError:
      break
  tmpf.seek(0)
  yield tmpf
  tmpf.close()


def temp_store_handler():
  tmpf = tempfile.TemporaryFile()

  def fn(buf):
    try:
      tmpf.write(buf)
    except IOError:
      pass

  return tmpf, fn


from local import LocalStorage
from s3 import S3Storage


_storage = {}


def load(kind=None):
  """Returns the right storage class according to the configuration."""
  global _storage

  # TODO hard code to local for now
  kind = app.config['STORAGE_KIND']
  # if not kind:
  #     kind = cfg.storage.lower()
  if kind in _storage:
    return _storage[kind]
  if kind == 's3':
    logger.debug('Using s3 storage.')
    store = S3Storage('', app.config['AWS_ACCESS_KEY'],
                      app.config['AWS_SECRET_KEY'],
                      app.config['REGISTRY_S3_BUCKET'])
  elif kind == 'local':
    logger.debug('Using local storage.')
    store = LocalStorage(app.config['LOCAL_STORAGE_DIR'])
  else:
    raise ValueError('Not supported storage \'{0}\''.format(kind))
  _storage[kind] = store
  return store