# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
#    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 shutil
import tarfile

from oslo_utils import importutils

from nova import exception
from nova.image import glance

_VDI_FORMAT_RAW = 1

IMAGE_API = glance.API()
IMAGE_HANDLERS = {'direct_vhd': 'glance.GlanceStore',
                  'vdi_local_dev': 'vdi_through_dev.VdiThroughDevStore',
                  'vdi_remote_stream': 'vdi_stream.VdiStreamStore'}


def get_image_handler(handler_name):
    if handler_name not in IMAGE_HANDLERS:
        raise exception.ImageHandlerUnsupported(image_handler=handler_name)
    return importutils.import_object('nova.virt.xenapi.image.'
                                     '%s' % IMAGE_HANDLERS[handler_name])


class GlanceImage(object):
    def __init__(self, context, image_href_or_id):
        self._context = context
        self._image_id = image_href_or_id
        self._cached_meta = None

    @property
    def meta(self):
        if self._cached_meta is None:
            self._cached_meta = IMAGE_API.get(self._context, self._image_id)
        return self._cached_meta

    def download_to(self, fileobj):
        return IMAGE_API.download(self._context, self._image_id, fileobj)

    def is_raw_tgz(self):
        return ['raw', 'tgz'] == [
            self.meta.get(key) for key in ('disk_format', 'container_format')]

    def data(self):
        return IMAGE_API.download(self._context, self._image_id)


class RawImage(object):
    def __init__(self, glance_image):
        self.glance_image = glance_image

    def get_size(self):
        return int(self.glance_image.meta['size'])

    def stream_to(self, fileobj):
        return self.glance_image.download_to(fileobj)


class IterableToFileAdapter(object):
    """A degenerate file-like so that an iterable could be read like a file.

    As Glance client returns an iterable, but tarfile requires a file like,
    this is the adapter between the two. This allows tarfile to access the
    glance stream.
    """

    def __init__(self, iterable):
        self.iterator = iterable.__iter__()
        self.remaining_data = ''

    def read(self, size):
        chunk = self.remaining_data
        try:
            while not chunk:
                chunk = next(self.iterator)
        except StopIteration:
            return ''
        return_value = chunk[0:size]
        self.remaining_data = chunk[size:]
        return return_value


class RawTGZImage(object):
    def __init__(self, glance_image):
        self.glance_image = glance_image
        self._tar_info = None
        self._tar_file = None

    def _as_file(self):
        return IterableToFileAdapter(self.glance_image.data())

    def _as_tarfile(self):
        return tarfile.open(mode='r|gz', fileobj=self._as_file())

    def get_size(self):
        if self._tar_file is None:
            self._tar_file = self._as_tarfile()
            self._tar_info = self._tar_file.next()
        return self._tar_info.size

    def stream_to(self, target_file):
        if self._tar_file is None:
            self._tar_file = self._as_tarfile()
            self._tar_info = self._tar_file.next()
        source_file = self._tar_file.extractfile(self._tar_info)
        shutil.copyfileobj(source_file, target_file)
        self._tar_file.close()
