# 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 nova import image

IMAGE_API = image.API()


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 = self.iterator.next()
        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()
