import tarfile
import requests
import os

from tempfile import TemporaryFile, mkdtemp
from zipfile import ZipFile
from util.dockerfileparse import parse_dockerfile
from util.safetar import safe_extractall

class BuildPackageException(Exception):
  """ Exception raised when retrieving or parsing a build package. """
  pass


class BuildPackage(object):
  """ Helper class for easy reading and updating of a Dockerfile build pack. """

  def __init__(self, requests_file):
    self._mime_processors = {
        'application/zip': BuildPackage._prepare_zip,
        'application/x-zip-compressed': BuildPackage._prepare_zip,
        'text/plain': BuildPackage._prepare_dockerfile,
        'application/octet-stream': BuildPackage._prepare_dockerfile,
        'application/x-tar': BuildPackage._prepare_tarball,
        'application/gzip': BuildPackage._prepare_tarball,
        'application/x-gzip': BuildPackage._prepare_tarball,
    }

    c_type = requests_file.headers['content-type']
    c_type = c_type.split(';')[0] if ';' in c_type else c_type

    if c_type not in self._mime_processors:
      raise BuildPackageException('Unknown build package mime type: %s' % c_type)

    self._package_directory = None
    try:
      self._package_directory = self._mime_processors[c_type](requests_file)
    except Exception as ex:
      raise BuildPackageException(ex.message)

  def parse_dockerfile(self, subdirectory):
    dockerfile_path = os.path.join(self._package_directory, subdirectory, 'Dockerfile')
    if not os.path.exists(dockerfile_path):
      if subdirectory:
        message = 'Build package did not contain a Dockerfile at sub directory %s.' % subdirectory
      else:
        message = 'Build package did not contain a Dockerfile at the root directory.'

      raise BuildPackageException(message)

    with open(dockerfile_path, 'r') as dockerfileobj:
      return parse_dockerfile(dockerfileobj.read())

  @staticmethod
  def from_url(url):
    buildpack_resource = requests.get(url, stream=True)
    return BuildPackage(buildpack_resource)

  @staticmethod
  def _prepare_zip(request_file):
    build_dir = mkdtemp(prefix='docker-build-')

    # Save the zip file to temp somewhere
    with TemporaryFile() as zip_file:
      zip_file.write(request_file.content)
      to_extract = ZipFile(zip_file)
      to_extract.extractall(build_dir)

    return build_dir

  @staticmethod
  def _prepare_dockerfile(request_file):
    build_dir = mkdtemp(prefix='docker-build-')
    dockerfile_path = os.path.join(build_dir, "Dockerfile")
    with open(dockerfile_path, 'w') as dockerfile:
      dockerfile.write(request_file.content)

    return build_dir

  @staticmethod
  def _prepare_tarball(request_file):
    build_dir = mkdtemp(prefix='docker-build-')

    # Save the zip file to temp somewhere
    with tarfile.open(mode='r|*', fileobj=request_file.raw) as tar_stream:
      safe_extractall(tar_stream, build_dir)

    return build_dir
